timespec_get: New module.
[gnulib.git] / lib / pthread-rwlock.c
blob039f675f48277242321c4e2475a335f758ccf99c
1 /* POSIX read-write locks.
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
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 2, or (at your option)
7 any later version.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2019. */
19 #include <config.h>
21 /* Specification. */
22 #include <pthread.h>
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
33 #if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
35 int
36 pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
38 *attr = 0;
39 return 0;
42 int
43 pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr _GL_UNUSED)
45 return 0;
48 #endif
50 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
51 /* Use Windows threads. */
53 int
54 pthread_rwlock_init (pthread_rwlock_t *lock,
55 const pthread_rwlockattr_t *attr _GL_UNUSED)
57 glwthread_timedrwlock_init (lock);
58 return 0;
61 int
62 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
64 return glwthread_timedrwlock_rdlock (lock);
67 int
68 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
70 return glwthread_timedrwlock_wrlock (lock);
73 int
74 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
76 return glwthread_timedrwlock_tryrdlock (lock);
79 int
80 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
82 return glwthread_timedrwlock_trywrlock (lock);
85 int
86 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
87 const struct timespec *abstime)
89 return glwthread_timedrwlock_timedrdlock (lock, abstime);
92 int
93 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
94 const struct timespec *abstime)
96 return glwthread_timedrwlock_timedwrlock (lock, abstime);
99 int
100 pthread_rwlock_unlock (pthread_rwlock_t *lock)
102 return glwthread_timedrwlock_unlock (lock);
106 pthread_rwlock_destroy (pthread_rwlock_t *lock)
108 return glwthread_timedrwlock_destroy (lock);
111 #elif HAVE_PTHREAD_H
112 /* Provide workarounds for POSIX threads. */
114 # if PTHREAD_RWLOCK_UNIMPLEMENTED
117 pthread_rwlock_init (pthread_rwlock_t *lock,
118 const pthread_rwlockattr_t *attr _GL_UNUSED)
120 int err;
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;
137 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
139 int err;
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))
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)
155 pthread_mutex_unlock (&lock->lock);
156 return err;
159 lock->runcount++;
160 return pthread_mutex_unlock (&lock->lock);
164 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
166 int err;
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))
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)
180 lock->waiting_writers_count--;
181 pthread_mutex_unlock (&lock->lock);
182 return err;
184 lock->waiting_writers_count--;
186 lock->runcount--; /* runcount becomes -1 */
187 return pthread_mutex_unlock (&lock->lock);
191 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
193 int err;
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))
204 /* This thread would have to wait for a while. Return instead. */
205 pthread_mutex_unlock (&lock->lock);
206 return EBUSY;
208 lock->runcount++;
209 return pthread_mutex_unlock (&lock->lock);
213 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
215 int err;
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))
223 /* This thread would have to wait for a while. Return instead. */
224 pthread_mutex_unlock (&lock->lock);
225 return EBUSY;
227 lock->runcount--; /* runcount becomes -1 */
228 return pthread_mutex_unlock (&lock->lock);
232 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
233 const struct timespec *abstime)
235 int err;
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))
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)
252 pthread_mutex_unlock (&lock->lock);
253 return err;
256 lock->runcount++;
257 return pthread_mutex_unlock (&lock->lock);
261 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
262 const struct timespec *abstime)
264 int err;
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))
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)
279 lock->waiting_writers_count--;
280 pthread_mutex_unlock (&lock->lock);
281 return err;
283 lock->waiting_writers_count--;
285 lock->runcount--; /* runcount becomes -1 */
286 return pthread_mutex_unlock (&lock->lock);
290 pthread_rwlock_unlock (pthread_rwlock_t *lock)
292 int err;
294 err = pthread_mutex_lock (&lock->lock);
295 if (err != 0)
296 return err;
297 if (lock->runcount < 0)
299 /* Drop a writer lock. */
300 if (!(lock->runcount == -1))
302 pthread_mutex_unlock (&lock->lock);
303 return EINVAL;
305 lock->runcount = 0;
307 else
309 /* Drop a reader lock. */
310 if (!(lock->runcount > 0))
312 pthread_mutex_unlock (&lock->lock);
313 return EINVAL;
315 lock->runcount--;
317 if (lock->runcount == 0)
319 /* POSIX recommends that "write locks shall take precedence over read
320 locks", to avoid "writer starvation". */
321 if (lock->waiting_writers_count > 0)
323 /* Wake up one of the waiting writers. */
324 err = pthread_cond_signal (&lock->waiting_writers);
325 if (err != 0)
327 pthread_mutex_unlock (&lock->lock);
328 return err;
331 else
333 /* Wake up all waiting readers. */
334 err = pthread_cond_broadcast (&lock->waiting_readers);
335 if (err != 0)
337 pthread_mutex_unlock (&lock->lock);
338 return err;
342 return pthread_mutex_unlock (&lock->lock);
346 pthread_rwlock_destroy (pthread_rwlock_t *lock)
348 int err;
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;
362 # elif PTHREAD_RWLOCK_LACKS_TIMEOUT
365 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
366 const struct timespec *abstime)
368 /* Poll the lock's state in regular intervals. Ugh. */
369 for (;;)
371 int err;
372 struct timeval currtime;
373 unsigned long remaining;
374 struct timespec duration;
376 err = pthread_rwlock_tryrdlock (lock);
377 if (err != EBUSY)
378 return err;
380 gettimeofday (&currtime, NULL);
382 if (currtime.tv_sec > abstime->tv_sec)
383 remaining = 0;
384 else
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
392 long nanoseconds =
393 abstime->tv_nsec - currtime.tv_usec * 1000;
394 if (nanoseconds >= 0)
396 remaining += nanoseconds;
397 if (remaining < nanoseconds) /* overflow? */
398 remaining = ULONG_MAX;
400 else
402 if (remaining >= - nanoseconds)
403 remaining -= (- nanoseconds);
404 else
405 remaining = 0;
409 if (remaining == 0)
410 return ETIMEDOUT;
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);
422 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
423 const struct timespec *abstime)
425 /* Poll the lock's state in regular intervals. Ugh. */
426 for (;;)
428 int err;
429 struct timeval currtime;
430 unsigned long remaining;
431 struct timespec duration;
433 err = pthread_rwlock_trywrlock (lock);
434 if (err != EBUSY)
435 return err;
437 gettimeofday (&currtime, NULL);
439 if (currtime.tv_sec > abstime->tv_sec)
440 remaining = 0;
441 else
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
449 long nanoseconds =
450 abstime->tv_nsec - currtime.tv_usec * 1000;
451 if (nanoseconds >= 0)
453 remaining += nanoseconds;
454 if (remaining < nanoseconds) /* overflow? */
455 remaining = ULONG_MAX;
457 else
459 if (remaining >= - nanoseconds)
460 remaining -= (- nanoseconds);
461 else
462 remaining = 0;
466 if (remaining == 0)
467 return ETIMEDOUT;
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);
478 # endif
480 #else
481 /* Provide a dummy implementation for single-threaded applications. */
483 /* The pthread_rwlock_t is an 'int', representing the number of readers running,
484 or -1 when a writer runs. */
487 pthread_rwlock_init (pthread_rwlock_t *lock,
488 const pthread_rwlockattr_t *attr _GL_UNUSED)
490 *lock = 0;
491 return 0;
495 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
497 if (*lock < 0)
498 return EDEADLK;
499 (*lock)++;
500 return 0;
504 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
506 if (*lock != 0)
507 return EDEADLK;
508 *lock = -1;
509 return 0;
513 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
515 return pthread_rwlock_rdlock (lock);
519 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
521 return pthread_rwlock_wrlock (lock);
525 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
526 const struct timespec *abstime _GL_UNUSED)
528 return pthread_rwlock_rdlock (lock);
532 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
533 const struct timespec *abstime _GL_UNUSED)
535 return pthread_rwlock_wrlock (lock);
539 pthread_rwlock_unlock (pthread_rwlock_t *lock)
541 if (*lock == 0)
542 return EPERM;
543 if (*lock < 0)
544 *lock = 0;
545 else /* *lock > 0 */
546 (*lock)--;
547 return 0;
551 pthread_rwlock_destroy (pthread_rwlock_t *lock)
553 if (*lock)
554 return EBUSY;
555 return 0;
558 #endif