Coverage Report

Created: 2026-06-15 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}