Back to index

courier  0.68.2
lock.c
Go to the documentation of this file.
00001 /* Locking in multithreaded situations.
00002    Copyright (C) 2005-2008 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or modify it
00005    under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public
00015    License along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
00017    USA.  */
00018 
00019 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
00020    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
00021    gthr-win32.h.  */
00022 
00023 #include <config.h>
00024 
00025 #include "lock.h"
00026 
00027 /* ========================================================================= */
00028 
00029 #if USE_POSIX_THREADS
00030 
00031 /* -------------------------- gl_lock_t datatype -------------------------- */
00032 
00033 /* ------------------------- gl_rwlock_t datatype ------------------------- */
00034 
00035 # if HAVE_PTHREAD_RWLOCK
00036 
00037 #  if !defined PTHREAD_RWLOCK_INITIALIZER
00038 
00039 int
00040 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
00041 {
00042   int err;
00043 
00044   err = pthread_rwlock_init (&lock->rwlock, NULL);
00045   if (err != 0)
00046     return err;
00047   lock->initialized = 1;
00048   return 0;
00049 }
00050 
00051 int
00052 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
00053 {
00054   if (!lock->initialized)
00055     {
00056       int err;
00057 
00058       err = pthread_mutex_lock (&lock->guard);
00059       if (err != 0)
00060         return err;
00061       if (!lock->initialized)
00062         {
00063           err = glthread_rwlock_init_multithreaded (lock);
00064           if (err != 0)
00065             {
00066               pthread_mutex_unlock (&lock->guard);
00067               return err;
00068             }
00069         }
00070       err = pthread_mutex_unlock (&lock->guard);
00071       if (err != 0)
00072         return err;
00073     }
00074   return pthread_rwlock_rdlock (&lock->rwlock);
00075 }
00076 
00077 int
00078 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
00079 {
00080   if (!lock->initialized)
00081     {
00082       int err;
00083 
00084       err = pthread_mutex_lock (&lock->guard);
00085       if (err != 0)
00086         return err;
00087       if (!lock->initialized)
00088         {
00089           err = glthread_rwlock_init_multithreaded (lock);
00090           if (err != 0)
00091             {
00092               pthread_mutex_unlock (&lock->guard);
00093               return err;
00094             }
00095         }
00096       err = pthread_mutex_unlock (&lock->guard);
00097       if (err != 0)
00098         return err;
00099     }
00100   return pthread_rwlock_wrlock (&lock->rwlock);
00101 }
00102 
00103 int
00104 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
00105 {
00106   if (!lock->initialized)
00107     return EINVAL;
00108   return pthread_rwlock_unlock (&lock->rwlock);
00109 }
00110 
00111 int
00112 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
00113 {
00114   int err;
00115 
00116   if (!lock->initialized)
00117     return EINVAL;
00118   err = pthread_rwlock_destroy (&lock->rwlock);
00119   if (err != 0)
00120     return err;
00121   lock->initialized = 0;
00122   return 0;
00123 }
00124 
00125 #  endif
00126 
00127 # else
00128 
00129 int
00130 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
00131 {
00132   int err;
00133 
00134   err = pthread_mutex_init (&lock->lock, NULL);
00135   if (err != 0)
00136     return err;
00137   err = pthread_cond_init (&lock->waiting_readers, NULL);
00138   if (err != 0)
00139     return err;
00140   err = pthread_cond_init (&lock->waiting_writers, NULL);
00141   if (err != 0)
00142     return err;
00143   lock->waiting_writers_count = 0;
00144   lock->runcount = 0;
00145   return 0;
00146 }
00147 
00148 int
00149 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
00150 {
00151   int err;
00152 
00153   err = pthread_mutex_lock (&lock->lock);
00154   if (err != 0)
00155     return err;
00156   /* Test whether only readers are currently running, and whether the runcount
00157      field will not overflow.  */
00158   /* POSIX says: "It is implementation-defined whether the calling thread
00159      acquires the lock when a writer does not hold the lock and there are
00160      writers blocked on the lock."  Let's say, no: give the writers a higher
00161      priority.  */
00162   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
00163     {
00164       /* This thread has to wait for a while.  Enqueue it among the
00165          waiting_readers.  */
00166       err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
00167       if (err != 0)
00168         {
00169           pthread_mutex_unlock (&lock->lock);
00170           return err;
00171         }
00172     }
00173   lock->runcount++;
00174   return pthread_mutex_unlock (&lock->lock);
00175 }
00176 
00177 int
00178 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
00179 {
00180   int err;
00181 
00182   err = pthread_mutex_lock (&lock->lock);
00183   if (err != 0)
00184     return err;
00185   /* Test whether no readers or writers are currently running.  */
00186   while (!(lock->runcount == 0))
00187     {
00188       /* This thread has to wait for a while.  Enqueue it among the
00189          waiting_writers.  */
00190       lock->waiting_writers_count++;
00191       err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
00192       if (err != 0)
00193         {
00194           lock->waiting_writers_count--;
00195           pthread_mutex_unlock (&lock->lock);
00196           return err;
00197         }
00198       lock->waiting_writers_count--;
00199     }
00200   lock->runcount--; /* runcount becomes -1 */
00201   return pthread_mutex_unlock (&lock->lock);
00202 }
00203 
00204 int
00205 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
00206 {
00207   int err;
00208 
00209   err = pthread_mutex_lock (&lock->lock);
00210   if (err != 0)
00211     return err;
00212   if (lock->runcount < 0)
00213     {
00214       /* Drop a writer lock.  */
00215       if (!(lock->runcount == -1))
00216         {
00217           pthread_mutex_unlock (&lock->lock);
00218           return EINVAL;
00219         }
00220       lock->runcount = 0;
00221     }
00222   else
00223     {
00224       /* Drop a reader lock.  */
00225       if (!(lock->runcount > 0))
00226         {
00227           pthread_mutex_unlock (&lock->lock);
00228           return EINVAL;
00229         }
00230       lock->runcount--;
00231     }
00232   if (lock->runcount == 0)
00233     {
00234       /* POSIX recommends that "write locks shall take precedence over read
00235          locks", to avoid "writer starvation".  */
00236       if (lock->waiting_writers_count > 0)
00237         {
00238           /* Wake up one of the waiting writers.  */
00239           err = pthread_cond_signal (&lock->waiting_writers);
00240           if (err != 0)
00241             {
00242               pthread_mutex_unlock (&lock->lock);
00243               return err;
00244             }
00245         }
00246       else
00247         {
00248           /* Wake up all waiting readers.  */
00249           err = pthread_cond_broadcast (&lock->waiting_readers);
00250           if (err != 0)
00251             {
00252               pthread_mutex_unlock (&lock->lock);
00253               return err;
00254             }
00255         }
00256     }
00257   return pthread_mutex_unlock (&lock->lock);
00258 }
00259 
00260 int
00261 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
00262 {
00263   int err;
00264 
00265   err = pthread_mutex_destroy (&lock->lock);
00266   if (err != 0)
00267     return err;
00268   err = pthread_cond_destroy (&lock->waiting_readers);
00269   if (err != 0)
00270     return err;
00271   err = pthread_cond_destroy (&lock->waiting_writers);
00272   if (err != 0)
00273     return err;
00274   return 0;
00275 }
00276 
00277 # endif
00278 
00279 /* --------------------- gl_recursive_lock_t datatype --------------------- */
00280 
00281 # if HAVE_PTHREAD_MUTEX_RECURSIVE
00282 
00283 #  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
00284 
00285 int
00286 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
00287 {
00288   pthread_mutexattr_t attributes;
00289   int err;
00290 
00291   err = pthread_mutexattr_init (&attributes);
00292   if (err != 0)
00293     return err;
00294   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
00295   if (err != 0)
00296     {
00297       pthread_mutexattr_destroy (&attributes);
00298       return err;
00299     }
00300   err = pthread_mutex_init (lock, &attributes);
00301   if (err != 0)
00302     {
00303       pthread_mutexattr_destroy (&attributes);
00304       return err;
00305     }
00306   err = pthread_mutexattr_destroy (&attributes);
00307   if (err != 0)
00308     return err;
00309   return 0;
00310 }
00311 
00312 #  else
00313 
00314 int
00315 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
00316 {
00317   pthread_mutexattr_t attributes;
00318   int err;
00319 
00320   err = pthread_mutexattr_init (&attributes);
00321   if (err != 0)
00322     return err;
00323   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
00324   if (err != 0)
00325     {
00326       pthread_mutexattr_destroy (&attributes);
00327       return err;
00328     }
00329   err = pthread_mutex_init (&lock->recmutex, &attributes);
00330   if (err != 0)
00331     {
00332       pthread_mutexattr_destroy (&attributes);
00333       return err;
00334     }
00335   err = pthread_mutexattr_destroy (&attributes);
00336   if (err != 0)
00337     return err;
00338   lock->initialized = 1;
00339   return 0;
00340 }
00341 
00342 int
00343 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
00344 {
00345   if (!lock->initialized)
00346     {
00347       int err;
00348 
00349       err = pthread_mutex_lock (&lock->guard);
00350       if (err != 0)
00351         return err;
00352       if (!lock->initialized)
00353         {
00354           err = glthread_recursive_lock_init_multithreaded (lock);
00355           if (err != 0)
00356             {
00357               pthread_mutex_unlock (&lock->guard);
00358               return err;
00359             }
00360         }
00361       err = pthread_mutex_unlock (&lock->guard);
00362       if (err != 0)
00363         return err;
00364     }
00365   return pthread_mutex_lock (&lock->recmutex);
00366 }
00367 
00368 int
00369 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
00370 {
00371   if (!lock->initialized)
00372     return EINVAL;
00373   return pthread_mutex_unlock (&lock->recmutex);
00374 }
00375 
00376 int
00377 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
00378 {
00379   int err;
00380 
00381   if (!lock->initialized)
00382     return EINVAL;
00383   err = pthread_mutex_destroy (&lock->recmutex);
00384   if (err != 0)
00385     return err;
00386   lock->initialized = 0;
00387   return 0;
00388 }
00389 
00390 #  endif
00391 
00392 # else
00393 
00394 int
00395 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
00396 {
00397   int err;
00398 
00399   err = pthread_mutex_init (&lock->mutex, NULL);
00400   if (err != 0)
00401     return err;
00402   lock->owner = (pthread_t) 0;
00403   lock->depth = 0;
00404   return 0;
00405 }
00406 
00407 int
00408 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
00409 {
00410   pthread_t self = pthread_self ();
00411   if (lock->owner != self)
00412     {
00413       int err;
00414 
00415       err = pthread_mutex_lock (&lock->mutex);
00416       if (err != 0)
00417         return err;
00418       lock->owner = self;
00419     }
00420   if (++(lock->depth) == 0) /* wraparound? */
00421     {
00422       lock->depth--;
00423       return EAGAIN;
00424     }
00425   return 0;
00426 }
00427 
00428 int
00429 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
00430 {
00431   if (lock->owner != pthread_self ())
00432     return EPERM;
00433   if (lock->depth == 0)
00434     return EINVAL;
00435   if (--(lock->depth) == 0)
00436     {
00437       lock->owner = (pthread_t) 0;
00438       return pthread_mutex_unlock (&lock->mutex);
00439     }
00440   else
00441     return 0;
00442 }
00443 
00444 int
00445 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
00446 {
00447   if (lock->owner != (pthread_t) 0)
00448     return EBUSY;
00449   return pthread_mutex_destroy (&lock->mutex);
00450 }
00451 
00452 # endif
00453 
00454 /* -------------------------- gl_once_t datatype -------------------------- */
00455 
00456 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
00457 
00458 int
00459 glthread_once_singlethreaded (pthread_once_t *once_control)
00460 {
00461   /* We don't know whether pthread_once_t is an integer type, a floating-point
00462      type, a pointer type, or a structure type.  */
00463   char *firstbyte = (char *)once_control;
00464   if (*firstbyte == *(const char *)&fresh_once)
00465     {
00466       /* First time use of once_control.  Invert the first byte.  */
00467       *firstbyte = ~ *(const char *)&fresh_once;
00468       return 1;
00469     }
00470   else
00471     return 0;
00472 }
00473 
00474 #endif
00475 
00476 /* ========================================================================= */
00477 
00478 #if USE_PTH_THREADS
00479 
00480 /* Use the GNU Pth threads library.  */
00481 
00482 /* -------------------------- gl_lock_t datatype -------------------------- */
00483 
00484 /* ------------------------- gl_rwlock_t datatype ------------------------- */
00485 
00486 /* --------------------- gl_recursive_lock_t datatype --------------------- */
00487 
00488 /* -------------------------- gl_once_t datatype -------------------------- */
00489 
00490 static void
00491 glthread_once_call (void *arg)
00492 {
00493   void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
00494   void (*initfunction) (void) = *gl_once_temp_addr;
00495   initfunction ();
00496 }
00497 
00498 int
00499 glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
00500 {
00501   void (*temp) (void) = initfunction;
00502   return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
00503 }
00504 
00505 int
00506 glthread_once_singlethreaded (pth_once_t *once_control)
00507 {
00508   /* We know that pth_once_t is an integer type.  */
00509   if (*once_control == PTH_ONCE_INIT)
00510     {
00511       /* First time use of once_control.  Invert the marker.  */
00512       *once_control = ~ PTH_ONCE_INIT;
00513       return 1;
00514     }
00515   else
00516     return 0;
00517 }
00518 
00519 #endif
00520 
00521 /* ========================================================================= */
00522 
00523 #if USE_SOLARIS_THREADS
00524 
00525 /* Use the old Solaris threads library.  */
00526 
00527 /* -------------------------- gl_lock_t datatype -------------------------- */
00528 
00529 /* ------------------------- gl_rwlock_t datatype ------------------------- */
00530 
00531 /* --------------------- gl_recursive_lock_t datatype --------------------- */
00532 
00533 int
00534 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
00535 {
00536   int err;
00537 
00538   err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
00539   if (err != 0)
00540     return err;
00541   lock->owner = (thread_t) 0;
00542   lock->depth = 0;
00543   return 0;
00544 }
00545 
00546 int
00547 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
00548 {
00549   thread_t self = thr_self ();
00550   if (lock->owner != self)
00551     {
00552       int err;
00553 
00554       err = mutex_lock (&lock->mutex);
00555       if (err != 0)
00556         return err;
00557       lock->owner = self;
00558     }
00559   if (++(lock->depth) == 0) /* wraparound? */
00560     {
00561       lock->depth--;
00562       return EAGAIN;
00563     }
00564   return 0;
00565 }
00566 
00567 int
00568 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
00569 {
00570   if (lock->owner != thr_self ())
00571     return EPERM;
00572   if (lock->depth == 0)
00573     return EINVAL;
00574   if (--(lock->depth) == 0)
00575     {
00576       lock->owner = (thread_t) 0;
00577       return mutex_unlock (&lock->mutex);
00578     }
00579   else
00580     return 0;
00581 }
00582 
00583 int
00584 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
00585 {
00586   if (lock->owner != (thread_t) 0)
00587     return EBUSY;
00588   return mutex_destroy (&lock->mutex);
00589 }
00590 
00591 /* -------------------------- gl_once_t datatype -------------------------- */
00592 
00593 int
00594 glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
00595 {
00596   if (!once_control->inited)
00597     {
00598       int err;
00599 
00600       /* Use the mutex to guarantee that if another thread is already calling
00601          the initfunction, this thread waits until it's finished.  */
00602       err = mutex_lock (&once_control->mutex);
00603       if (err != 0)
00604         return err;
00605       if (!once_control->inited)
00606         {
00607           once_control->inited = 1;
00608           initfunction ();
00609         }
00610       return mutex_unlock (&once_control->mutex);
00611     }
00612   else
00613     return 0;
00614 }
00615 
00616 int
00617 glthread_once_singlethreaded (gl_once_t *once_control)
00618 {
00619   /* We know that gl_once_t contains an integer type.  */
00620   if (!once_control->inited)
00621     {
00622       /* First time use of once_control.  Invert the marker.  */
00623       once_control->inited = ~ 0;
00624       return 1;
00625     }
00626   else
00627     return 0;
00628 }
00629 
00630 #endif
00631 
00632 /* ========================================================================= */
00633 
00634 #if USE_WIN32_THREADS
00635 
00636 /* -------------------------- gl_lock_t datatype -------------------------- */
00637 
00638 void
00639 glthread_lock_init_func (gl_lock_t *lock)
00640 {
00641   InitializeCriticalSection (&lock->lock);
00642   lock->guard.done = 1;
00643 }
00644 
00645 int
00646 glthread_lock_lock_func (gl_lock_t *lock)
00647 {
00648   if (!lock->guard.done)
00649     {
00650       if (InterlockedIncrement (&lock->guard.started) == 0)
00651         /* This thread is the first one to need this lock.  Initialize it.  */
00652         glthread_lock_init (lock);
00653       else
00654         /* Yield the CPU while waiting for another thread to finish
00655            initializing this lock.  */
00656         while (!lock->guard.done)
00657           Sleep (0);
00658     }
00659   EnterCriticalSection (&lock->lock);
00660   return 0;
00661 }
00662 
00663 int
00664 glthread_lock_unlock_func (gl_lock_t *lock)
00665 {
00666   if (!lock->guard.done)
00667     return EINVAL;
00668   LeaveCriticalSection (&lock->lock);
00669   return 0;
00670 }
00671 
00672 int
00673 glthread_lock_destroy_func (gl_lock_t *lock)
00674 {
00675   if (!lock->guard.done)
00676     return EINVAL;
00677   DeleteCriticalSection (&lock->lock);
00678   lock->guard.done = 0;
00679   return 0;
00680 }
00681 
00682 /* ------------------------- gl_rwlock_t datatype ------------------------- */
00683 
00684 /* In this file, the waitqueues are implemented as circular arrays.  */
00685 #define gl_waitqueue_t gl_carray_waitqueue_t
00686 
00687 static inline void
00688 gl_waitqueue_init (gl_waitqueue_t *wq)
00689 {
00690   wq->array = NULL;
00691   wq->count = 0;
00692   wq->alloc = 0;
00693   wq->offset = 0;
00694 }
00695 
00696 /* Enqueues the current thread, represented by an event, in a wait queue.
00697    Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
00698 static HANDLE
00699 gl_waitqueue_add (gl_waitqueue_t *wq)
00700 {
00701   HANDLE event;
00702   unsigned int index;
00703 
00704   if (wq->count == wq->alloc)
00705     {
00706       unsigned int new_alloc = 2 * wq->alloc + 1;
00707       HANDLE *new_array =
00708         (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
00709       if (new_array == NULL)
00710         /* No more memory.  */
00711         return INVALID_HANDLE_VALUE;
00712       /* Now is a good opportunity to rotate the array so that its contents
00713          starts at offset 0.  */
00714       if (wq->offset > 0)
00715         {
00716           unsigned int old_count = wq->count;
00717           unsigned int old_alloc = wq->alloc;
00718           unsigned int old_offset = wq->offset;
00719           unsigned int i;
00720           if (old_offset + old_count > old_alloc)
00721             {
00722               unsigned int limit = old_offset + old_count - old_alloc;
00723               for (i = 0; i < limit; i++)
00724                 new_array[old_alloc + i] = new_array[i];
00725             }
00726           for (i = 0; i < old_count; i++)
00727             new_array[i] = new_array[old_offset + i];
00728           wq->offset = 0;
00729         }
00730       wq->array = new_array;
00731       wq->alloc = new_alloc;
00732     }
00733   /* Whether the created event is a manual-reset one or an auto-reset one,
00734      does not matter, since we will wait on it only once.  */
00735   event = CreateEvent (NULL, TRUE, FALSE, NULL);
00736   if (event == INVALID_HANDLE_VALUE)
00737     /* No way to allocate an event.  */
00738     return INVALID_HANDLE_VALUE;
00739   index = wq->offset + wq->count;
00740   if (index >= wq->alloc)
00741     index -= wq->alloc;
00742   wq->array[index] = event;
00743   wq->count++;
00744   return event;
00745 }
00746 
00747 /* Notifies the first thread from a wait queue and dequeues it.  */
00748 static inline void
00749 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
00750 {
00751   SetEvent (wq->array[wq->offset + 0]);
00752   wq->offset++;
00753   wq->count--;
00754   if (wq->count == 0 || wq->offset == wq->alloc)
00755     wq->offset = 0;
00756 }
00757 
00758 /* Notifies all threads from a wait queue and dequeues them all.  */
00759 static inline void
00760 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
00761 {
00762   unsigned int i;
00763 
00764   for (i = 0; i < wq->count; i++)
00765     {
00766       unsigned int index = wq->offset + i;
00767       if (index >= wq->alloc)
00768         index -= wq->alloc;
00769       SetEvent (wq->array[index]);
00770     }
00771   wq->count = 0;
00772   wq->offset = 0;
00773 }
00774 
00775 void
00776 glthread_rwlock_init_func (gl_rwlock_t *lock)
00777 {
00778   InitializeCriticalSection (&lock->lock);
00779   gl_waitqueue_init (&lock->waiting_readers);
00780   gl_waitqueue_init (&lock->waiting_writers);
00781   lock->runcount = 0;
00782   lock->guard.done = 1;
00783 }
00784 
00785 int
00786 glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
00787 {
00788   if (!lock->guard.done)
00789     {
00790       if (InterlockedIncrement (&lock->guard.started) == 0)
00791         /* This thread is the first one to need this lock.  Initialize it.  */
00792         glthread_rwlock_init (lock);
00793       else
00794         /* Yield the CPU while waiting for another thread to finish
00795            initializing this lock.  */
00796         while (!lock->guard.done)
00797           Sleep (0);
00798     }
00799   EnterCriticalSection (&lock->lock);
00800   /* Test whether only readers are currently running, and whether the runcount
00801      field will not overflow.  */
00802   if (!(lock->runcount + 1 > 0))
00803     {
00804       /* This thread has to wait for a while.  Enqueue it among the
00805          waiting_readers.  */
00806       HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
00807       if (event != INVALID_HANDLE_VALUE)
00808         {
00809           DWORD result;
00810           LeaveCriticalSection (&lock->lock);
00811           /* Wait until another thread signals this event.  */
00812           result = WaitForSingleObject (event, INFINITE);
00813           if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
00814             abort ();
00815           CloseHandle (event);
00816           /* The thread which signalled the event already did the bookkeeping:
00817              removed us from the waiting_readers, incremented lock->runcount.  */
00818           if (!(lock->runcount > 0))
00819             abort ();
00820           return 0;
00821         }
00822       else
00823         {
00824           /* Allocation failure.  Weird.  */
00825           do
00826             {
00827               LeaveCriticalSection (&lock->lock);
00828               Sleep (1);
00829               EnterCriticalSection (&lock->lock);
00830             }
00831           while (!(lock->runcount + 1 > 0));
00832         }
00833     }
00834   lock->runcount++;
00835   LeaveCriticalSection (&lock->lock);
00836   return 0;
00837 }
00838 
00839 int
00840 glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
00841 {
00842   if (!lock->guard.done)
00843     {
00844       if (InterlockedIncrement (&lock->guard.started) == 0)
00845         /* This thread is the first one to need this lock.  Initialize it.  */
00846         glthread_rwlock_init (lock);
00847       else
00848         /* Yield the CPU while waiting for another thread to finish
00849            initializing this lock.  */
00850         while (!lock->guard.done)
00851           Sleep (0);
00852     }
00853   EnterCriticalSection (&lock->lock);
00854   /* Test whether no readers or writers are currently running.  */
00855   if (!(lock->runcount == 0))
00856     {
00857       /* This thread has to wait for a while.  Enqueue it among the
00858          waiting_writers.  */
00859       HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
00860       if (event != INVALID_HANDLE_VALUE)
00861         {
00862           DWORD result;
00863           LeaveCriticalSection (&lock->lock);
00864           /* Wait until another thread signals this event.  */
00865           result = WaitForSingleObject (event, INFINITE);
00866           if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
00867             abort ();
00868           CloseHandle (event);
00869           /* The thread which signalled the event already did the bookkeeping:
00870              removed us from the waiting_writers, set lock->runcount = -1.  */
00871           if (!(lock->runcount == -1))
00872             abort ();
00873           return 0;
00874         }
00875       else
00876         {
00877           /* Allocation failure.  Weird.  */
00878           do
00879             {
00880               LeaveCriticalSection (&lock->lock);
00881               Sleep (1);
00882               EnterCriticalSection (&lock->lock);
00883             }
00884           while (!(lock->runcount == 0));
00885         }
00886     }
00887   lock->runcount--; /* runcount becomes -1 */
00888   LeaveCriticalSection (&lock->lock);
00889   return 0;
00890 }
00891 
00892 int
00893 glthread_rwlock_unlock_func (gl_rwlock_t *lock)
00894 {
00895   if (!lock->guard.done)
00896     return EINVAL;
00897   EnterCriticalSection (&lock->lock);
00898   if (lock->runcount < 0)
00899     {
00900       /* Drop a writer lock.  */
00901       if (!(lock->runcount == -1))
00902         abort ();
00903       lock->runcount = 0;
00904     }
00905   else
00906     {
00907       /* Drop a reader lock.  */
00908       if (!(lock->runcount > 0))
00909         {
00910           LeaveCriticalSection (&lock->lock);
00911           return EPERM;
00912         }
00913       lock->runcount--;
00914     }
00915   if (lock->runcount == 0)
00916     {
00917       /* POSIX recommends that "write locks shall take precedence over read
00918          locks", to avoid "writer starvation".  */
00919       if (lock->waiting_writers.count > 0)
00920         {
00921           /* Wake up one of the waiting writers.  */
00922           lock->runcount--;
00923           gl_waitqueue_notify_first (&lock->waiting_writers);
00924         }
00925       else
00926         {
00927           /* Wake up all waiting readers.  */
00928           lock->runcount += lock->waiting_readers.count;
00929           gl_waitqueue_notify_all (&lock->waiting_readers);
00930         }
00931     }
00932   LeaveCriticalSection (&lock->lock);
00933   return 0;
00934 }
00935 
00936 int
00937 glthread_rwlock_destroy_func (gl_rwlock_t *lock)
00938 {
00939   if (!lock->guard.done)
00940     return EINVAL;
00941   if (lock->runcount != 0)
00942     return EBUSY;
00943   DeleteCriticalSection (&lock->lock);
00944   if (lock->waiting_readers.array != NULL)
00945     free (lock->waiting_readers.array);
00946   if (lock->waiting_writers.array != NULL)
00947     free (lock->waiting_writers.array);
00948   lock->guard.done = 0;
00949   return 0;
00950 }
00951 
00952 /* --------------------- gl_recursive_lock_t datatype --------------------- */
00953 
00954 void
00955 glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
00956 {
00957   lock->owner = 0;
00958   lock->depth = 0;
00959   InitializeCriticalSection (&lock->lock);
00960   lock->guard.done = 1;
00961 }
00962 
00963 int
00964 glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
00965 {
00966   if (!lock->guard.done)
00967     {
00968       if (InterlockedIncrement (&lock->guard.started) == 0)
00969         /* This thread is the first one to need this lock.  Initialize it.  */
00970         glthread_recursive_lock_init (lock);
00971       else
00972         /* Yield the CPU while waiting for another thread to finish
00973            initializing this lock.  */
00974         while (!lock->guard.done)
00975           Sleep (0);
00976     }
00977   {
00978     DWORD self = GetCurrentThreadId ();
00979     if (lock->owner != self)
00980       {
00981         EnterCriticalSection (&lock->lock);
00982         lock->owner = self;
00983       }
00984     if (++(lock->depth) == 0) /* wraparound? */
00985       {
00986         lock->depth--;
00987         return EAGAIN;
00988       }
00989   }
00990   return 0;
00991 }
00992 
00993 int
00994 glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
00995 {
00996   if (lock->owner != GetCurrentThreadId ())
00997     return EPERM;
00998   if (lock->depth == 0)
00999     return EINVAL;
01000   if (--(lock->depth) == 0)
01001     {
01002       lock->owner = 0;
01003       LeaveCriticalSection (&lock->lock);
01004     }
01005   return 0;
01006 }
01007 
01008 int
01009 glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
01010 {
01011   if (lock->owner != 0)
01012     return EBUSY;
01013   DeleteCriticalSection (&lock->lock);
01014   lock->guard.done = 0;
01015   return 0;
01016 }
01017 
01018 /* -------------------------- gl_once_t datatype -------------------------- */
01019 
01020 void
01021 glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
01022 {
01023   if (once_control->inited <= 0)
01024     {
01025       if (InterlockedIncrement (&once_control->started) == 0)
01026         {
01027           /* This thread is the first one to come to this once_control.  */
01028           InitializeCriticalSection (&once_control->lock);
01029           EnterCriticalSection (&once_control->lock);
01030           once_control->inited = 0;
01031           initfunction ();
01032           once_control->inited = 1;
01033           LeaveCriticalSection (&once_control->lock);
01034         }
01035       else
01036         {
01037           /* Undo last operation.  */
01038           InterlockedDecrement (&once_control->started);
01039           /* Some other thread has already started the initialization.
01040              Yield the CPU while waiting for the other thread to finish
01041              initializing and taking the lock.  */
01042           while (once_control->inited < 0)
01043             Sleep (0);
01044           if (once_control->inited <= 0)
01045             {
01046               /* Take the lock.  This blocks until the other thread has
01047                  finished calling the initfunction.  */
01048               EnterCriticalSection (&once_control->lock);
01049               LeaveCriticalSection (&once_control->lock);
01050               if (!(once_control->inited > 0))
01051                 abort ();
01052             }
01053         }
01054     }
01055 }
01056 
01057 #endif
01058 
01059 /* ========================================================================= */