(root)/
m4-1.4.19/
lib/
glthread/
lock.c
       1  /* Locking in multithreaded situations.
       2     Copyright (C) 2005-2021 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 <https://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.  */
      19  
      20  #include <config.h>
      21  
      22  #include "glthread/lock.h"
      23  
      24  /* ========================================================================= */
      25  
      26  #if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
      27  
      28  /* -------------------------- gl_lock_t datatype -------------------------- */
      29  
      30  int
      31  glthread_lock_init (gl_lock_t *lock)
      32  {
      33    if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
      34      return ENOMEM;
      35    lock->init_needed = 0;
      36    return 0;
      37  }
      38  
      39  int
      40  glthread_lock_lock (gl_lock_t *lock)
      41  {
      42    if (lock->init_needed)
      43      call_once (&lock->init_once, lock->init_func);
      44    if (mtx_lock (&lock->mutex) != thrd_success)
      45      return EAGAIN;
      46    return 0;
      47  }
      48  
      49  int
      50  glthread_lock_unlock (gl_lock_t *lock)
      51  {
      52    if (lock->init_needed)
      53      call_once (&lock->init_once, lock->init_func);
      54    if (mtx_unlock (&lock->mutex) != thrd_success)
      55      return EINVAL;
      56    return 0;
      57  }
      58  
      59  int
      60  glthread_lock_destroy (gl_lock_t *lock)
      61  {
      62    if (lock->init_needed)
      63      call_once (&lock->init_once, lock->init_func);
      64    mtx_destroy (&lock->mutex);
      65    return 0;
      66  }
      67  
      68  /* ------------------------- gl_rwlock_t datatype ------------------------- */
      69  
      70  int
      71  glthread_rwlock_init (gl_rwlock_t *lock)
      72  {
      73    if (mtx_init (&lock->lock, mtx_plain) != thrd_success
      74        || cnd_init (&lock->waiting_readers) != thrd_success
      75        || cnd_init (&lock->waiting_writers) != thrd_success)
      76      return ENOMEM;
      77    lock->waiting_writers_count = 0;
      78    lock->runcount = 0;
      79    lock->init_needed = 0;
      80    return 0;
      81  }
      82  
      83  int
      84  glthread_rwlock_rdlock (gl_rwlock_t *lock)
      85  {
      86    if (lock->init_needed)
      87      call_once (&lock->init_once, lock->init_func);
      88    if (mtx_lock (&lock->lock) != thrd_success)
      89      return EAGAIN;
      90    /* Test whether only readers are currently running, and whether the runcount
      91       field will not overflow, and whether no writer is waiting.  The latter
      92       condition is because POSIX recommends that "write locks shall take
      93       precedence over read locks", to avoid "writer starvation".  */
      94    while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
      95      {
      96        /* This thread has to wait for a while.  Enqueue it among the
      97           waiting_readers.  */
      98        if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
      99          {
     100            mtx_unlock (&lock->lock);
     101            return EINVAL;
     102          }
     103      }
     104    lock->runcount++;
     105    if (mtx_unlock (&lock->lock) != thrd_success)
     106      return EINVAL;
     107    return 0;
     108  }
     109  
     110  int
     111  glthread_rwlock_wrlock (gl_rwlock_t *lock)
     112  {
     113    if (lock->init_needed)
     114      call_once (&lock->init_once, lock->init_func);
     115    if (mtx_lock (&lock->lock) != thrd_success)
     116      return EAGAIN;
     117    /* Test whether no readers or writers are currently running.  */
     118    while (!(lock->runcount == 0))
     119      {
     120        /* This thread has to wait for a while.  Enqueue it among the
     121           waiting_writers.  */
     122        lock->waiting_writers_count++;
     123        if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
     124          {
     125            lock->waiting_writers_count--;
     126            mtx_unlock (&lock->lock);
     127            return EINVAL;
     128          }
     129        lock->waiting_writers_count--;
     130      }
     131    lock->runcount--; /* runcount becomes -1 */
     132    if (mtx_unlock (&lock->lock) != thrd_success)
     133      return EINVAL;
     134    return 0;
     135  }
     136  
     137  int
     138  glthread_rwlock_unlock (gl_rwlock_t *lock)
     139  {
     140    if (lock->init_needed)
     141      call_once (&lock->init_once, lock->init_func);
     142    if (mtx_lock (&lock->lock) != thrd_success)
     143      return EAGAIN;
     144    if (lock->runcount < 0)
     145      {
     146        /* Drop a writer lock.  */
     147        if (!(lock->runcount == -1))
     148          {
     149            mtx_unlock (&lock->lock);
     150            return EINVAL;
     151          }
     152        lock->runcount = 0;
     153      }
     154    else
     155      {
     156        /* Drop a reader lock.  */
     157        if (!(lock->runcount > 0))
     158          {
     159            mtx_unlock (&lock->lock);
     160            return EINVAL;
     161          }
     162        lock->runcount--;
     163      }
     164    if (lock->runcount == 0)
     165      {
     166        /* POSIX recommends that "write locks shall take precedence over read
     167           locks", to avoid "writer starvation".  */
     168        if (lock->waiting_writers_count > 0)
     169          {
     170            /* Wake up one of the waiting writers.  */
     171            if (cnd_signal (&lock->waiting_writers) != thrd_success)
     172              {
     173                mtx_unlock (&lock->lock);
     174                return EINVAL;
     175              }
     176          }
     177        else
     178          {
     179            /* Wake up all waiting readers.  */
     180            if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
     181              {
     182                mtx_unlock (&lock->lock);
     183                return EINVAL;
     184              }
     185          }
     186      }
     187    if (mtx_unlock (&lock->lock) != thrd_success)
     188      return EINVAL;
     189    return 0;
     190  }
     191  
     192  int
     193  glthread_rwlock_destroy (gl_rwlock_t *lock)
     194  {
     195    if (lock->init_needed)
     196      call_once (&lock->init_once, lock->init_func);
     197    mtx_destroy (&lock->lock);
     198    cnd_destroy (&lock->waiting_readers);
     199    cnd_destroy (&lock->waiting_writers);
     200    return 0;
     201  }
     202  
     203  /* --------------------- gl_recursive_lock_t datatype --------------------- */
     204  
     205  int
     206  glthread_recursive_lock_init (gl_recursive_lock_t *lock)
     207  {
     208    if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
     209      return ENOMEM;
     210    lock->init_needed = 0;
     211    return 0;
     212  }
     213  
     214  int
     215  glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
     216  {
     217    if (lock->init_needed)
     218      call_once (&lock->init_once, lock->init_func);
     219    if (mtx_lock (&lock->mutex) != thrd_success)
     220      return EAGAIN;
     221    return 0;
     222  }
     223  
     224  int
     225  glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
     226  {
     227    if (lock->init_needed)
     228      call_once (&lock->init_once, lock->init_func);
     229    if (mtx_unlock (&lock->mutex) != thrd_success)
     230      return EINVAL;
     231    return 0;
     232  }
     233  
     234  int
     235  glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
     236  {
     237    if (lock->init_needed)
     238      call_once (&lock->init_once, lock->init_func);
     239    mtx_destroy (&lock->mutex);
     240    return 0;
     241  }
     242  
     243  /* -------------------------- gl_once_t datatype -------------------------- */
     244  
     245  #endif
     246  
     247  /* ========================================================================= */
     248  
     249  #if USE_POSIX_THREADS
     250  
     251  /* -------------------------- gl_lock_t datatype -------------------------- */
     252  
     253  /* ------------------------- gl_rwlock_t datatype ------------------------- */
     254  
     255  # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
     256  
     257  #  if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
     258  
     259  #   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
     260       /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
     261  
     262  int
     263  glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
     264  {
     265    pthread_rwlockattr_t attributes;
     266    int err;
     267  
     268    err = pthread_rwlockattr_init (&attributes);
     269    if (err != 0)
     270      return err;
     271    /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
     272       causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
     273       do this; see
     274       http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
     275    err = pthread_rwlockattr_setkind_np (&attributes,
     276                                         PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
     277    if (err == 0)
     278      err = pthread_rwlock_init(lock, &attributes);
     279    /* pthread_rwlockattr_destroy always returns 0.  It cannot influence the
     280       return value.  */
     281    pthread_rwlockattr_destroy (&attributes);
     282    return err;
     283  }
     284  
     285  #   endif
     286  #  else
     287  
     288  int
     289  glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
     290  {
     291    int err;
     292  
     293    err = pthread_rwlock_init (&lock->rwlock, NULL);
     294    if (err != 0)
     295      return err;
     296    lock->initialized = 1;
     297    return 0;
     298  }
     299  
     300  int
     301  glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
     302  {
     303    if (!lock->initialized)
     304      {
     305        int err;
     306  
     307        err = pthread_mutex_lock (&lock->guard);
     308        if (err != 0)
     309          return err;
     310        if (!lock->initialized)
     311          {
     312            err = glthread_rwlock_init_multithreaded (lock);
     313            if (err != 0)
     314              {
     315                pthread_mutex_unlock (&lock->guard);
     316                return err;
     317              }
     318          }
     319        err = pthread_mutex_unlock (&lock->guard);
     320        if (err != 0)
     321          return err;
     322      }
     323    return pthread_rwlock_rdlock (&lock->rwlock);
     324  }
     325  
     326  int
     327  glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
     328  {
     329    if (!lock->initialized)
     330      {
     331        int err;
     332  
     333        err = pthread_mutex_lock (&lock->guard);
     334        if (err != 0)
     335          return err;
     336        if (!lock->initialized)
     337          {
     338            err = glthread_rwlock_init_multithreaded (lock);
     339            if (err != 0)
     340              {
     341                pthread_mutex_unlock (&lock->guard);
     342                return err;
     343              }
     344          }
     345        err = pthread_mutex_unlock (&lock->guard);
     346        if (err != 0)
     347          return err;
     348      }
     349    return pthread_rwlock_wrlock (&lock->rwlock);
     350  }
     351  
     352  int
     353  glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
     354  {
     355    if (!lock->initialized)
     356      return EINVAL;
     357    return pthread_rwlock_unlock (&lock->rwlock);
     358  }
     359  
     360  int
     361  glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
     362  {
     363    int err;
     364  
     365    if (!lock->initialized)
     366      return EINVAL;
     367    err = pthread_rwlock_destroy (&lock->rwlock);
     368    if (err != 0)
     369      return err;
     370    lock->initialized = 0;
     371    return 0;
     372  }
     373  
     374  #  endif
     375  
     376  # else
     377  
     378  int
     379  glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
     380  {
     381    int err;
     382  
     383    err = pthread_mutex_init (&lock->lock, NULL);
     384    if (err != 0)
     385      return err;
     386    err = pthread_cond_init (&lock->waiting_readers, NULL);
     387    if (err != 0)
     388      return err;
     389    err = pthread_cond_init (&lock->waiting_writers, NULL);
     390    if (err != 0)
     391      return err;
     392    lock->waiting_writers_count = 0;
     393    lock->runcount = 0;
     394    return 0;
     395  }
     396  
     397  int
     398  glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
     399  {
     400    int err;
     401  
     402    err = pthread_mutex_lock (&lock->lock);
     403    if (err != 0)
     404      return err;
     405    /* Test whether only readers are currently running, and whether the runcount
     406       field will not overflow, and whether no writer is waiting.  The latter
     407       condition is because POSIX recommends that "write locks shall take
     408       precedence over read locks", to avoid "writer starvation".  */
     409    while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
     410      {
     411        /* This thread has to wait for a while.  Enqueue it among the
     412           waiting_readers.  */
     413        err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
     414        if (err != 0)
     415          {
     416            pthread_mutex_unlock (&lock->lock);
     417            return err;
     418          }
     419      }
     420    lock->runcount++;
     421    return pthread_mutex_unlock (&lock->lock);
     422  }
     423  
     424  int
     425  glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
     426  {
     427    int err;
     428  
     429    err = pthread_mutex_lock (&lock->lock);
     430    if (err != 0)
     431      return err;
     432    /* Test whether no readers or writers are currently running.  */
     433    while (!(lock->runcount == 0))
     434      {
     435        /* This thread has to wait for a while.  Enqueue it among the
     436           waiting_writers.  */
     437        lock->waiting_writers_count++;
     438        err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
     439        if (err != 0)
     440          {
     441            lock->waiting_writers_count--;
     442            pthread_mutex_unlock (&lock->lock);
     443            return err;
     444          }
     445        lock->waiting_writers_count--;
     446      }
     447    lock->runcount--; /* runcount becomes -1 */
     448    return pthread_mutex_unlock (&lock->lock);
     449  }
     450  
     451  int
     452  glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
     453  {
     454    int err;
     455  
     456    err = pthread_mutex_lock (&lock->lock);
     457    if (err != 0)
     458      return err;
     459    if (lock->runcount < 0)
     460      {
     461        /* Drop a writer lock.  */
     462        if (!(lock->runcount == -1))
     463          {
     464            pthread_mutex_unlock (&lock->lock);
     465            return EINVAL;
     466          }
     467        lock->runcount = 0;
     468      }
     469    else
     470      {
     471        /* Drop a reader lock.  */
     472        if (!(lock->runcount > 0))
     473          {
     474            pthread_mutex_unlock (&lock->lock);
     475            return EINVAL;
     476          }
     477        lock->runcount--;
     478      }
     479    if (lock->runcount == 0)
     480      {
     481        /* POSIX recommends that "write locks shall take precedence over read
     482           locks", to avoid "writer starvation".  */
     483        if (lock->waiting_writers_count > 0)
     484          {
     485            /* Wake up one of the waiting writers.  */
     486            err = pthread_cond_signal (&lock->waiting_writers);
     487            if (err != 0)
     488              {
     489                pthread_mutex_unlock (&lock->lock);
     490                return err;
     491              }
     492          }
     493        else
     494          {
     495            /* Wake up all waiting readers.  */
     496            err = pthread_cond_broadcast (&lock->waiting_readers);
     497            if (err != 0)
     498              {
     499                pthread_mutex_unlock (&lock->lock);
     500                return err;
     501              }
     502          }
     503      }
     504    return pthread_mutex_unlock (&lock->lock);
     505  }
     506  
     507  int
     508  glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
     509  {
     510    int err;
     511  
     512    err = pthread_mutex_destroy (&lock->lock);
     513    if (err != 0)
     514      return err;
     515    err = pthread_cond_destroy (&lock->waiting_readers);
     516    if (err != 0)
     517      return err;
     518    err = pthread_cond_destroy (&lock->waiting_writers);
     519    if (err != 0)
     520      return err;
     521    return 0;
     522  }
     523  
     524  # endif
     525  
     526  /* --------------------- gl_recursive_lock_t datatype --------------------- */
     527  
     528  # if HAVE_PTHREAD_MUTEX_RECURSIVE
     529  
     530  #  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
     531  
     532  int
     533  glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
     534  {
     535    pthread_mutexattr_t attributes;
     536    int err;
     537  
     538    err = pthread_mutexattr_init (&attributes);
     539    if (err != 0)
     540      return err;
     541    err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
     542    if (err != 0)
     543      {
     544        pthread_mutexattr_destroy (&attributes);
     545        return err;
     546      }
     547    err = pthread_mutex_init (lock, &attributes);
     548    if (err != 0)
     549      {
     550        pthread_mutexattr_destroy (&attributes);
     551        return err;
     552      }
     553    err = pthread_mutexattr_destroy (&attributes);
     554    if (err != 0)
     555      return err;
     556    return 0;
     557  }
     558  
     559  #  else
     560  
     561  int
     562  glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
     563  {
     564    pthread_mutexattr_t attributes;
     565    int err;
     566  
     567    err = pthread_mutexattr_init (&attributes);
     568    if (err != 0)
     569      return err;
     570    err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
     571    if (err != 0)
     572      {
     573        pthread_mutexattr_destroy (&attributes);
     574        return err;
     575      }
     576    err = pthread_mutex_init (&lock->recmutex, &attributes);
     577    if (err != 0)
     578      {
     579        pthread_mutexattr_destroy (&attributes);
     580        return err;
     581      }
     582    err = pthread_mutexattr_destroy (&attributes);
     583    if (err != 0)
     584      return err;
     585    lock->initialized = 1;
     586    return 0;
     587  }
     588  
     589  int
     590  glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
     591  {
     592    if (!lock->initialized)
     593      {
     594        int err;
     595  
     596        err = pthread_mutex_lock (&lock->guard);
     597        if (err != 0)
     598          return err;
     599        if (!lock->initialized)
     600          {
     601            err = glthread_recursive_lock_init_multithreaded (lock);
     602            if (err != 0)
     603              {
     604                pthread_mutex_unlock (&lock->guard);
     605                return err;
     606              }
     607          }
     608        err = pthread_mutex_unlock (&lock->guard);
     609        if (err != 0)
     610          return err;
     611      }
     612    return pthread_mutex_lock (&lock->recmutex);
     613  }
     614  
     615  int
     616  glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
     617  {
     618    if (!lock->initialized)
     619      return EINVAL;
     620    return pthread_mutex_unlock (&lock->recmutex);
     621  }
     622  
     623  int
     624  glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
     625  {
     626    int err;
     627  
     628    if (!lock->initialized)
     629      return EINVAL;
     630    err = pthread_mutex_destroy (&lock->recmutex);
     631    if (err != 0)
     632      return err;
     633    lock->initialized = 0;
     634    return 0;
     635  }
     636  
     637  #  endif
     638  
     639  # else
     640  
     641  int
     642  glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
     643  {
     644    int err;
     645  
     646    err = pthread_mutex_init (&lock->mutex, NULL);
     647    if (err != 0)
     648      return err;
     649    lock->owner = (pthread_t) 0;
     650    lock->depth = 0;
     651    return 0;
     652  }
     653  
     654  int
     655  glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
     656  {
     657    pthread_t self = pthread_self ();
     658    if (lock->owner != self)
     659      {
     660        int err;
     661  
     662        err = pthread_mutex_lock (&lock->mutex);
     663        if (err != 0)
     664          return err;
     665        lock->owner = self;
     666      }
     667    if (++(lock->depth) == 0) /* wraparound? */
     668      {
     669        lock->depth--;
     670        return EAGAIN;
     671      }
     672    return 0;
     673  }
     674  
     675  int
     676  glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
     677  {
     678    if (lock->owner != pthread_self ())
     679      return EPERM;
     680    if (lock->depth == 0)
     681      return EINVAL;
     682    if (--(lock->depth) == 0)
     683      {
     684        lock->owner = (pthread_t) 0;
     685        return pthread_mutex_unlock (&lock->mutex);
     686      }
     687    else
     688      return 0;
     689  }
     690  
     691  int
     692  glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
     693  {
     694    if (lock->owner != (pthread_t) 0)
     695      return EBUSY;
     696    return pthread_mutex_destroy (&lock->mutex);
     697  }
     698  
     699  # endif
     700  
     701  /* -------------------------- gl_once_t datatype -------------------------- */
     702  
     703  static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
     704  
     705  int
     706  glthread_once_singlethreaded (pthread_once_t *once_control)
     707  {
     708    /* We don't know whether pthread_once_t is an integer type, a floating-point
     709       type, a pointer type, or a structure type.  */
     710    char *firstbyte = (char *)once_control;
     711    if (*firstbyte == *(const char *)&fresh_once)
     712      {
     713        /* First time use of once_control.  Invert the first byte.  */
     714        *firstbyte = ~ *(const char *)&fresh_once;
     715        return 1;
     716      }
     717    else
     718      return 0;
     719  }
     720  
     721  # if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
     722  
     723  int
     724  glthread_once_multithreaded (pthread_once_t *once_control,
     725                               void (*init_function) (void))
     726  {
     727    int err = pthread_once (once_control, init_function);
     728    if (err == ENOSYS)
     729      {
     730        /* This happens on FreeBSD 11: The pthread_once function in libc returns
     731           ENOSYS.  */
     732        if (glthread_once_singlethreaded (once_control))
     733          init_function ();
     734        return 0;
     735      }
     736    return err;
     737  }
     738  
     739  # endif
     740  
     741  #endif
     742  
     743  /* ========================================================================= */
     744  
     745  #if USE_WINDOWS_THREADS
     746  
     747  #endif
     748  
     749  /* ========================================================================= */