1 /* POSIX read-write locks.
2 Copyright (C) 2019-2020 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)
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. */
24 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
25 # include "windows-timedrwlock.h"
29 # include <sys/time.h>
33 #if ((defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS) || !HAVE_PTHREAD_H
36 pthread_rwlockattr_init (pthread_rwlockattr_t
*attr
)
43 pthread_rwlockattr_destroy (pthread_rwlockattr_t
*attr _GL_UNUSED
)
50 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
51 /* Use Windows threads. */
54 pthread_rwlock_init (pthread_rwlock_t
*lock
,
55 const pthread_rwlockattr_t
*attr _GL_UNUSED
)
57 glwthread_timedrwlock_init (lock
);
62 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
64 return glwthread_timedrwlock_rdlock (lock
);
68 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
70 return glwthread_timedrwlock_wrlock (lock
);
74 pthread_rwlock_tryrdlock (pthread_rwlock_t
*lock
)
76 return glwthread_timedrwlock_tryrdlock (lock
);
80 pthread_rwlock_trywrlock (pthread_rwlock_t
*lock
)
82 return glwthread_timedrwlock_trywrlock (lock
);
86 pthread_rwlock_timedrdlock (pthread_rwlock_t
*lock
,
87 const struct timespec
*abstime
)
89 return glwthread_timedrwlock_timedrdlock (lock
, abstime
);
93 pthread_rwlock_timedwrlock (pthread_rwlock_t
*lock
,
94 const struct timespec
*abstime
)
96 return glwthread_timedrwlock_timedwrlock (lock
, abstime
);
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
);
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
)
122 err
= pthread_mutex_init (&lock
->lock
, NULL
);
125 err
= pthread_cond_init (&lock
->waiting_readers
, NULL
);
128 err
= pthread_cond_init (&lock
->waiting_writers
, NULL
);
131 lock
->waiting_writers_count
= 0;
137 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
141 err
= pthread_mutex_lock (&lock
->lock
);
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
152 err
= pthread_cond_wait (&lock
->waiting_readers
, &lock
->lock
);
155 pthread_mutex_unlock (&lock
->lock
);
160 return pthread_mutex_unlock (&lock
->lock
);
164 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
168 err
= pthread_mutex_lock (&lock
->lock
);
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
176 lock
->waiting_writers_count
++;
177 err
= pthread_cond_wait (&lock
->waiting_writers
, &lock
->lock
);
180 lock
->waiting_writers_count
--;
181 pthread_mutex_unlock (&lock
->lock
);
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
)
195 err
= pthread_mutex_lock (&lock
->lock
);
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
);
209 return pthread_mutex_unlock (&lock
->lock
);
213 pthread_rwlock_trywrlock (pthread_rwlock_t
*lock
)
217 err
= pthread_mutex_lock (&lock
->lock
);
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
);
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
)
237 err
= pthread_mutex_lock (&lock
->lock
);
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
248 err
= pthread_cond_timedwait (&lock
->waiting_readers
, &lock
->lock
,
252 pthread_mutex_unlock (&lock
->lock
);
257 return pthread_mutex_unlock (&lock
->lock
);
261 pthread_rwlock_timedwrlock (pthread_rwlock_t
*lock
,
262 const struct timespec
*abstime
)
266 err
= pthread_mutex_lock (&lock
->lock
);
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
274 lock
->waiting_writers_count
++;
275 err
= pthread_cond_timedwait (&lock
->waiting_writers
, &lock
->lock
,
279 lock
->waiting_writers_count
--;
280 pthread_mutex_unlock (&lock
->lock
);
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
)
294 err
= pthread_mutex_lock (&lock
->lock
);
297 if (lock
->runcount
< 0)
299 /* Drop a writer lock. */
300 if (!(lock
->runcount
== -1))
302 pthread_mutex_unlock (&lock
->lock
);
309 /* Drop a reader lock. */
310 if (!(lock
->runcount
> 0))
312 pthread_mutex_unlock (&lock
->lock
);
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
);
327 pthread_mutex_unlock (&lock
->lock
);
333 /* Wake up all waiting readers. */
334 err
= pthread_cond_broadcast (&lock
->waiting_readers
);
337 pthread_mutex_unlock (&lock
->lock
);
342 return pthread_mutex_unlock (&lock
->lock
);
346 pthread_rwlock_destroy (pthread_rwlock_t
*lock
)
350 err
= pthread_mutex_destroy (&lock
->lock
);
353 err
= pthread_cond_destroy (&lock
->waiting_readers
);
356 err
= pthread_cond_destroy (&lock
->waiting_writers
);
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. */
372 struct timeval currtime
;
373 unsigned long remaining
;
374 struct timespec duration
;
376 err
= pthread_rwlock_tryrdlock (lock
);
380 gettimeofday (&currtime
, NULL
);
382 if (currtime
.tv_sec
> abstime
->tv_sec
)
386 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
387 remaining
= seconds
* 1000000000;
388 if (remaining
/ 1000000000 != seconds
) /* overflow? */
389 remaining
= ULONG_MAX
;
393 abstime
->tv_nsec
- currtime
.tv_usec
* 1000;
394 if (nanoseconds
>= 0)
396 remaining
+= nanoseconds
;
397 if (remaining
< nanoseconds
) /* overflow? */
398 remaining
= ULONG_MAX
;
402 if (remaining
>= - nanoseconds
)
403 remaining
-= (- nanoseconds
);
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. */
429 struct timeval currtime
;
430 unsigned long remaining
;
431 struct timespec duration
;
433 err
= pthread_rwlock_trywrlock (lock
);
437 gettimeofday (&currtime
, NULL
);
439 if (currtime
.tv_sec
> abstime
->tv_sec
)
443 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
444 remaining
= seconds
* 1000000000;
445 if (remaining
/ 1000000000 != seconds
) /* overflow? */
446 remaining
= ULONG_MAX
;
450 abstime
->tv_nsec
- currtime
.tv_usec
* 1000;
451 if (nanoseconds
>= 0)
453 remaining
+= nanoseconds
;
454 if (remaining
< nanoseconds
) /* overflow? */
455 remaining
= ULONG_MAX
;
459 if (remaining
>= - nanoseconds
)
460 remaining
-= (- nanoseconds
);
471 duration
.tv_nsec
= 1000000;
472 if (duration
.tv_nsec
> remaining
)
473 duration
.tv_nsec
= remaining
;
474 nanosleep (&duration
, NULL
);
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
)
495 pthread_rwlock_rdlock (pthread_rwlock_t
*lock
)
504 pthread_rwlock_wrlock (pthread_rwlock_t
*lock
)
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
)
551 pthread_rwlock_destroy (pthread_rwlock_t
*lock
)