Coverage Report

Created: 2025-08-26 06:58

/src/bind9/lib/isc/rwlock.c
Line
Count
Source (jump to first uncovered line)
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.h"
66
67
static atomic_uint_fast16_t isc__crwlock_workers = 128;
68
69
2.05k
#define ISC_RWLOCK_UNLOCKED false
70
0
#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
static void
86
0
read_indicator_arrive(isc_rwlock_t *rwl) {
87
0
  (void)atomic_fetch_add_release(&rwl->readers_ingress, 1);
88
0
}
89
90
static void
91
0
read_indicator_depart(isc_rwlock_t *rwl) {
92
0
  (void)atomic_fetch_add_release(&rwl->readers_egress, 1);
93
0
}
94
95
static bool
96
0
read_indicator_isempty(isc_rwlock_t *rwl) {
97
0
  return atomic_load_acquire(&rwl->readers_egress) ==
98
0
         atomic_load_acquire(&rwl->readers_ingress);
99
0
}
100
101
static void
102
0
writers_barrier_raise(isc_rwlock_t *rwl) {
103
0
  (void)atomic_fetch_add_release(&rwl->writers_barrier, 1);
104
0
}
105
106
static void
107
0
writers_barrier_lower(isc_rwlock_t *rwl) {
108
0
  (void)atomic_fetch_sub_release(&rwl->writers_barrier, 1);
109
0
}
110
111
static bool
112
0
writers_barrier_israised(isc_rwlock_t *rwl) {
113
0
  return atomic_load_acquire(&rwl->writers_barrier) > 0;
114
0
}
115
116
static bool
117
0
writers_lock_islocked(isc_rwlock_t *rwl) {
118
0
  return atomic_load_acquire(&rwl->writers_lock) == ISC_RWLOCK_LOCKED;
119
0
}
120
121
static bool
122
0
writers_lock_acquire(isc_rwlock_t *rwl) {
123
0
  return atomic_compare_exchange_weak_acq_rel(
124
0
    &rwl->writers_lock, &(bool){ ISC_RWLOCK_UNLOCKED },
125
0
    ISC_RWLOCK_LOCKED);
126
0
}
127
128
static void
129
0
writers_lock_release(isc_rwlock_t *rwl) {
130
0
  REQUIRE(atomic_compare_exchange_strong_acq_rel(
131
0
    &rwl->writers_lock, &(bool){ ISC_RWLOCK_LOCKED },
132
0
    ISC_RWLOCK_UNLOCKED));
133
0
}
134
135
0
#define ran_out_of_patience(cnt) (cnt >= RWLOCK_MAX_READER_PATIENCE)
136
137
void
138
0
isc_rwlock_rdlock(isc_rwlock_t *rwl) {
139
0
  uint32_t cnt = 0;
140
0
  bool barrier_raised = false;
141
142
0
  LIBISC_RWLOCK_RDLOCK_REQ(rwl);
143
144
0
  while (true) {
145
0
    read_indicator_arrive(rwl);
146
0
    if (!writers_lock_islocked(rwl)) {
147
      /* Acquired lock in read-only mode */
148
0
      break;
149
0
    }
150
151
    /* Writer has acquired the lock, must reset to 0 and wait */
152
0
    read_indicator_depart(rwl);
153
154
0
    while (writers_lock_islocked(rwl)) {
155
0
      isc_pause();
156
0
      if (ran_out_of_patience(cnt++) && !barrier_raised) {
157
0
        writers_barrier_raise(rwl);
158
0
        barrier_raised = true;
159
0
      }
160
0
    }
161
0
  }
162
0
  if (barrier_raised) {
163
0
    writers_barrier_lower(rwl);
164
0
  }
165
166
0
  LIBISC_RWLOCK_RDLOCK_ACQ(rwl);
167
0
}
168
169
isc_result_t
170
0
isc_rwlock_tryrdlock(isc_rwlock_t *rwl) {
171
0
  read_indicator_arrive(rwl);
172
0
  if (writers_lock_islocked(rwl)) {
173
    /* Writer has acquired the lock, release the read lock */
174
0
    read_indicator_depart(rwl);
175
176
0
    LIBISC_RWLOCK_TRYRDLOCK(rwl, ISC_R_LOCKBUSY);
177
0
    return ISC_R_LOCKBUSY;
178
0
  }
179
180
  /* Acquired lock in read-only mode */
181
0
  LIBISC_RWLOCK_TRYRDLOCK(rwl, ISC_R_SUCCESS);
182
0
  return ISC_R_SUCCESS;
183
0
}
184
185
void
186
0
isc_rwlock_rdunlock(isc_rwlock_t *rwl) {
187
0
  read_indicator_depart(rwl);
188
0
  LIBISC_RWLOCK_RDUNLOCK(rwl);
189
0
}
190
191
isc_result_t
192
0
isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
193
  /* Write Barriers has been raised */
