netdb tests: Check for NI_MAXHOST and NI_MAXSERV.
[gnulib.git] / lib / pthread-rwlock.c
blobcb972407db54a4b379d95f84ee3b7358e2abcbb5
1 /* POSIX read-write locks.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser 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 #ifndef MIN
34 # define MIN(a, b) ((a) < (b) ? (a) : (b))
35 #endif
37 #if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
39 int
40 pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
42 *attr = 0;
43 return 0;
46 int
47 pthread_rwlockattr_destroy (_GL_UNUSED pthread_rwlockattr_t *attr)
49 return 0;
52 #endif
54 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
55 /* Use Windows threads. */
57 int
58 pthread_rwlock_init (pthread_rwlock_t *lock,
59 _GL_UNUSED const pthread_rwlockattr_t *attr)
61 glwthread_timedrwlock_init (lock);
62 return 0;
65 int
66 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
68 return glwthread_timedrwlock_rdlock (lock);
71 int
72 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
74 return glwthread_timedrwlock_wrlock (lock);
77 int
78 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
80 return glwthread_timedrwlock_tryrdlock (lock);
83 int
84 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
86 return glwthread_timedrwlock_trywrlock (lock);
89 int
90 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
91 const struct timespec *abstime)
93 return glwthread_timedrwlock_timedrdlock (lock, abstime);
96 int
97 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
98 const struct timespec *abstime)
100 return glwthread_timedrwlock_timedwrlock (lock, abstime);
104 pthread_rwlock_unlock (pthread_rwlock_t *lock)
106 return glwthread_timedrwlock_unlock (lock);
110 pthread_rwlock_destroy (pthread_rwlock_t *lock)
112 return glwthread_timedrwlock_destroy (lock);
115 #elif HAVE_PTHREAD_H
116 /* Provide workarounds for POSIX threads. */
118 # if PTHREAD_RWLOCK_UNIMPLEMENTED
121 pthread_rwlock_init (pthread_rwlock_t *lock,
122 _GL_UNUSED const pthread_rwlockattr_t *attr)
124 int err;
126 err = pthread_mutex_init (&lock->lock, NULL);
127 if (err != 0)
128 return err;
129 err = pthread_cond_init (&lock->waiting_readers, NULL);
130 if (err != 0)
131 return err;
132 err = pthread_cond_init (&lock->waiting_writers, NULL);
133 if (err != 0)
134 return err;
135 lock->waiting_writers_count = 0;
136 lock->runcount = 0;
137 return 0;
141 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
143 int err;
145 err = pthread_mutex_lock (&lock->lock);
146 if (err != 0)
147 return err;
148 /* Test whether only readers are currently running, and whether the runcount
149 field will not overflow, and whether no writer is waiting. The latter
150 condition is because POSIX recommends that "write locks shall take
151 precedence over read locks", to avoid "writer starvation". */
152 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
154 /* This thread has to wait for a while. Enqueue it among the
155 waiting_readers. */
156 err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
157 if (err != 0)
159 pthread_mutex_unlock (&lock->lock);
160 return err;
163 lock->runcount++;
164 return pthread_mutex_unlock (&lock->lock);
168 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
170 int err;
172 err = pthread_mutex_lock (&lock->lock);
173 if (err != 0)
174 return err;
175 /* Test whether no readers or writers are currently running. */
176 while (!(lock->runcount == 0))
178 /* This thread has to wait for a while. Enqueue it among the
179 waiting_writers. */
180 lock->waiting_writers_count++;
181 err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
182 if (err != 0)
184 lock->waiting_writers_count--;
185 pthread_mutex_unlock (&lock->lock);
186 return err;
188 lock->waiting_writers_count--;
190 lock->runcount--; /* runcount becomes -1 */
191 return pthread_mutex_unlock (&lock->lock);
195 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
197 int err;
199 err = pthread_mutex_lock (&lock->lock);
200 if (err != 0)
201 return err;
202 /* Test whether only readers are currently running, and whether the runcount
203 field will not overflow, and whether no writer is waiting. The latter
204 condition is because POSIX recommends that "write locks shall take
205 precedence over read locks", to avoid "writer starvation". */
206 if (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
208 /* This thread would have to wait for a while. Return instead. */
209 pthread_mutex_unlock (&lock->lock);
210 return EBUSY;
212 lock->runcount++;
213 return pthread_mutex_unlock (&lock->lock);
217 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
219 int err;
221 err = pthread_mutex_lock (&lock->lock);
222 if (err != 0)
223 return err;
224 /* Test whether no readers or writers are currently running. */
225 if (!(lock->runcount == 0))
227 /* This thread would have to wait for a while. Return instead. */
228 pthread_mutex_unlock (&lock->lock);
229 return EBUSY;
231 lock->runcount--; /* runcount becomes -1 */
232 return pthread_mutex_unlock (&lock->lock);
236 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
237 const struct timespec *abstime)
239 int err;
241 err = pthread_mutex_lock (&lock->lock);
242 if (err != 0)
243 return err;
244 /* Test whether only readers are currently running, and whether the runcount
245 field will not overflow, and whether no writer is waiting. The latter
246 condition is because POSIX recommends that "write locks shall take
247 precedence over read locks", to avoid "writer starvation". */
248 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
250 /* This thread has to wait for a while. Enqueue it among the
251 waiting_readers. */
252 err = pthread_cond_timedwait (&lock->waiting_readers, &lock->lock,
253 abstime);
254 if (err != 0)
256 pthread_mutex_unlock (&lock->lock);
257 return err;
260 lock->runcount++;
261 return pthread_mutex_unlock (&lock->lock);
265 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
266 const struct timespec *abstime)
268 int err;
270 err = pthread_mutex_lock (&lock->lock);
271 if (err != 0)
272 return err;
273 /* Test whether no readers or writers are currently running. */
274 while (!(lock->runcount == 0))
276 /* This thread has to wait for a while. Enqueue it among the
277 waiting_writers. */
278 lock->waiting_writers_count++;
279 err = pthread_cond_timedwait (&lock->waiting_writers, &lock->lock,
280 abstime);
281 if (err != 0)
283 lock->waiting_writers_count--;
284 pthread_mutex_unlock (&lock->lock);
285 return err;
287 lock->waiting_writers_count--;
289 lock->runcount--; /* runcount becomes -1 */
290 return pthread_mutex_unlock (&lock->lock);
294 pthread_rwlock_unlock (pthread_rwlock_t *lock)
296 int err;
298 err = pthread_mutex_lock (&lock->lock);
299 if (err != 0)
300 return err;
301 if (lock->runcount < 0)
303 /* Drop a writer lock. */
304 if (!(lock->runcount == -1))
306 pthread_mutex_unlock (&lock->lock);
307 return EINVAL;
309 lock->runcount = 0;
311 else
313 /* Drop a reader lock. */
314 if (!(lock->runcount > 0))
316 pthread_mutex_unlock (&lock->lock);
317 return EINVAL;
319 lock->runcount--;
321 if (lock->runcount == 0)
323 /* POSIX recommends that "write locks shall take precedence over read
324 locks", to avoid "writer starvation". */
325 if (lock->waiting_writers_count > 0)
327 /* Wake up one of the waiting writers. */
328 err = pthread_cond_signal (&lock->waiting_writers);
329 if (err != 0)
331 pthread_mutex_unlock (&lock->lock);
332 return err;
335 else
337 /* Wake up all waiting readers. */
338 err = pthread_cond_broadcast (&lock->waiting_readers);
339 if (err != 0)
341 pthread_mutex_unlock (&lock->lock);
342 return err;
346 return pthread_mutex_unlock (&lock->lock);
350 pthread_rwlock_destroy (pthread_rwlock_t *lock)
352 int err;
354 err = pthread_mutex_destroy (&lock->lock);
355 if (err != 0)
356 return err;
357 err = pthread_cond_destroy (&lock->waiting_readers);
358 if (err != 0)
359 return err;
360 err = pthread_cond_destroy (&lock->waiting_writers);
361 if (err != 0)
362 return err;
363 return 0;
366 # elif PTHREAD_RWLOCK_LACKS_TIMEOUT
369 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
370 const struct timespec *abstime)
372 /* Poll the lock's state in regular intervals. Ugh. */
373 for (;;)
375 int err;
376 struct timeval currtime;
377 unsigned long remaining;
379 err = pthread_rwlock_tryrdlock (lock);
380 if (err != EBUSY)
381 return err;
383 gettimeofday (&currtime, NULL);
385 if (currtime.tv_sec > abstime->tv_sec)
386 remaining = 0;
387 else
389 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
390 remaining = seconds * 1000000000;
391 if (remaining / 1000000000 != seconds) /* overflow? */
392 remaining = ULONG_MAX;
393 else
395 long nanoseconds =
396 abstime->tv_nsec - currtime.tv_usec * 1000;
397 if (nanoseconds >= 0)
399 remaining += nanoseconds;
400 if (remaining < nanoseconds) /* overflow? */
401 remaining = ULONG_MAX;
403 else
405 if (remaining >= - nanoseconds)
406 remaining -= (- nanoseconds);
407 else
408 remaining = 0;
412 if (remaining == 0)
413 return ETIMEDOUT;
415 /* Sleep 1 ms. */
416 struct timespec duration =
418 .tv_sec = 0,
419 .tv_nsec = MIN (1000000, remaining)
421 nanosleep (&duration, NULL);
426 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
427 const struct timespec *abstime)
429 /* Poll the lock's state in regular intervals. Ugh. */
430 for (;;)
432 int err;
433 struct timeval currtime;
434 unsigned long remaining;
436 err = pthread_rwlock_trywrlock (lock);
437 if (err != EBUSY)
438 return err;
440 gettimeofday (&currtime, NULL);
442 if (currtime.tv_sec > abstime->tv_sec)
443 remaining = 0;
444 else
446 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
447 remaining = seconds * 1000000000;
448 if (remaining / 1000000000 != seconds) /* overflow? */
449 remaining = ULONG_MAX;
450 else
452 long nanoseconds =
453 abstime->tv_nsec - currtime.tv_usec * 1000;
454 if (nanoseconds >= 0)
456 remaining += nanoseconds;
457 if (remaining < nanoseconds) /* overflow? */
458 remaining = ULONG_MAX;
460 else
462 if (remaining >= - nanoseconds)
463 remaining -= (- nanoseconds);
464 else
465 remaining = 0;
469 if (remaining == 0)
470 return ETIMEDOUT;
472 /* Sleep 1 ms. */
473 struct timespec duration =
475 .tv_sec = 0,
476 .tv_nsec = MIN (1000000, remaining)
478 nanosleep (&duration, NULL);
482 # endif
484 #else
485 /* Provide a dummy implementation for single-threaded applications. */
487 /* The pthread_rwlock_t is an 'int', representing the number of readers running,
488 or -1 when a writer runs. */
491 pthread_rwlock_init (pthread_rwlock_t *lock,
492 _GL_UNUSED const pthread_rwlockattr_t *attr)
494 *lock = 0;
495 return 0;
499 pthread_rwlock_rdlock (pthread_rwlock_t *lock)
501 if (*lock < 0)
502 return EDEADLK;
503 (*lock)++;
504 return 0;
508 pthread_rwlock_wrlock (pthread_rwlock_t *lock)
510 if (*lock != 0)
511 return EDEADLK;
512 *lock = -1;
513 return 0;
517 pthread_rwlock_tryrdlock (pthread_rwlock_t *lock)
519 return pthread_rwlock_rdlock (lock);
523 pthread_rwlock_trywrlock (pthread_rwlock_t *lock)
525 return pthread_rwlock_wrlock (lock);
529 pthread_rwlock_timedrdlock (pthread_rwlock_t *lock,
530 _GL_UNUSED const struct timespec *abstime)
532 return pthread_rwlock_rdlock (lock);
536 pthread_rwlock_timedwrlock (pthread_rwlock_t *lock,
537 _GL_UNUSED const struct timespec *abstime)
539 return pthread_rwlock_wrlock (lock);
543 pthread_rwlock_unlock (pthread_rwlock_t *lock)
545 if (*lock == 0)
546 return EPERM;
547 if (*lock < 0)
548 *lock = 0;
549 else /* *lock > 0 */
550 (*lock)--;
551 return 0;
555 pthread_rwlock_destroy (pthread_rwlock_t *lock)
557 if (*lock)
558 return EBUSY;
559 return 0;
562 #endif