1/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include "pthreadP.h"
21#include <atomic.h>
22#include <stdbool.h>
23#include "pthread_rwlock_common.c"
24
25
26/* See pthread_rwlock_common.c for an overview. */
27int
28__pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
29{
30 /* For tryrdlock, we could speculate that we will succeed and go ahead and
31 register as a reader. However, if we misspeculate, we have to do the
32 same steps as a timed-out rdlock, which will increase contention.
33 Therefore, there is a trade-off between being able to use a combinable
34 read-modify-write operation and a CAS loop as used below; we pick the
35 latter because it simplifies the code, and should perform better when
36 tryrdlock is used in cases where writers are infrequent.
37 Because POSIX does not require a failed trylock to "synchronize memory",
38 relaxed MO is sufficient here and on the failure path of the CAS
39 below. */
40 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
41 unsigned int rnew;
42 do
43 {
44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
45 {
46 /* If we are in a read phase, try to acquire unless there is a
47 primary writer and we prefer writers and there will be no
48 recursive read locks. */
49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
50 && (rwlock->__data.__flags
51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
52 return EBUSY;
53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
54 }
55 else
56 {
57 /* If there is a writer that has acquired the lock and we are in
58 a write phase, fail. */
59 if ((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
60 return EBUSY;
61 else
62 {
63 /* If we do not care about potentially waiting writers, just
64 try to acquire. */
65 rnew = (r + (1 << PTHREAD_RWLOCK_READER_SHIFT))
66 ^ PTHREAD_RWLOCK_WRPHASE;
67 }
68 }
69 /* If we could have caused an overflow or take effect during an
70 overflow, we just can / need to return EAGAIN. There is no need to
71 have actually modified the number of readers because we could have
72 done that and cleaned up immediately. */
73 if (rnew >= PTHREAD_RWLOCK_READER_OVERFLOW)
74 return EAGAIN;
75 }
76 /* If the CAS fails, we retry; this prevents that tryrdlock fails spuriously
77 (i.e., fails to acquire the lock although there is no writer), which is
78 fine for C++14 but not currently allowed by POSIX.
79 However, because tryrdlock must not appear to block, we should avoid
80 starving this CAS loop due to constant changes to __readers:
81 While normal rdlock readers that won't be able to acquire will just block
82 (and we expect timeouts on timedrdlock to be longer than one retry of the
83 CAS loop), we can have concurrently failing tryrdlock calls due to
84 readers or writers that acquire and release in the meantime. Using
85 randomized exponential back-off to make a live-lock unlikely should be
86 sufficient.
87 TODO Back-off.
88 Acquire MO so we synchronize with prior writers. */
89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
90 &r, rnew));
91
92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
93 {
94 /* Same as in __pthread_rwlock_rdlock_full:
95 We started the read phase, so we are also responsible for
96 updating the write-phase futex. Relaxed MO is sufficient.
97 Note that there can be no other reader that we have to wake
98 because all other readers will see the read phase started by us
99 (or they will try to start it themselves); if a writer started
100 the read phase, we cannot have started it. Furthermore, we
101 cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
102 overwrite the value set by the most recent writer (or the readers
103 before it in case of explicit hand-over) and we know that there
104 are no waiting readers. */
105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
106 }
107
108 return 0;
109
110
111}
112strong_alias (__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock)
113