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