/src/bind9/lib/isc/rwlock.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | /* |
15 | | * Modified C-RW-WP Implementation from NUMA-Aware Reader-Writer Locks paper: |
16 | | * http://dl.acm.org/citation.cfm?id=2442532 |
17 | | * |
18 | | * This work is based on C++ code available from |
19 | | * https://github.com/pramalhe/ConcurrencyFreaks/ |
20 | | * |
21 | | * Copyright (c) 2014-2016, Pedro Ramalhete, Andreia Correia |
22 | | * All rights reserved. |
23 | | * |
24 | | * Redistribution and use in source and binary forms, with or without |
25 | | * modification, are permitted provided that the following conditions are met: |
26 | | * |
27 | | * * Redistributions of source code must retain the above copyright |
28 | | * notice, this list of conditions and the following disclaimer. |
29 | | * * Redistributions in binary form must reproduce the above copyright |
30 | | * notice, this list of conditions and the following disclaimer in the |
31 | | * documentation and/or other materials provided with the distribution. |
32 | | * * Neither the name of Concurrency Freaks nor the |
33 | | * names of its contributors may be used to endorse or promote products |
34 | | * derived from this software without specific prior written permission. |
35 | | * |
36 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
37 | | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
38 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
39 | | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> |
40 | | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
41 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
42 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
43 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
44 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
45 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
46 | | * THE POSSIBILITY OF SUCH DAMAGE. |
47 | | */ |
48 | | |
49 | | /*! \file */ |
50 | | |
51 | | #include <inttypes.h> |
52 | | #include <stdbool.h> |
53 | | #include <stddef.h> |
54 | | #include <stdlib.h> |
55 | | #include <unistd.h> |
56 | | |
57 | | #include <isc/atomic.h> |
58 | | #include <isc/hash.h> |
59 | | #include <isc/pause.h> |
60 | | #include <isc/rwlock.h> |
61 | | #include <isc/thread.h> |
62 | | #include <isc/tid.h> |
63 | | #include <isc/util.h> |
64 | | |
65 | | #include "probes-isc.h" |
66 | | |
67 | | static atomic_uint_fast16_t isc__crwlock_workers = 128; |
68 | | |
69 | 20.0M | #define ISC_RWLOCK_UNLOCKED false |
70 | 19.9M | #define ISC_RWLOCK_LOCKED true |
71 | | |
72 | | /* |
73 | | * See https://csce.ucmss.com/cr/books/2017/LFS/CSREA2017/FCS3701.pdf for |
74 | | * guidance on patience level |
75 | | */ |
76 | | #ifndef RWLOCK_MAX_READER_PATIENCE |
77 | 0 | #define RWLOCK_MAX_READER_PATIENCE 500 |
78 | | #endif /* ifndef RWLOCK_MAX_READER_PATIENCE */ |
79 | | |
80 | | static void |
81 | | read_indicator_wait_until_empty(isc_rwlock_t *rwl); |
82 | | |
83 | | #include <stdio.h> |
84 | | |
85 | | /* |
86 | | * The reader/writer handshake below is the store-buffer pattern: each side |
87 | | * publishes a store (the reader into readers_ingress, the writer into |
88 | | * writers_lock) and then loads the other side's state. These are independent |
89 | | * atomic objects, so acquire/release ordering does not order the publish store |
90 | | * against the later check load, and a reader and a writer could both observe |
91 | | * the other's slot as free and enter their critical sections at once. The |
92 | | * operations therefore use memory_order_seq_cst, not acquire/release: the |
93 | | * single total order over the seq_cst operations is what forbids that overlap. |
94 | | * Do not weaken these to acquire/release -- doing so reintroduces #6060. |
95 | | */ |
96 | | static void |
97 | 36.8k | read_indicator_arrive(isc_rwlock_t *rwl) { |
98 | 36.8k | (void)atomic_fetch_add_explicit(&rwl->readers_ingress, 1, |
99 | 36.8k | memory_order_seq_cst); |
100 | 36.8k | } |
101 | | |
102 | | static void |
103 | 36.8k | read_indicator_depart(isc_rwlock_t *rwl) { |
104 | 36.8k | (void)atomic_fetch_add_explicit(&rwl->readers_egress, 1, |
105 | 36.8k | memory_order_seq_cst); |
106 | 36.8k | } |
107 | | |
108 | | static bool |
109 | 19.9M | read_indicator_isempty(isc_rwlock_t *rwl) { |
110 | 19.9M | return atomic_load_explicit(&rwl->readers_egress, |
111 | 19.9M | memory_order_seq_cst) == |
112 | 19.9M | atomic_load_explicit(&rwl->readers_ingress, |
113 | 19.9M | memory_order_seq_cst); |
114 | 19.9M | } |
115 | | |
116 | | static void |
117 | 0 | writers_barrier_raise(isc_rwlock_t *rwl) { |
118 | 0 | (void)atomic_fetch_add_explicit(&rwl->writers_barrier, 1, |
119 | 0 | memory_order_seq_cst); |
120 | 0 | } |
121 | | |
122 | | static void |
123 | 0 | writers_barrier_lower(isc_rwlock_t *rwl) { |
124 | 0 | (void)atomic_fetch_sub_explicit(&rwl->writers_barrier, 1, |
125 | 0 | memory_order_seq_cst); |
126 | 0 | } |
127 | | |
128 | | static bool |
129 | 19.9M | writers_barrier_israised(isc_rwlock_t *rwl) { |
130 | 19.9M | return atomic_load_explicit(&rwl->writers_barrier, |
131 | 19.9M | memory_order_seq_cst) > 0; |
132 | 19.9M | } |
133 | | |
134 | | static bool |
135 | 36.8k | writers_lock_islocked(isc_rwlock_t *rwl) { |
136 | 36.8k | return atomic_load_explicit(&rwl->writers_lock, memory_order_seq_cst) == |
137 | 36.8k | ISC_RWLOCK_LOCKED; |
138 | 36.8k | } |
139 | | |
140 | | static bool |
141 | 19.9M | writers_lock_acquire(isc_rwlock_t *rwl) { |
142 | 19.9M | return atomic_compare_exchange_weak_explicit( |
143 | 19.9M | &rwl->writers_lock, &(bool){ ISC_RWLOCK_UNLOCKED }, |
144 | 19.9M | ISC_RWLOCK_LOCKED, memory_order_seq_cst, memory_order_seq_cst); |
145 | 19.9M | } |
146 | | |
147 | | static void |
148 | 19.9M | writers_lock_release(isc_rwlock_t *rwl) { |
149 | 19.9M | REQUIRE(atomic_compare_exchange_strong_explicit( |
150 | 19.9M | &rwl->writers_lock, &(bool){ ISC_RWLOCK_LOCKED }, |
151 | 19.9M | ISC_RWLOCK_UNLOCKED, memory_order_seq_cst, |
152 | 19.9M | memory_order_seq_cst)); |
153 | 19.9M | } |
154 | | |
155 | 0 | #define ran_out_of_patience(cnt) (cnt >= RWLOCK_MAX_READER_PATIENCE) |
156 | | |
157 | | void |
158 | 36.8k | isc_rwlock_rdlock(isc_rwlock_t *rwl) { |
159 | 36.8k | uint32_t cnt = 0; |
160 | 36.8k | bool barrier_raised = false; |
161 | | |
162 | 36.8k | LIBISC_RWLOCK_RDLOCK_REQ(rwl); |
163 | | |
164 | 36.8k | while (true) { |
165 | 36.8k | read_indicator_arrive(rwl); |
166 | | |
167 | 36.8k | if (!writers_lock_islocked(rwl)) { |
168 | | /* Acquired lock in read-only mode */ |
169 | 36.8k | break; |
170 | 36.8k | } |
171 | | |
172 | | /* Writer has acquired the lock, must reset to 0 and wait */ |
173 | 0 | read_indicator_depart(rwl); |
174 | |
|
175 | 0 | while (writers_lock_islocked(rwl)) { |
176 | 0 | isc_pause(); |
177 | 0 | if (ran_out_of_patience(cnt++) && !barrier_raised) { |
178 | 0 | writers_barrier_raise(rwl); |
179 | 0 | barrier_raised = true; |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | 36.8k | if (barrier_raised) { |
184 | 0 | writers_barrier_lower(rwl); |
185 | 0 | } |
186 | | |
187 | 36.8k | LIBISC_RWLOCK_RDLOCK_ACQ(rwl); |
188 | 36.8k | } |
189 | | |
190 | | isc_result_t |
191 | 0 | isc_rwlock_tryrdlock(isc_rwlock_t *rwl) { |
192 | 0 | read_indicator_arrive(rwl); |
193 | |
|
194 | 0 | if (writers_lock_islocked(rwl)) { |
195 | | /* Writer has acquired the lock, release the read lock */ |
196 | 0 | read_indicator_depart(rwl); |
197 | |
|
198 | 0 | LIBISC_RWLOCK_TRYRDLOCK(rwl, ISC_R_LOCKBUSY); |
199 | 0 | return ISC_R_LOCKBUSY; |
200 | 0 | } |
201 | | |
202 | | /* Acquired lock in read-only mode */ |
203 | 0 | LIBISC_RWLOCK_TRYRDLOCK(rwl, ISC_R_SUCCESS); |
204 | 0 | return ISC_R_SUCCESS; |
205 | 0 | } |
206 | | |
207 | | void |
208 | 36.8k | isc_rwlock_rdunlock(isc_rwlock_t *rwl) { |
209 | 36.8k | read_indicator_depart(rwl); |
210 | 36.8k | LIBISC_RWLOCK_RDUNLOCK(rwl); |
211 | 36.8k | } |
212 | | |
213 | | isc_result_t |
214 | 0 | isc_rwlock_tryupgrade(isc_rwlock_t *rwl) { |
215 | | /* Write Barriers has been raised */ |
216 | 0 | if (writers_barrier_israised(rwl)) { |
217 | 0 | LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY); |
218 | 0 | return ISC_R_LOCKBUSY; |
219 | 0 | } |
220 | | |
221 | | /* Try to acquire the write-lock */ |
222 | 0 | if (!writers_lock_acquire(rwl)) { |
223 | 0 | LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY); |
224 | 0 | return ISC_R_LOCKBUSY; |
225 | 0 | } |
226 | | |
227 | | /* Unlock the read-lock */ |
228 | 0 | read_indicator_depart(rwl); |
229 | |
|
230 | 0 | if (!read_indicator_isempty(rwl)) { |
231 | | /* Re-acquire the read-lock back */ |
232 | 0 | read_indicator_arrive(rwl); |
233 | | |
234 | | /* Unlock the write-lock */ |
235 | 0 | writers_lock_release(rwl); |
236 | 0 | LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY); |
237 | 0 | return ISC_R_LOCKBUSY; |
238 | 0 | } |
239 | 0 | LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_SUCCESS); |
240 | 0 | return ISC_R_SUCCESS; |
241 | 0 | } |
242 | | |
243 | | static void |
244 | 19.9M | read_indicator_wait_until_empty(isc_rwlock_t *rwl) { |
245 | | /* Write-lock was acquired, now wait for running Readers to finish */ |
246 | 19.9M | while (true) { |
247 | 19.9M | if (read_indicator_isempty(rwl)) { |
248 | 19.9M | break; |
249 | 19.9M | } |
250 | 0 | isc_pause(); |
251 | 0 | } |
252 | 19.9M | } |
253 | | |
254 | | void |
255 | 19.9M | isc_rwlock_wrlock(isc_rwlock_t *rwl) { |
256 | 19.9M | LIBISC_RWLOCK_WRLOCK_REQ(rwl); |
257 | | |
258 | | /* Write Barriers has been raised, wait */ |
259 | 19.9M | while (writers_barrier_israised(rwl)) { |
260 | 0 | isc_pause(); |
261 | 0 | } |
262 | | |
263 | | /* Try to acquire the write-lock */ |
264 | 19.9M | while (!writers_lock_acquire(rwl)) { |
265 | 0 | isc_pause(); |
266 | 0 | } |
267 | | |
268 | 19.9M | read_indicator_wait_until_empty(rwl); |
269 | | |
270 | 19.9M | LIBISC_RWLOCK_WRLOCK_ACQ(rwl); |
271 | 19.9M | } |
272 | | |
273 | | void |
274 | 19.9M | isc_rwlock_wrunlock(isc_rwlock_t *rwl) { |
275 | 19.9M | writers_lock_release(rwl); |
276 | 19.9M | LIBISC_RWLOCK_WRUNLOCK(rwl); |
277 | 19.9M | } |
278 | | |
279 | | isc_result_t |
280 | 0 | isc_rwlock_trywrlock(isc_rwlock_t *rwl) { |
281 | | /* Write Barriers has been raised */ |
282 | 0 | if (writers_barrier_israised(rwl)) { |
283 | 0 | LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY); |
284 | 0 | return ISC_R_LOCKBUSY; |
285 | 0 | } |
286 | | |
287 | | /* Try to acquire the write-lock */ |
288 | 0 | if (!writers_lock_acquire(rwl)) { |
289 | 0 | LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY); |
290 | 0 | return ISC_R_LOCKBUSY; |
291 | 0 | } |
292 | | |
293 | 0 | if (!read_indicator_isempty(rwl)) { |
294 | | /* Unlock the write-lock */ |
295 | 0 | writers_lock_release(rwl); |
296 | |
|
297 | 0 | LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY); |
298 | 0 | return ISC_R_LOCKBUSY; |
299 | 0 | } |
300 | | |
301 | 0 | LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_SUCCESS); |
302 | 0 | return ISC_R_SUCCESS; |
303 | 0 | } |
304 | | |
305 | | void |
306 | 0 | isc_rwlock_downgrade(isc_rwlock_t *rwl) { |
307 | 0 | read_indicator_arrive(rwl); |
308 | |
|
309 | 0 | writers_lock_release(rwl); |
310 | |
|
311 | 0 | LIBISC_RWLOCK_DOWNGRADE(rwl); |
312 | 0 | } |
313 | | |
314 | | void |
315 | 57.8k | isc_rwlock_init(isc_rwlock_t *rwl) { |
316 | 57.8k | REQUIRE(rwl != NULL); |
317 | | |
318 | 57.8k | atomic_init(&rwl->writers_lock, ISC_RWLOCK_UNLOCKED); |
319 | 57.8k | atomic_init(&rwl->writers_barrier, 0); |
320 | 57.8k | atomic_init(&rwl->readers_ingress, 0); |
321 | 57.8k | atomic_init(&rwl->readers_egress, 0); |
322 | 57.8k | LIBISC_RWLOCK_INIT(rwl); |
323 | 57.8k | } |
324 | | |
325 | | void |
326 | 35.2k | isc_rwlock_destroy(isc_rwlock_t *rwl) { |
327 | 35.2k | LIBISC_RWLOCK_DESTROY(rwl); |
328 | | /* Check whether write lock has been unlocked */ |
329 | 35.2k | REQUIRE(atomic_load(&rwl->writers_lock) == ISC_RWLOCK_UNLOCKED); |
330 | 35.2k | REQUIRE(read_indicator_isempty(rwl)); |
331 | 35.2k | } |
332 | | |
333 | | void |
334 | 0 | isc_rwlock_setworkers(uint16_t workers) { |
335 | | atomic_store(&isc__crwlock_workers, workers); |
336 | 0 | } |