1/* -*- buffer-read-only: t -*- vi: set ro: */ 2/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ 3#line 1 4/* Locking in multithreaded situations. 5 Copyright (C) 2005-2010 Free Software Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software Foundation, 19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21/* Written by Bruno Haible <bruno@clisp.org>, 2005. 22 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, 23 gthr-win32.h. */ 24 25#include <config.h> 26 27#include "glthread/lock.h" 28 29/* ========================================================================= */ 30 31#if USE_POSIX_THREADS 32 33/* -------------------------- gl_lock_t datatype -------------------------- */ 34 35/* ------------------------- gl_rwlock_t datatype ------------------------- */ 36 37# if HAVE_PTHREAD_RWLOCK 38 39# if !defined PTHREAD_RWLOCK_INITIALIZER 40 41int 42glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) 43{ 44 int err; 45 46 err = pthread_rwlock_init (&lock->rwlock, NULL); 47 if (err != 0) 48 return err; 49 lock->initialized = 1; 50 return 0; 51} 52 53int 54glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock) 55{ 56 if (!lock->initialized) 57 { 58 int err; 59 60 err = pthread_mutex_lock (&lock->guard); 61 if (err != 0) 62 return err; 63 if (!lock->initialized) 64 { 65 err = glthread_rwlock_init_multithreaded (lock); 66 if (err != 0) 67 { 68 pthread_mutex_unlock (&lock->guard); 69 return err; 70 } 71 } 72 err = pthread_mutex_unlock (&lock->guard); 73 if (err != 0) 74 return err; 75 } 76 return pthread_rwlock_rdlock (&lock->rwlock); 77} 78 79int 80glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock) 81{ 82 if (!lock->initialized) 83 { 84 int err; 85 86 err = pthread_mutex_lock (&lock->guard); 87 if (err != 0) 88 return err; 89 if (!lock->initialized) 90 { 91 err = glthread_rwlock_init_multithreaded (lock); 92 if (err != 0) 93 { 94 pthread_mutex_unlock (&lock->guard); 95 return err; 96 } 97 } 98 err = pthread_mutex_unlock (&lock->guard); 99 if (err != 0) 100 return err; 101 } 102 return pthread_rwlock_wrlock (&lock->rwlock); 103} 104 105int 106glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) 107{ 108 if (!lock->initialized) 109 return EINVAL; 110 return pthread_rwlock_unlock (&lock->rwlock); 111} 112 113int 114glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) 115{ 116 int err; 117 118 if (!lock->initialized) 119 return EINVAL; 120 err = pthread_rwlock_destroy (&lock->rwlock); 121 if (err != 0) 122 return err; 123 lock->initialized = 0; 124 return 0; 125} 126 127# endif 128 129# else 130 131int 132glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) 133{ 134 int err; 135 136 err = pthread_mutex_init (&lock->lock, NULL); 137 if (err != 0) 138 return err; 139 err = pthread_cond_init (&lock->waiting_readers, NULL); 140 if (err != 0) 141 return err; 142 err = pthread_cond_init (&lock->waiting_writers, NULL); 143 if (err != 0) 144 return err; 145 lock->waiting_writers_count = 0; 146 lock->runcount = 0; 147 return 0; 148} 149 150int 151glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock) 152{ 153 int err; 154 155 err = pthread_mutex_lock (&lock->lock); 156 if (err != 0) 157 return err; 158 /* Test whether only readers are currently running, and whether the runcount 159 field will not overflow. */ 160 /* POSIX says: "It is implementation-defined whether the calling thread 161 acquires the lock when a writer does not hold the lock and there are 162 writers blocked on the lock." Let's say, no: give the writers a higher 163 priority. */ 164 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) 165 { 166 /* This thread has to wait for a while. Enqueue it among the 167 waiting_readers. */ 168 err = pthread_cond_wait (&lock->waiting_readers, &lock->lock); 169 if (err != 0) 170 { 171 pthread_mutex_unlock (&lock->lock); 172 return err; 173 } 174 } 175 lock->runcount++; 176 return pthread_mutex_unlock (&lock->lock); 177} 178 179int 180glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock) 181{ 182 int err; 183 184 err = pthread_mutex_lock (&lock->lock); 185 if (err != 0) 186 return err; 187 /* Test whether no readers or writers are currently running. */ 188 while (!(lock->runcount == 0)) 189 { 190 /* This thread has to wait for a while. Enqueue it among the 191 waiting_writers. */ 192 lock->waiting_writers_count++; 193 err = pthread_cond_wait (&lock->waiting_writers, &lock->lock); 194 if (err != 0) 195 { 196 lock->waiting_writers_count--; 197 pthread_mutex_unlock (&lock->lock); 198 return err; 199 } 200 lock->waiting_writers_count--; 201 } 202 lock->runcount--; /* runcount becomes -1 */ 203 return pthread_mutex_unlock (&lock->lock); 204} 205 206int 207glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) 208{ 209 int err; 210 211 err = pthread_mutex_lock (&lock->lock); 212 if (err != 0) 213 return err; 214 if (lock->runcount < 0) 215 { 216 /* Drop a writer lock. */ 217 if (!(lock->runcount == -1)) 218 { 219 pthread_mutex_unlock (&lock->lock); 220 return EINVAL; 221 } 222 lock->runcount = 0; 223 } 224 else 225 { 226 /* Drop a reader lock. */ 227 if (!(lock->runcount > 0)) 228 { 229 pthread_mutex_unlock (&lock->lock); 230 return EINVAL; 231 } 232 lock->runcount--; 233 } 234 if (lock->runcount == 0) 235 { 236 /* POSIX recommends that "write locks shall take precedence over read 237 locks", to avoid "writer starvation". */ 238 if (lock->waiting_writers_count > 0) 239 { 240 /* Wake up one of the waiting writers. */ 241 err = pthread_cond_signal (&lock->waiting_writers); 242 if (err != 0) 243 { 244 pthread_mutex_unlock (&lock->lock); 245 return err; 246 } 247 } 248 else 249 { 250 /* Wake up all waiting readers. */ 251 err = pthread_cond_broadcast (&lock->waiting_readers); 252 if (err != 0) 253 { 254 pthread_mutex_unlock (&lock->lock); 255 return err; 256 } 257 } 258 } 259 return pthread_mutex_unlock (&lock->lock); 260} 261 262int 263glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) 264{ 265 int err; 266 267 err = pthread_mutex_destroy (&lock->lock); 268 if (err != 0) 269 return err; 270 err = pthread_cond_destroy (&lock->waiting_readers); 271 if (err != 0) 272 return err; 273 err = pthread_cond_destroy (&lock->waiting_writers); 274 if (err != 0) 275 return err; 276 return 0; 277} 278 279# endif 280 281/* --------------------- gl_recursive_lock_t datatype --------------------- */ 282 283# if HAVE_PTHREAD_MUTEX_RECURSIVE 284 285# if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 286 287int 288glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) 289{ 290 pthread_mutexattr_t attributes; 291 int err; 292 293 err = pthread_mutexattr_init (&attributes); 294 if (err != 0) 295 return err; 296 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE); 297 if (err != 0) 298 { 299 pthread_mutexattr_destroy (&attributes); 300 return err; 301 } 302 err = pthread_mutex_init (lock, &attributes); 303 if (err != 0) 304 { 305 pthread_mutexattr_destroy (&attributes); 306 return err; 307 } 308 err = pthread_mutexattr_destroy (&attributes); 309 if (err != 0) 310 return err; 311 return 0; 312} 313 314# else 315 316int 317glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) 318{ 319 pthread_mutexattr_t attributes; 320 int err; 321 322 err = pthread_mutexattr_init (&attributes); 323 if (err != 0) 324 return err; 325 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE); 326 if (err != 0) 327 { 328 pthread_mutexattr_destroy (&attributes); 329 return err; 330 } 331 err = pthread_mutex_init (&lock->recmutex, &attributes); 332 if (err != 0) 333 { 334 pthread_mutexattr_destroy (&attributes); 335 return err; 336 } 337 err = pthread_mutexattr_destroy (&attributes); 338 if (err != 0) 339 return err; 340 lock->initialized = 1; 341 return 0; 342} 343 344int 345glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) 346{ 347 if (!lock->initialized) 348 { 349 int err; 350 351 err = pthread_mutex_lock (&lock->guard); 352 if (err != 0) 353 return err; 354 if (!lock->initialized) 355 { 356 err = glthread_recursive_lock_init_multithreaded (lock); 357 if (err != 0) 358 { 359 pthread_mutex_unlock (&lock->guard); 360 return err; 361 } 362 } 363 err = pthread_mutex_unlock (&lock->guard); 364 if (err != 0) 365 return err; 366 } 367 return pthread_mutex_lock (&lock->recmutex); 368} 369 370int 371glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) 372{ 373 if (!lock->initialized) 374 return EINVAL; 375 return pthread_mutex_unlock (&lock->recmutex); 376} 377 378int 379glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) 380{ 381 int err; 382 383 if (!lock->initialized) 384 return EINVAL; 385 err = pthread_mutex_destroy (&lock->recmutex); 386 if (err != 0) 387 return err; 388 lock->initialized = 0; 389 return 0; 390} 391 392# endif 393 394# else 395 396int 397glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) 398{ 399 int err; 400 401 err = pthread_mutex_init (&lock->mutex, NULL); 402 if (err != 0) 403 return err; 404 lock->owner = (pthread_t) 0; 405 lock->depth = 0; 406 return 0; 407} 408 409int 410glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) 411{ 412 pthread_t self = pthread_self (); 413 if (lock->owner != self) 414 { 415 int err; 416 417 err = pthread_mutex_lock (&lock->mutex); 418 if (err != 0) 419 return err; 420 lock->owner = self; 421 } 422 if (++(lock->depth) == 0) /* wraparound? */ 423 { 424 lock->depth--; 425 return EAGAIN; 426 } 427 return 0; 428} 429 430int 431glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) 432{ 433 if (lock->owner != pthread_self ()) 434 return EPERM; 435 if (lock->depth == 0) 436 return EINVAL; 437 if (--(lock->depth) == 0) 438 { 439 lock->owner = (pthread_t) 0; 440 return pthread_mutex_unlock (&lock->mutex); 441 } 442 else 443 return 0; 444} 445 446int 447glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) 448{ 449 if (lock->owner != (pthread_t) 0) 450 return EBUSY; 451 return pthread_mutex_destroy (&lock->mutex); 452} 453 454# endif 455 456/* -------------------------- gl_once_t datatype -------------------------- */ 457 458static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT; 459 460int 461glthread_once_singlethreaded (pthread_once_t *once_control) 462{ 463 /* We don't know whether pthread_once_t is an integer type, a floating-point 464 type, a pointer type, or a structure type. */ 465 char *firstbyte = (char *)once_control; 466 if (*firstbyte == *(const char *)&fresh_once) 467 { 468 /* First time use of once_control. Invert the first byte. */ 469 *firstbyte = ~ *(const char *)&fresh_once; 470 return 1; 471 } 472 else 473 return 0; 474} 475 476#endif 477 478/* ========================================================================= */ 479 480#if USE_PTH_THREADS 481 482/* Use the GNU Pth threads library. */ 483 484/* -------------------------- gl_lock_t datatype -------------------------- */ 485 486/* ------------------------- gl_rwlock_t datatype ------------------------- */ 487 488/* --------------------- gl_recursive_lock_t datatype --------------------- */ 489 490/* -------------------------- gl_once_t datatype -------------------------- */ 491 492static void 493glthread_once_call (void *arg) 494{ 495 void (**gl_once_temp_addr) (void) = (void (**) (void)) arg; 496 void (*initfunction) (void) = *gl_once_temp_addr; 497 initfunction (); 498} 499 500int 501glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void)) 502{ 503 void (*temp) (void) = initfunction; 504 return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0); 505} 506 507int 508glthread_once_singlethreaded (pth_once_t *once_control) 509{ 510 /* We know that pth_once_t is an integer type. */ 511 if (*once_control == PTH_ONCE_INIT) 512 { 513 /* First time use of once_control. Invert the marker. */ 514 *once_control = ~ PTH_ONCE_INIT; 515 return 1; 516 } 517 else 518 return 0; 519} 520 521#endif 522 523/* ========================================================================= */ 524 525#if USE_SOLARIS_THREADS 526 527/* Use the old Solaris threads library. */ 528 529/* -------------------------- gl_lock_t datatype -------------------------- */ 530 531/* ------------------------- gl_rwlock_t datatype ------------------------- */ 532 533/* --------------------- gl_recursive_lock_t datatype --------------------- */ 534 535int 536glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) 537{ 538 int err; 539 540 err = mutex_init (&lock->mutex, USYNC_THREAD, NULL); 541 if (err != 0) 542 return err; 543 lock->owner = (thread_t) 0; 544 lock->depth = 0; 545 return 0; 546} 547 548int 549glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) 550{ 551 thread_t self = thr_self (); 552 if (lock->owner != self) 553 { 554 int err; 555 556 err = mutex_lock (&lock->mutex); 557 if (err != 0) 558 return err; 559 lock->owner = self; 560 } 561 if (++(lock->depth) == 0) /* wraparound? */ 562 { 563 lock->depth--; 564 return EAGAIN; 565 } 566 return 0; 567} 568 569int 570glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) 571{ 572 if (lock->owner != thr_self ()) 573 return EPERM; 574 if (lock->depth == 0) 575 return EINVAL; 576 if (--(lock->depth) == 0) 577 { 578 lock->owner = (thread_t) 0; 579 return mutex_unlock (&lock->mutex); 580 } 581 else 582 return 0; 583} 584 585int 586glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) 587{ 588 if (lock->owner != (thread_t) 0) 589 return EBUSY; 590 return mutex_destroy (&lock->mutex); 591} 592 593/* -------------------------- gl_once_t datatype -------------------------- */ 594 595int 596glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void)) 597{ 598 if (!once_control->inited) 599 { 600 int err; 601 602 /* Use the mutex to guarantee that if another thread is already calling 603 the initfunction, this thread waits until it's finished. */ 604 err = mutex_lock (&once_control->mutex); 605 if (err != 0) 606 return err; 607 if (!once_control->inited) 608 { 609 once_control->inited = 1; 610 initfunction (); 611 } 612 return mutex_unlock (&once_control->mutex); 613 } 614 else 615 return 0; 616} 617 618int 619glthread_once_singlethreaded (gl_once_t *once_control) 620{ 621 /* We know that gl_once_t contains an integer type. */ 622 if (!once_control->inited) 623 { 624 /* First time use of once_control. Invert the marker. */ 625 once_control->inited = ~ 0; 626 return 1; 627 } 628 else 629 return 0; 630} 631 632#endif 633 634/* ========================================================================= */ 635 636#if USE_WIN32_THREADS 637 638/* -------------------------- gl_lock_t datatype -------------------------- */ 639 640void 641glthread_lock_init_func (gl_lock_t *lock) 642{ 643 InitializeCriticalSection (&lock->lock); 644 lock->guard.done = 1; 645} 646 647int 648glthread_lock_lock_func (gl_lock_t *lock) 649{ 650 if (!lock->guard.done) 651 { 652 if (InterlockedIncrement (&lock->guard.started) == 0) 653 /* This thread is the first one to need this lock. Initialize it. */ 654 glthread_lock_init (lock); 655 else 656 /* Yield the CPU while waiting for another thread to finish 657 initializing this lock. */ 658 while (!lock->guard.done) 659 Sleep (0); 660 } 661 EnterCriticalSection (&lock->lock); 662 return 0; 663} 664 665int 666glthread_lock_unlock_func (gl_lock_t *lock) 667{ 668 if (!lock->guard.done) 669 return EINVAL; 670 LeaveCriticalSection (&lock->lock); 671 return 0; 672} 673 674int 675glthread_lock_destroy_func (gl_lock_t *lock) 676{ 677 if (!lock->guard.done) 678 return EINVAL; 679 DeleteCriticalSection (&lock->lock); 680 lock->guard.done = 0; 681 return 0; 682} 683 684/* ------------------------- gl_rwlock_t datatype ------------------------- */ 685 686/* In this file, the waitqueues are implemented as circular arrays. */ 687#define gl_waitqueue_t gl_carray_waitqueue_t 688 689static inline void 690gl_waitqueue_init (gl_waitqueue_t *wq) 691{ 692 wq->array = NULL; 693 wq->count = 0; 694 wq->alloc = 0; 695 wq->offset = 0; 696} 697 698/* Enqueues the current thread, represented by an event, in a wait queue. 699 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */ 700static HANDLE 701gl_waitqueue_add (gl_waitqueue_t *wq) 702{ 703 HANDLE event; 704 unsigned int index; 705 706 if (wq->count == wq->alloc) 707 { 708 unsigned int new_alloc = 2 * wq->alloc + 1; 709 HANDLE *new_array = 710 (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE)); 711 if (new_array == NULL) 712 /* No more memory. */ 713 return INVALID_HANDLE_VALUE; 714 /* Now is a good opportunity to rotate the array so that its contents 715 starts at offset 0. */ 716 if (wq->offset > 0) 717 { 718 unsigned int old_count = wq->count; 719 unsigned int old_alloc = wq->alloc; 720 unsigned int old_offset = wq->offset; 721 unsigned int i; 722 if (old_offset + old_count > old_alloc) 723 { 724 unsigned int limit = old_offset + old_count - old_alloc; 725 for (i = 0; i < limit; i++) 726 new_array[old_alloc + i] = new_array[i]; 727 } 728 for (i = 0; i < old_count; i++) 729 new_array[i] = new_array[old_offset + i]; 730 wq->offset = 0; 731 } 732 wq->array = new_array; 733 wq->alloc = new_alloc; 734 } 735 /* Whether the created event is a manual-reset one or an auto-reset one, 736 does not matter, since we will wait on it only once. */ 737 event = CreateEvent (NULL, TRUE, FALSE, NULL); 738 if (event == INVALID_HANDLE_VALUE) 739 /* No way to allocate an event. */ 740 return INVALID_HANDLE_VALUE; 741 index = wq->offset + wq->count; 742 if (index >= wq->alloc) 743 index -= wq->alloc; 744 wq->array[index] = event; 745 wq->count++; 746 return event; 747} 748 749/* Notifies the first thread from a wait queue and dequeues it. */ 750static inline void 751gl_waitqueue_notify_first (gl_waitqueue_t *wq) 752{ 753 SetEvent (wq->array[wq->offset + 0]); 754 wq->offset++; 755 wq->count--; 756 if (wq->count == 0 || wq->offset == wq->alloc) 757 wq->offset = 0; 758} 759 760/* Notifies all threads from a wait queue and dequeues them all. */ 761static inline void 762gl_waitqueue_notify_all (gl_waitqueue_t *wq) 763{ 764 unsigned int i; 765 766 for (i = 0; i < wq->count; i++) 767 { 768 unsigned int index = wq->offset + i; 769 if (index >= wq->alloc) 770 index -= wq->alloc; 771 SetEvent (wq->array[index]); 772 } 773 wq->count = 0; 774 wq->offset = 0; 775} 776 777void 778glthread_rwlock_init_func (gl_rwlock_t *lock) 779{ 780 InitializeCriticalSection (&lock->lock); 781 gl_waitqueue_init (&lock->waiting_readers); 782 gl_waitqueue_init (&lock->waiting_writers); 783 lock->runcount = 0; 784 lock->guard.done = 1; 785} 786 787int 788glthread_rwlock_rdlock_func (gl_rwlock_t *lock) 789{ 790 if (!lock->guard.done) 791 { 792 if (InterlockedIncrement (&lock->guard.started) == 0) 793 /* This thread is the first one to need this lock. Initialize it. */ 794 glthread_rwlock_init (lock); 795 else 796 /* Yield the CPU while waiting for another thread to finish 797 initializing this lock. */ 798 while (!lock->guard.done) 799 Sleep (0); 800 } 801 EnterCriticalSection (&lock->lock); 802 /* Test whether only readers are currently running, and whether the runcount 803 field will not overflow. */ 804 if (!(lock->runcount + 1 > 0)) 805 { 806 /* This thread has to wait for a while. Enqueue it among the 807 waiting_readers. */ 808 HANDLE event = gl_waitqueue_add (&lock->waiting_readers); 809 if (event != INVALID_HANDLE_VALUE) 810 { 811 DWORD result; 812 LeaveCriticalSection (&lock->lock); 813 /* Wait until another thread signals this event. */ 814 result = WaitForSingleObject (event, INFINITE); 815 if (result == WAIT_FAILED || result == WAIT_TIMEOUT) 816 abort (); 817 CloseHandle (event); 818 /* The thread which signalled the event already did the bookkeeping: 819 removed us from the waiting_readers, incremented lock->runcount. */ 820 if (!(lock->runcount > 0)) 821 abort (); 822 return 0; 823 } 824 else 825 { 826 /* Allocation failure. Weird. */ 827 do 828 { 829 LeaveCriticalSection (&lock->lock); 830 Sleep (1); 831 EnterCriticalSection (&lock->lock); 832 } 833 while (!(lock->runcount + 1 > 0)); 834 } 835 } 836 lock->runcount++; 837 LeaveCriticalSection (&lock->lock); 838 return 0; 839} 840 841int 842glthread_rwlock_wrlock_func (gl_rwlock_t *lock) 843{ 844 if (!lock->guard.done) 845 { 846 if (InterlockedIncrement (&lock->guard.started) == 0) 847 /* This thread is the first one to need this lock. Initialize it. */ 848 glthread_rwlock_init (lock); 849 else 850 /* Yield the CPU while waiting for another thread to finish 851 initializing this lock. */ 852 while (!lock->guard.done) 853 Sleep (0); 854 } 855 EnterCriticalSection (&lock->lock); 856 /* Test whether no readers or writers are currently running. */ 857 if (!(lock->runcount == 0)) 858 { 859 /* This thread has to wait for a while. Enqueue it among the 860 waiting_writers. */ 861 HANDLE event = gl_waitqueue_add (&lock->waiting_writers); 862 if (event != INVALID_HANDLE_VALUE) 863 { 864 DWORD result; 865 LeaveCriticalSection (&lock->lock); 866 /* Wait until another thread signals this event. */ 867 result = WaitForSingleObject (event, INFINITE); 868 if (result == WAIT_FAILED || result == WAIT_TIMEOUT) 869 abort (); 870 CloseHandle (event); 871 /* The thread which signalled the event already did the bookkeeping: 872 removed us from the waiting_writers, set lock->runcount = -1. */ 873 if (!(lock->runcount == -1)) 874 abort (); 875 return 0; 876 } 877 else 878 { 879 /* Allocation failure. Weird. */ 880 do 881 { 882 LeaveCriticalSection (&lock->lock); 883 Sleep (1); 884 EnterCriticalSection (&lock->lock); 885 } 886 while (!(lock->runcount == 0)); 887 } 888 } 889 lock->runcount--; /* runcount becomes -1 */ 890 LeaveCriticalSection (&lock->lock); 891 return 0; 892} 893 894int 895glthread_rwlock_unlock_func (gl_rwlock_t *lock) 896{ 897 if (!lock->guard.done) 898 return EINVAL; 899 EnterCriticalSection (&lock->lock); 900 if (lock->runcount < 0) 901 { 902 /* Drop a writer lock. */ 903 if (!(lock->runcount == -1)) 904 abort (); 905 lock->runcount = 0; 906 } 907 else 908 { 909 /* Drop a reader lock. */ 910 if (!(lock->runcount > 0)) 911 { 912 LeaveCriticalSection (&lock->lock); 913 return EPERM; 914 } 915 lock->runcount--; 916 } 917 if (lock->runcount == 0) 918 { 919 /* POSIX recommends that "write locks shall take precedence over read 920 locks", to avoid "writer starvation". */ 921 if (lock->waiting_writers.count > 0) 922 { 923 /* Wake up one of the waiting writers. */ 924 lock->runcount--; 925 gl_waitqueue_notify_first (&lock->waiting_writers); 926 } 927 else 928 { 929 /* Wake up all waiting readers. */ 930 lock->runcount += lock->waiting_readers.count; 931 gl_waitqueue_notify_all (&lock->waiting_readers); 932 } 933 } 934 LeaveCriticalSection (&lock->lock); 935 return 0; 936} 937 938int 939glthread_rwlock_destroy_func (gl_rwlock_t *lock) 940{ 941 if (!lock->guard.done) 942 return EINVAL; 943 if (lock->runcount != 0) 944 return EBUSY; 945 DeleteCriticalSection (&lock->lock); 946 if (lock->waiting_readers.array != NULL) 947 free (lock->waiting_readers.array); 948 if (lock->waiting_writers.array != NULL) 949 free (lock->waiting_writers.array); 950 lock->guard.done = 0; 951 return 0; 952} 953 954/* --------------------- gl_recursive_lock_t datatype --------------------- */ 955 956void 957glthread_recursive_lock_init_func (gl_recursive_lock_t *lock) 958{ 959 lock->owner = 0; 960 lock->depth = 0; 961 InitializeCriticalSection (&lock->lock); 962 lock->guard.done = 1; 963} 964 965int 966glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock) 967{ 968 if (!lock->guard.done) 969 { 970 if (InterlockedIncrement (&lock->guard.started) == 0) 971 /* This thread is the first one to need this lock. Initialize it. */ 972 glthread_recursive_lock_init (lock); 973 else 974 /* Yield the CPU while waiting for another thread to finish 975 initializing this lock. */ 976 while (!lock->guard.done) 977 Sleep (0); 978 } 979 { 980 DWORD self = GetCurrentThreadId (); 981 if (lock->owner != self) 982 { 983 EnterCriticalSection (&lock->lock); 984 lock->owner = self; 985 } 986 if (++(lock->depth) == 0) /* wraparound? */ 987 { 988 lock->depth--; 989 return EAGAIN; 990 } 991 } 992 return 0; 993} 994 995int 996glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock) 997{ 998 if (lock->owner != GetCurrentThreadId ()) 999 return EPERM; 1000 if (lock->depth == 0) 1001 return EINVAL; 1002 if (--(lock->depth) == 0) 1003 { 1004 lock->owner = 0; 1005 LeaveCriticalSection (&lock->lock); 1006 } 1007 return 0; 1008} 1009 1010int 1011glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock) 1012{ 1013 if (lock->owner != 0) 1014 return EBUSY; 1015 DeleteCriticalSection (&lock->lock); 1016 lock->guard.done = 0; 1017 return 0; 1018} 1019 1020/* -------------------------- gl_once_t datatype -------------------------- */ 1021 1022void 1023glthread_once_func (gl_once_t *once_control, void (*initfunction) (void)) 1024{ 1025 if (once_control->inited <= 0) 1026 { 1027 if (InterlockedIncrement (&once_control->started) == 0) 1028 { 1029 /* This thread is the first one to come to this once_control. */ 1030 InitializeCriticalSection (&once_control->lock); 1031 EnterCriticalSection (&once_control->lock); 1032 once_control->inited = 0; 1033 initfunction (); 1034 once_control->inited = 1; 1035 LeaveCriticalSection (&once_control->lock); 1036 } 1037 else 1038 { 1039 /* Undo last operation. */ 1040 InterlockedDecrement (&once_control->started); 1041 /* Some other thread has already started the initialization. 1042 Yield the CPU while waiting for the other thread to finish 1043 initializing and taking the lock. */ 1044 while (once_control->inited < 0) 1045 Sleep (0); 1046 if (once_control->inited <= 0) 1047 { 1048 /* Take the lock. This blocks until the other thread has 1049 finished calling the initfunction. */ 1050 EnterCriticalSection (&once_control->lock); 1051 LeaveCriticalSection (&once_control->lock); 1052 if (!(once_control->inited > 0)) 1053 abort (); 1054 } 1055 } 1056 } 1057} 1058 1059#endif 1060 1061/* ========================================================================= */ 1062