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. */
24 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
25 # include "windows-timedrwlock.h"
29 # include <sys/time.h>
34 # define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
40 pthread_rwlockattr_init (pthread_rwlockattr_t
*attr
)
47 pthread_rwlockattr_destroy (_GL_UNUSED pthread_rwlockattr_t
*attr
)
54 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
55 /* Use Windows threads. */
58 pthread_rwlock_init (pthread_rwlock_t
*lock
,
59 _GL_UNUSED
const pthread_rwlockattr_t
*attr
)
61 glwthread_timedrwlock_init (lock
);
66 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
68 return glwthread_timedrwlock_rdlock (lock
);
72 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
74 return glwthread_timedrwlock_wrlock (lock
);
78 pthread_rwlock_tryrdlock (pthread_rwlock_t
*lock
)
80 return glwthread_timedrwlock_tryrdlock (lock
);
84 pthread_rwlock_trywrlock (pthread_rwlock_t
*lock
)
86 return glwthread_timedrwlock_trywrlock (lock
);
90 pthread_rwlock_timedrdlock (pthread_rwlock_t
*lock
,
91 const struct timespec
*abstime
)
93 return glwthread_timedrwlock_timedrdlock (lock
, abstime
);
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
);
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
)
126 err
= pthread_mutex_init (&lock
->lock
, NULL
);
129 err
= pthread_cond_init (&lock
->waiting_readers
, NULL
);
132 err
= pthread_cond_init (&lock
->waiting_writers
, NULL
);
135 lock
->waiting_writers_count
= 0;
141 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
145 err
= pthread_mutex_lock (&lock
->lock
);
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
156 err
= pthread_cond_wait (&lock
->waiting_readers
, &lock
->lock
);
159 pthread_mutex_unlock (&lock
->lock
);
164 return pthread_mutex_unlock (&lock
->lock
);
168 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
172 err
= pthread_mutex_lock (&lock
->lock
);
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
180 lock
->waiting_writers_count
++;
181 err
= pthread_cond_wait (&lock
->waiting_writers
, &lock
->lock
);
184 lock
->waiting_writers_count
--;
185 pthread_mutex_unlock (&lock
->lock
);
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
)
199 err
= pthread_mutex_lock (&lock
->lock
);
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
);
213 return pthread_mutex_unlock (&lock
->lock
);
217 pthread_rwlock_trywrlock (pthread_rwlock_t
*lock
)
221 err
= pthread_mutex_lock (&lock
->lock
);
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
);
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
)
241 err
= pthread_mutex_lock (&lock
->lock
);
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
252 err
= pthread_cond_timedwait (&lock
->waiting_readers
, &lock
->lock
,
256 pthread_mutex_unlock (&lock
->lock
);
261 return pthread_mutex_unlock (&lock
->lock
);
265 pthread_rwlock_timedwrlock (pthread_rwlock_t
*lock
,
266 const struct timespec
*abstime
)
270 err
= pthread_mutex_lock (&lock
->lock
);
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
278 lock
->waiting_writers_count
++;
279 err
= pthread_cond_timedwait (&lock
->waiting_writers
, &lock
->lock
,
283 lock
->waiting_writers_count
--;
284 pthread_mutex_unlock (&lock
->lock
);
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
)
298 err
= pthread_mutex_lock (&lock
->lock
);
301 if (lock
->runcount
< 0)
303 /* Drop a writer lock. */
304 if (!(lock
->runcount
== -1))
306 pthread_mutex_unlock (&lock
->lock
);
313 /* Drop a reader lock. */
314 if (!(lock
->runcount
> 0))
316 pthread_mutex_unlock (&lock
->lock
);
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
);
331 pthread_mutex_unlock (&lock
->lock
);
337 /* Wake up all waiting readers. */
338 err
= pthread_cond_broadcast (&lock
->waiting_readers
);
341 pthread_mutex_unlock (&lock
->lock
);
346 return pthread_mutex_unlock (&lock
->lock
);
350 pthread_rwlock_destroy (pthread_rwlock_t
*lock
)
354 err
= pthread_mutex_destroy (&lock
->lock
);
357 err
= pthread_cond_destroy (&lock
->waiting_readers
);
360 err
= pthread_cond_destroy (&lock
->waiting_writers
);
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. */
376 struct timeval currtime
;
377 unsigned long remaining
;
379 err
= pthread_rwlock_tryrdlock (lock
);
383 gettimeofday (&currtime
, NULL
);
385 if (currtime
.tv_sec
> abstime
->tv_sec
)
389 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
390 remaining
= seconds
* 1000000000;
391 if (remaining
/ 1000000000 != seconds
) /* overflow? */
392 remaining
= ULONG_MAX
;
396 abstime
->tv_nsec
- currtime
.tv_usec
* 1000;
397 if (nanoseconds
>= 0)
399 remaining
+= nanoseconds
;
400 if (remaining
< nanoseconds
) /* overflow? */
401 remaining
= ULONG_MAX
;
405 if (remaining
>= - nanoseconds
)
406 remaining
-= (- nanoseconds
);
416 struct timespec duration
=
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. */
433 struct timeval currtime
;
434 unsigned long remaining
;
436 err
= pthread_rwlock_trywrlock (lock
);
440 gettimeofday (&currtime
, NULL
);
442 if (currtime
.tv_sec
> abstime
->tv_sec
)
446 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
447 remaining
= seconds
* 1000000000;
448 if (remaining
/ 1000000000 != seconds
) /* overflow? */
449 remaining
= ULONG_MAX
;
453 abstime
->tv_nsec
- currtime
.tv_usec
* 1000;
454 if (nanoseconds
>= 0)
456 remaining
+= nanoseconds
;
457 if (remaining
< nanoseconds
) /* overflow? */
458 remaining
= ULONG_MAX
;
462 if (remaining
>= - nanoseconds
)
463 remaining
-= (- nanoseconds
);
473 struct timespec duration
=
476 .tv_nsec
= MIN (1000000, remaining
)
478 nanosleep (&duration
, NULL
);
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
)
499 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
508 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
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
)
555 pthread_rwlock_destroy (pthread_rwlock_t
*lock
)