194
0
  if (writers_barrier_israised(rwl)) {
195
0
    LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY);
196
0
    return ISC_R_LOCKBUSY;
197
0
  }
198
199
  /* Try to acquire the write-lock */
200
0
  if (!writers_lock_acquire(rwl)) {
201
0
    LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY);
202
0
    return ISC_R_LOCKBUSY;
203
0
  }
204
205
  /* Unlock the read-lock */
206
0
  read_indicator_depart(rwl);
207
208
0
  if (!read_indicator_isempty(rwl)) {
209
    /* Re-acquire the read-lock back */
210
0
    read_indicator_arrive(rwl);
211
212
    /* Unlock the write-lock */
213
0
    writers_lock_release(rwl);
214
0
    LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_LOCKBUSY);
215
0
    return ISC_R_LOCKBUSY;
216
0
  }
217
0
  LIBISC_RWLOCK_TRYUPGRADE(rwl, ISC_R_SUCCESS);
218
0
  return ISC_R_SUCCESS;
219
0
}
220
221
static void
222
0
read_indicator_wait_until_empty(isc_rwlock_t *rwl) {
223
  /* Write-lock was acquired, now wait for running Readers to finish */
224
0
  while (true) {
225
0
    if (read_indicator_isempty(rwl)) {
226
0
      break;
227
0
    }
228
0
    isc_pause();
229
0
  }
230
0
}
231
232
void
233
0
isc_rwlock_wrlock(isc_rwlock_t *rwl) {
234
0
  LIBISC_RWLOCK_WRLOCK_REQ(rwl);
235
236
  /* Write Barriers has been raised, wait */
237
0
  while (writers_barrier_israised(rwl)) {
238
0
    isc_pause();
239
0
  }
240
241
  /* Try to acquire the write-lock */
242
0
  while (!writers_lock_acquire(rwl)) {
243
0
    isc_pause();
244
0
  }
245
246
0
  read_indicator_wait_until_empty(rwl);
247
248
0
  LIBISC_RWLOCK_WRLOCK_ACQ(rwl);
249
0
}
250
251
void
252
0
isc_rwlock_wrunlock(isc_rwlock_t *rwl) {
253
0
  writers_lock_release(rwl);
254
0
  LIBISC_RWLOCK_WRUNLOCK(rwl);
255
0
}
256
257
isc_result_t
258
0
isc_rwlock_trywrlock(isc_rwlock_t *rwl) {
259
  /* Write Barriers has been raised */
260
0
  if (writers_barrier_israised(rwl)) {
261
0
    LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY);
262
0
    return ISC_R_LOCKBUSY;
263
0
  }
264
265
  /* Try to acquire the write-lock */
266
0
  if (!writers_lock_acquire(rwl)) {
267
0
    LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY);
268
0
    return ISC_R_LOCKBUSY;
269
0
  }
270
271
0
  if (!read_indicator_isempty(rwl)) {
272
    /* Unlock the write-lock */
273
0
    writers_lock_release(rwl);
274
275
0
    LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_LOCKBUSY);
276
0
    return ISC_R_LOCKBUSY;
277
0
  }
278
279
0
  LIBISC_RWLOCK_TRYWRLOCK(rwl, ISC_R_SUCCESS);
280
0
  return ISC_R_SUCCESS;
281
0
}
282
283
void
284
0
isc_rwlock_downgrade(isc_rwlock_t *rwl) {
285
0
  read_indicator_arrive(rwl);
286
287
0
  writers_lock_release(rwl);
288
289
0
  LIBISC_RWLOCK_DOWNGRADE(rwl);
290
0
}
291
292
void
293
2.05k
isc_rwlock_init(isc_rwlock_t *rwl) {
294
2.05k
  REQUIRE(rwl != NULL);
295
296
2.05k
  atomic_init(&rwl->writers_lock, ISC_RWLOCK_UNLOCKED);
297
2.05k
  atomic_init(&rwl->writers_barrier, 0);
298
2.05k
  atomic_init(&rwl->readers_ingress, 0);
299
2.05k
  atomic_init(&rwl->readers_egress, 0);
300
2.05k
  LIBISC_RWLOCK_INIT(rwl);
301
2.05k
}
302
303
void
304
0
isc_rwlock_destroy(isc_rwlock_t *rwl) {
305
0
  LIBISC_RWLOCK_DESTROY(rwl);
306
  /* Check whether write lock has been unlocked */
307
0
  REQUIRE(atomic_load(&rwl->writers_lock) == ISC_RWLOCK_UNLOCKED);
308
0
  REQUIRE(read_indicator_isempty(rwl));
309
0
}
310
311
void
312
0
isc_rwlock_setworkers(uint16_t workers) {
313
0
  atomic_store(&isc__crwlock_workers, workers);
314
0
}