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