1 /* POSIX read-write locks.
2    Copyright (C) 2019-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>, 2019.  */
18 
19 #include <config.h>
20 
21 /* Specification.  */
22 #include <pthread.h>
23 
24 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
25 # include "windows-timedrwlock.h"
26 #else
27 # include <errno.h>
28 # include <limits.h>
29 # include <sys/time.h>
30 # include <time.h>
31 #endif
32 
33 #if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
34 
35 int
pthread_rwlockattr_init(pthread_rwlockattr_t * attr)36 pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
37 {
38   *attr = 0;
39   return 0;
40 }
41 
42 int
pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr _GL_UNUSED)43 pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr _GL_UNUSED)
44 {
45   return 0;
46 }
47 
48 #endif
49 
50 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
51 /* Use Windows threads.  */
52 
53 int
pthread_rwlock_init(pthread_rwlock_t * lock,const pthread_rwlockattr_t * attr _GL_UNUSED)54 pthread_rwlock_init (pthread_rwlock_t *lock,
55                      const pthread_rwlockattr_t *attr _GL_UNUSED)
56 {
57   glwthread_timedrwlock_init (lock);
58   return 0;
59 }
60 
61 int
pthread_rwlock_rdlock(pthread_rwlock_t * lock)62 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
63 {
64   return glwthread_timedrwlock_rdlock (lock);
65 }
66 
67 int
pthread_rwlock_wrlock(pthread_rwlock_t * lock)68 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
69 {
70   return glwthread_timedrwlock_wrlock (lock);
71 }
72 
73 int
pthread_rwlock_tryrdlock(pthread_rwlock_t * lock)74 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
75 {
76   return glwthread_timedrwlock_tryrdlock (lock);
77 }
78 
79 int
pthread_rwlock_trywrlock(pthread_rwlock_t * lock)80 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
81 {
82   return glwthread_timedrwlock_trywrlock (lock);
83 }
84 
85 int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lock,const struct timespec * abstime)86 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
87                             const struct timespec *abstime)
88 {
89   return glwthread_timedrwlock_timedrdlock (lock, abstime);
90 }
91 
92 int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lock,const struct timespec * abstime)93 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
94                             const struct timespec *abstime)
95 {
96   return glwthread_timedrwlock_timedwrlock (lock, abstime);
97 }
98 
99 int
pthread_rwlock_unlock(pthread_rwlock_t * lock)100 pthread_rwlock_unlock (pthread_rwlock_t *lock)
101 {
102   return glwthread_timedrwlock_unlock (lock);
103 }
104 
105 int
pthread_rwlock_destroy(pthread_rwlock_t * lock)106 pthread_rwlock_destroy (pthread_rwlock_t *lock)
107 {
108   return glwthread_timedrwlock_destroy (lock);
109 }
110 
111 #elif HAVE_PTHREAD_H
112 /* Provide workarounds for POSIX threads.  */
113 
114 # if PTHREAD_RWLOCK_UNIMPLEMENTED
115 
116 int
pthread_rwlock_init(pthread_rwlock_t * lock,const pthread_rwlockattr_t * attr _GL_UNUSED)117 pthread_rwlock_init (pthread_rwlock_t *lock,
118                      const pthread_rwlockattr_t *attr _GL_UNUSED)
119 {
120   int err;
121 
122   err = pthread_mutex_init (&lock->lock, NULL);
123   if (err != 0)
124     return err;
125   err = pthread_cond_init (&lock->waiting_readers, NULL);
126   if (err != 0)
127     return err;
128   err = pthread_cond_init (&lock->waiting_writers, NULL);
129   if (err != 0)
130     return err;
131   lock->waiting_writers_count = 0;
132   lock->runcount = 0;
133   return 0;
134 }
135 
136 int
pthread_rwlock_rdlock(pthread_rwlock_t * lock)137 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
138 {
139   int err;
140 
141   err = pthread_mutex_lock (&lock->lock);
142   if (err != 0)
143     return err;
144   /* Test whether only readers are currently running, and whether the runcount
145      field will not overflow, and whether no writer is waiting.  The latter
146      condition is because POSIX recommends that "write locks shall take
147      precedence over read locks", to avoid "writer starvation".  */
148   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
149     {
150       /* This thread has to wait for a while.  Enqueue it among the
151          waiting_readers.  */
152       err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
153       if (err != 0)
154         {
155           pthread_mutex_unlock (&lock->lock);
156           return err;
157         }
158     }
159   lock->runcount++;
160   return pthread_mutex_unlock (&lock->lock);
161 }
162 
163 int
pthread_rwlock_wrlock(pthread_rwlock_t * lock)164 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
165 {
166   int err;
167 
168   err = pthread_mutex_lock (&lock->lock);
169   if (err != 0)
170     return err;
171   /* Test whether no readers or writers are currently running.  */
172   while (!(lock->runcount == 0))
173     {
174       /* This thread has to wait for a while.  Enqueue it among the
175          waiting_writers.  */
176       lock->waiting_writers_count++;
177       err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
178       if (err != 0)
179         {
180           lock->waiting_writers_count--;
181           pthread_mutex_unlock (&lock->lock);
182           return err;
183         }
184       lock->waiting_writers_count--;
185     }
186   lock->runcount--; /* runcount becomes -1 */
187   return pthread_mutex_unlock (&lock->lock);
188 }
189 
190 int
pthread_rwlock_tryrdlock(pthread_rwlock_t * lock)191 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
192 {
193   int err;
194 
195   err = pthread_mutex_lock (&lock->lock);
196   if (err != 0)
197     return err;
198   /* Test whether only readers are currently running, and whether the runcount
199      field will not overflow, and whether no writer is waiting.  The latter
200      condition is because POSIX recommends that "write locks shall take
201      precedence over read locks", to avoid "writer starvation".  */
202   if (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
203     {
204       /* This thread would have to wait for a while.  Return instead.  */
205       pthread_mutex_unlock (&lock->lock);
206       return EBUSY;
207     }
208   lock->runcount++;
209   return pthread_mutex_unlock (&lock->lock);
210 }
211 
212 int
pthread_rwlock_trywrlock(pthread_rwlock_t * lock)213 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
214 {
215   int err;
216 
217   err = pthread_mutex_lock (&lock->lock);
218   if (err != 0)
219     return err;
220   /* Test whether no readers or writers are currently running.  */
221   if (!(lock->runcount == 0))
222     {
223       /* This thread would have to wait for a while.  Return instead.  */
224       pthread_mutex_unlock (&lock->lock);
225       return EBUSY;
226     }
227   lock->runcount--; /* runcount becomes -1 */
228   return pthread_mutex_unlock (&lock->lock);
229 }
230 
231 int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lock,const struct timespec * abstime)232 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
233                             const struct timespec *abstime)
234 {
235   int err;
236 
237   err = pthread_mutex_lock (&lock->lock);
238   if (err != 0)
239     return err;
240   /* Test whether only readers are currently running, and whether the runcount
241      field will not overflow, and whether no writer is waiting.  The latter
242      condition is because POSIX recommends that "write locks shall take
243      precedence over read locks", to avoid "writer starvation".  */
244   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
245     {
246       /* This thread has to wait for a while.  Enqueue it among the
247          waiting_readers.  */
248       err = pthread_cond_timedwait (&lock->waiting_readers, &lock->lock,
249                                     abstime);
250       if (err != 0)
251         {
252           pthread_mutex_unlock (&lock->lock);
253           return err;
254         }
255     }
256   lock->runcount++;
257   return pthread_mutex_unlock (&lock->lock);
258 }
259 
260 int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lock,const struct timespec * abstime)261 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
262                             const struct timespec *abstime)
263 {
264   int err;
265 
266   err = pthread_mutex_lock (&lock->lock);
267   if (err != 0)
268     return err;
269   /* Test whether no readers or writers are currently running.  */
270   while (!(lock->runcount == 0))
271     {
272       /* This thread has to wait for a while.  Enqueue it among the
273          waiting_writers.  */
274       lock->waiting_writers_count++;
275       err = pthread_cond_timedwait (&lock->waiting_writers, &lock->lock,
276                                     abstime);
277       if (err != 0)
278         {
279           lock->waiting_writers_count--;
280           pthread_mutex_unlock (&lock->lock);
281           return err;
282         }
283       lock->waiting_writers_count--;
284     }
285   lock->runcount--; /* runcount becomes -1 */
286   return pthread_mutex_unlock (&lock->lock);
287 }
288 
289 int
pthread_rwlock_unlock(pthread_rwlock_t * lock)290 pthread_rwlock_unlock (pthread_rwlock_t *lock)
291 {
292   int err;
293 
294   err = pthread_mutex_lock (&lock->lock);
295   if (err != 0)
296     return err;
297   if (lock->runcount < 0)
298     {
299       /* Drop a writer lock.  */
300       if (!(lock->runcount == -1))
301         {
302           pthread_mutex_unlock (&lock->lock);
303           return EINVAL;
304         }
305       lock->runcount = 0;
306     }
307   else
308     {
309       /* Drop a reader lock.  */
310       if (!(lock->runcount > 0))
311         {
312           pthread_mutex_unlock (&lock->lock);
313           return EINVAL;
314         }
315       lock->runcount--;
316     }
317   if (lock->runcount == 0)
318     {
319       /* POSIX recommends that "write locks shall take precedence over read
320          locks", to avoid "writer starvation".  */
321       if (lock->waiting_writers_count > 0)
322         {
323           /* Wake up one of the waiting writers.  */
324           err = pthread_cond_signal (&lock->waiting_writers);
325           if (err != 0)
326             {
327               pthread_mutex_unlock (&lock->lock);
328               return err;
329             }
330         }
331       else
332         {
333           /* Wake up all waiting readers.  */
334           err = pthread_cond_broadcast (&lock->waiting_readers);
335           if (err != 0)
336             {
337               pthread_mutex_unlock (&lock->lock);
338               return err;
339             }
340         }
341     }
342   return pthread_mutex_unlock (&lock->lock);
343 }
344 
345 int
pthread_rwlock_destroy(pthread_rwlock_t * lock)346 pthread_rwlock_destroy (pthread_rwlock_t *lock)
347 {
348   int err;
349 
350   err = pthread_mutex_destroy (&lock->lock);
351   if (err != 0)
352     return err;
353   err = pthread_cond_destroy (&lock->waiting_readers);
354   if (err != 0)
355     return err;
356   err = pthread_cond_destroy (&lock->waiting_writers);
357   if (err != 0)
358     return err;
359   return 0;
360 }
361 
362 # elif PTHREAD_RWLOCK_LACKS_TIMEOUT
363 
364 int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lock,const struct timespec * abstime)365 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
366                             const struct timespec *abstime)
367 {
368   /* Poll the lock's state in regular intervals.  Ugh.  */
369   for (;;)
370     {
371       int err;
372       struct timeval currtime;
373       unsigned long remaining;
374       struct timespec duration;
375 
376       err = pthread_rwlock_tryrdlock (lock);
377       if (err != EBUSY)
378         return err;
379 
380       gettimeofday (&currtime, NULL);
381 
382       if (currtime.tv_sec > abstime->tv_sec)
383         remaining = 0;
384       else
385         {
386           unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
387           remaining = seconds * 1000000000;
388           if (remaining / 1000000000 != seconds) /* overflow? */
389             remaining = ULONG_MAX;
390           else
391             {
392               long nanoseconds =
393                 abstime->tv_nsec - currtime.tv_usec * 1000;
394               if (nanoseconds >= 0)
395                 {
396                   remaining += nanoseconds;
397                   if (remaining < nanoseconds) /* overflow? */
398                     remaining = ULONG_MAX;
399                 }
400               else
401                 {
402                   if (remaining >= - nanoseconds)
403                     remaining -= (- nanoseconds);
404                   else
405                     remaining = 0;
406                 }
407             }
408         }
409       if (remaining == 0)
410         return ETIMEDOUT;
411 
412       /* Sleep 1 ms.  */
413       duration.tv_sec = 0;
414       duration.tv_nsec = 1000000;
415       if (duration.tv_nsec > remaining)
416         duration.tv_nsec = remaining;
417       nanosleep (&duration, NULL);
418     }
419 }
420 
421 int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lock,const struct timespec * abstime)422 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
423                             const struct timespec *abstime)
424 {
425   /* Poll the lock's state in regular intervals.  Ugh.  */
426   for (;;)
427     {
428       int err;
429       struct timeval currtime;
430       unsigned long remaining;
431       struct timespec duration;
432 
433       err = pthread_rwlock_trywrlock (lock);
434       if (err != EBUSY)
435         return err;
436 
437       gettimeofday (&currtime, NULL);
438 
439       if (currtime.tv_sec > abstime->tv_sec)
440         remaining = 0;
441       else
442         {
443           unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
444           remaining = seconds * 1000000000;
445           if (remaining / 1000000000 != seconds) /* overflow? */
446             remaining = ULONG_MAX;
447           else
448             {
449               long nanoseconds =
450                 abstime->tv_nsec - currtime.tv_usec * 1000;
451               if (nanoseconds >= 0)
452                 {
453                   remaining += nanoseconds;
454                   if (remaining < nanoseconds) /* overflow? */
455                     remaining = ULONG_MAX;
456                 }
457               else
458                 {
459                   if (remaining >= - nanoseconds)
460                     remaining -= (- nanoseconds);
461                   else
462                     remaining = 0;
463                 }
464             }
465         }
466       if (remaining == 0)
467         return ETIMEDOUT;
468 
469       /* Sleep 1 ms.  */
470       duration.tv_sec = 0;
471       duration.tv_nsec = 1000000;
472       if (duration.tv_nsec > remaining)
473         duration.tv_nsec = remaining;
474       nanosleep (&duration, NULL);
475     }
476 }
477 
478 # endif
479 
480 #else
481 /* Provide a dummy implementation for single-threaded applications.  */
482 
483 /* The pthread_rwlock_t is an 'int', representing the number of readers running,
484    or -1 when a writer runs.  */
485 
486 int
pthread_rwlock_init(pthread_rwlock_t * lock,const pthread_rwlockattr_t * attr _GL_UNUSED)487 pthread_rwlock_init (pthread_rwlock_t *lock,
488                      const pthread_rwlockattr_t *attr _GL_UNUSED)
489 {
490   *lock = 0;
491   return 0;
492 }
493 
494 int
pthread_rwlock_rdlock(pthread_rwlock_t * lock)495 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
496 {
497   if (*lock < 0)
498     return EDEADLK;
499   (*lock)++;
500   return 0;
501 }
502 
503 int
pthread_rwlock_wrlock(pthread_rwlock_t * lock)504 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
505 {
506   if (*lock != 0)
507     return EDEADLK;
508   *lock = -1;
509   return 0;
510 }
511 
512 int
pthread_rwlock_tryrdlock(pthread_rwlock_t * lock)513 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
514 {
515   return pthread_rwlock_rdlock (lock);
516 }
517 
518 int
pthread_rwlock_trywrlock(pthread_rwlock_t * lock)519 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
520 {
521   return pthread_rwlock_wrlock (lock);
522 }
523 
524 int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lock,const struct timespec * abstime _GL_UNUSED)525 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
526                             const struct timespec *abstime _GL_UNUSED)
527 {
528   return pthread_rwlock_rdlock (lock);
529 }
530 
531 int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lock,const struct timespec * abstime _GL_UNUSED)532 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
533                             const struct timespec *abstime _GL_UNUSED)
534 {
535   return pthread_rwlock_wrlock (lock);
536 }
537 
538 int
pthread_rwlock_unlock(pthread_rwlock_t * lock)539 pthread_rwlock_unlock (pthread_rwlock_t *lock)
540 {
541   if (*lock == 0)
542     return EPERM;
543   if (*lock < 0)
544     *lock = 0;
545   else /* *lock > 0 */
546     (*lock)--;
547   return 0;
548 }
549 
550 int
pthread_rwlock_destroy(pthread_rwlock_t * lock)551 pthread_rwlock_destroy (pthread_rwlock_t *lock)
552 {
553   if (*lock)
554     return EBUSY;
555   return 0;
556 }
557 
558 #endif
559