LCOV - code coverage report
Current view: top level - lib/gltests/glthread/glthread - lock.c (source / functions) Hit Total Coverage
Test: GNU Libidn Lines: 0 22 0.0 %
Date: 2015-08-01 15:30:48 Functions: 0 2 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Locking in multithreaded situations.
       2             :    Copyright (C) 2005-2015 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@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           0 : glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
     285             : {
     286             :   pthread_mutexattr_t attributes;
     287             :   int err;
     288             : 
     289           0 :   err = pthread_mutexattr_init (&attributes);
     290           0 :   if (err != 0)
     291           0 :     return err;
     292           0 :   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
     293           0 :   if (err != 0)
     294             :     {
     295           0 :       pthread_mutexattr_destroy (&attributes);
     296           0 :       return err;
     297             :     }
     298           0 :   err = pthread_mutex_init (lock, &attributes);
     299           0 :   if (err != 0)
     300             :     {
     301           0 :       pthread_mutexattr_destroy (&attributes);
     302           0 :       return err;
     303             :     }
     304           0 :   err = pthread_mutexattr_destroy (&attributes);
     305           0 :   if (err != 0)
     306           0 :     return err;
     307           0 :   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           0 : 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           0 :   char *firstbyte = (char *)once_control;
     462           0 :   if (*firstbyte == *(const char *)&fresh_once)
     463             :     {
     464             :       /* First time use of once_control.  Invert the first byte.  */
     465           0 :       *firstbyte = ~ *(const char *)&fresh_once;
     466           0 :       return 1;
     467             :     }
     468             :   else
     469           0 :     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             : /* ========================================================================= */

Generated by: LCOV version 1.11