• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.43
/pdns/lock.hh
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#pragma once
23
#include <mutex>
24
#include <shared_mutex>
25
#include <stdexcept>
26

27
/*
28
  This file provides several features around locks:
29

30
  - LockGuarded and SharedLockGuarded provide a way to wrap any data structure as
31
  protected by a lock (mutex or shared mutex), while making it immediately clear
32
  which data is protected by that lock, and preventing any access to the data without
33
  holding the lock.
34

35
  For example, to protect a set of integers with a simple mutex:
36

37
  LockGuarded<std::set<int>> d_data;
38

39
  or with a shared mutex instead:
40

41
  SharedLockGuarded<std::set<int>> d_data;
42

43
  Then the only ways to access the data is to call the lock(), read_only_lock() or try_lock() methods
44
  for the simple case, or the read_lock(), write_lock(), try_read_lock() or try_write_lock() for the
45
  shared one.
46
  Doing so will return a "holder" object, which provides access to the protected data, checking that
47
  the lock has really been acquired if needed (try_ cases). The data might be read-only if read_lock(),
48
  try_read_lock() or read_only_lock() was called. Access is provided by dereferencing the holder object
49
  via '*' or '->', allowing a quick-access syntax:
50

51
  return d_data.lock()->size();
52

53
  Or when the lock needs to be kept for a bit longer:
54

55
  {
56
    auto data = d_data.lock();
57
    data->clear();
58
    data->insert(42);
59
  }
60

61
  - ReadWriteLock is a very light wrapper around a std::shared_mutex.
62
  It used to be useful as a RAII wrapper around pthread_rwlock, but since
63
  C++17 we don't actually that, so it's mostly there for historical
64
  reasons.
65

66
  - ReadLock, WriteLock, TryReadLock and TryWriteLock are there as RAII
67
  objects allowing to take a lock and be sure that it will always be unlocked
68
  when we exit the block, even with a unforeseen exception.
69
  They are light wrappers around std::unique_lock and std::shared_lock
70
  since C++17.
71

72
  Note that while the use of a shared mutex might be very efficient when the data
73
  is predominantly concurrently accessed for reading by multiple threads and not
74
  often written to (although if it is almost never updated our StateHolder in
75
  sholder.hh might be a better fit), it is significantly more expensive than
76
  a regular mutex, so that one might be a better choice if the contention is
77
  low. It is wise to start with a regular mutex and actually measure the contention
78
  under load before switching to a shared mutex.
79
 */
80

81
class ReadWriteLock
82
{
83
public:
84
  ReadWriteLock() = default;
1,000✔
85
  ~ReadWriteLock() = default;
86

87
  ReadWriteLock(const ReadWriteLock& rhs) = delete;
88
  ReadWriteLock(ReadWriteLock&& rhs) = delete;
89
  ReadWriteLock& operator=(ReadWriteLock&&) = delete;
90
  ReadWriteLock& operator=(const ReadWriteLock& rhs) = delete;
91

92
  std::shared_mutex& getLock()
93
  {
3,002✔
94
    return d_lock;
3,002✔
95
  }
3,002✔
96

97
private:
98
  std::shared_mutex d_lock;
99
};
100

101
class ReadLock
102
{
103
public:
104
  ReadLock(ReadWriteLock& lock) :
105
    ReadLock(lock.getLock())
106
  {
2,000✔
107
  }
2,000✔
108

109
  ReadLock(ReadWriteLock* lock) :
110
    ReadLock(lock->getLock())
111
  {
×
112
  }
×
113

114
  ~ReadLock() = default;
4,046✔
115
  ReadLock(const ReadLock& rhs) = delete;
116
  ReadLock& operator=(const ReadLock& rhs) = delete;
117
  ReadLock& operator=(ReadLock&&) = delete;
118

119
  ReadLock(ReadLock&& rhs) noexcept :
120
    d_lock(std::move(rhs.d_lock))
121
  {
2,046✔
122
  }
2,046✔
123

124
private:
125
  ReadLock(std::shared_mutex& lock) :
126
    d_lock(lock)
127
  {
2,000✔
128
  }
2,000✔
129

130
  std::shared_lock<std::shared_mutex> d_lock;
131
};
132

133
class WriteLock
134
{
135
public:
136
  WriteLock(ReadWriteLock& lock) :
137
    WriteLock(lock.getLock())
138
  {
1,000✔
139
  }
1,000✔
140

141
  WriteLock(ReadWriteLock* lock) :
142
    WriteLock(lock->getLock())
143
  {
×
144
  }
×
145

146
  ~WriteLock() = default;
2,023✔
147
  WriteLock(const WriteLock& rhs) = delete;
148
  WriteLock& operator=(const WriteLock& rhs) = delete;
149
  WriteLock& operator=(WriteLock&&) = delete;
150

151
  WriteLock(WriteLock&& rhs) noexcept :
152
    d_lock(std::move(rhs.d_lock))
153
  {
1,023✔
154
  }
1,023✔
155

156
private:
157
  WriteLock(std::shared_mutex& lock) :
158
    d_lock(lock)
159
  {
1,000✔
160
  }
1,000✔
161

162
  std::unique_lock<std::shared_mutex> d_lock;
163
};
164

165
class TryReadLock
166
{
167
public:
168
  TryReadLock(ReadWriteLock& lock) :
169
    TryReadLock(lock.getLock())
170
  {
2✔
171
  }
2✔
172

173
  TryReadLock(ReadWriteLock* lock) :
174
    TryReadLock(lock->getLock())
175
  {
×
176
  }
×
177

178
  ~TryReadLock() = default;
2✔
179
  TryReadLock(const TryReadLock& rhs) = delete;
180
  TryReadLock(TryReadLock&&) = delete;
181
  TryReadLock& operator=(const TryReadLock& rhs) = delete;
182
  TryReadLock& operator=(TryReadLock&&) = delete;
183

184
  [[nodiscard]] bool gotIt() const
185
  {
2✔
186
    return d_lock.owns_lock();
2✔
187
  }
2✔
188

189
private:
190
  TryReadLock(std::shared_mutex& lock) :
191
    d_lock(lock, std::try_to_lock)
192
  {
2✔
193
  }
2✔
194

195
  std::shared_lock<std::shared_mutex> d_lock;
196
};
197

198
class TryWriteLock
199
{
200
public:
201
  TryWriteLock(ReadWriteLock& lock) :
202
    TryWriteLock(lock.getLock())
203
  {
×
204
  }
×
205

206
  TryWriteLock(ReadWriteLock* lock) :
207
    TryWriteLock(lock->getLock())
208
  {
×
209
  }
×
210

211
  ~TryWriteLock() = default;
212
  TryWriteLock(const TryWriteLock& rhs) = delete;
213
  TryWriteLock(TryWriteLock&&) = delete;
214
  TryWriteLock& operator=(const TryWriteLock& rhs) = delete;
215
  TryWriteLock& operator=(TryWriteLock&&) = delete;
216

217
  [[nodiscard]] bool gotIt() const
218
  {
×
219
    return d_lock.owns_lock();
×
220
  }
×
221

222
private:
223
  TryWriteLock(std::shared_mutex& lock) :
224
    d_lock(lock, std::try_to_lock)
225
  {
×
226
  }
×
227

228
  std::unique_lock<std::shared_mutex> d_lock;
229
};
230

231
template <typename T>
232
class LockGuardedHolder
233
{
234
public:
235
  explicit LockGuardedHolder(T& value, std::mutex& mutex) :
236
    d_lock(mutex), d_value(value)
237
  {
10,787,124✔
238
  }
10,787,124✔
239

240
  T& operator*() const noexcept
241
  {
13,321,750✔
242
    return d_value;
13,321,750✔
243
  }
13,321,750✔
244

245
  T* operator->() const noexcept
246
  {
2,057,554✔
247
    return &d_value;
2,057,554✔
248
  }
2,057,554✔
249

250
private:
251
  std::lock_guard<std::mutex> d_lock;
252
  T& d_value;
253
};
254

255
template <typename T>
256
class LockGuardedTryHolder
257
{
258
public:
259
  explicit LockGuardedTryHolder(T& value, std::mutex& mutex) :
260
    d_lock(mutex, std::try_to_lock), d_value(value)
261
  {
24,769,640✔
262
  }
24,769,640✔
263

264
  T& operator*() const
265
  {
20,192,779✔
266
    if (!owns_lock()) {
20,192,779!
267
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
268
    }
269
    return d_value;
20,192,779✔
270
  }
20,192,779✔
271

272
  T* operator->() const
273
  {
23,122,752✔
274
    if (!owns_lock()) {
23,122,752!
275
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
276
    }
277
    return &d_value;
23,122,752✔
278
  }
23,122,752✔
279

280
  operator bool() const noexcept
281
  {
282
    return d_lock.owns_lock();
283
  }
284

285
  [[nodiscard]] bool owns_lock() const noexcept
286
  {
68,350,468✔
287
    return d_lock.owns_lock();
68,350,468✔
288
  }
68,350,468✔
289

290
  void lock()
291
  {
5,323✔
292
    d_lock.lock();
5,323✔
293
  }
5,323✔
294

295
private:
296
  std::unique_lock<std::mutex> d_lock;
297
  T& d_value;
298
};
299

300
template <typename T>
301
class LockGuarded
302
{
303
public:
304
  explicit LockGuarded(const T& value) :
305
    d_value(value)
306
  {
307
  }
308

309
  explicit LockGuarded(T&& value) :
310
    d_value(std::move(value))
311
  {
5,442✔
312
  }
5,442✔
313

314
  explicit LockGuarded() = default;
1,100,531✔
315

316
  LockGuardedTryHolder<T> try_lock()
317
  {
25,151,228✔
318
    return LockGuardedTryHolder<T>(d_value, d_mutex);
25,151,228✔
319
  }
25,151,228✔
320

321
  LockGuardedHolder<T> lock()
322
  {
10,784,241✔
323
    return LockGuardedHolder<T>(d_value, d_mutex);
10,784,241✔
324
  }
10,784,241✔
325

326
  LockGuardedHolder<const T> read_only_lock()
327
  {
22✔
328
    return LockGuardedHolder<const T>(d_value, d_mutex);
22✔
329
  }
22✔
330

331
private:
332
  std::mutex d_mutex;
333
  T d_value;
334
};
335

336
template <typename T>
337
class RecursiveLockGuardedHolder
338
{
339
public:
340
  explicit RecursiveLockGuardedHolder(T& value, std::recursive_mutex& mutex) :
341
    d_lock(mutex), d_value(value)
342
  {
35,108✔
343
  }
35,108✔
344

345
  T& operator*() const noexcept
346
  {
1,331✔
347
    return d_value;
1,331✔
348
  }
1,331✔
349

350
  T* operator->() const noexcept
351
  {
2,875✔
352
    return &d_value;
2,875✔
353
  }
2,875✔
354

355
private:
356
  std::lock_guard<std::recursive_mutex> d_lock;
357
  T& d_value;
358
};
359

360
template <typename T>
361
class RecursiveLockGuardedTryHolder
362
{
363
public:
364
  explicit RecursiveLockGuardedTryHolder(T& value, std::recursive_mutex& mutex) :
365
    d_lock(mutex, std::try_to_lock), d_value(value)
366
  {
367
  }
368

369
  T& operator*() const
370
  {
371
    if (!owns_lock()) {
372
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
373
    }
374
    return d_value;
375
  }
376

377
  T* operator->() const
378
  {
379
    if (!owns_lock()) {
380
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
381
    }
382
    return &d_value;
383
  }
384

385
  operator bool() const noexcept
386
  {
387
    return d_lock.owns_lock();
388
  }
389

390
  [[nodiscard]] bool owns_lock() const noexcept
391
  {
392
    return d_lock.owns_lock();
393
  }
394

395
  void lock()
396
  {
397
    d_lock.lock();
398
  }
399

400
private:
401
  std::unique_lock<std::recursive_mutex> d_lock;
402
  T& d_value;
403
};
404

405
template <typename T>
406
class RecursiveLockGuarded
407
{
408
public:
409
  explicit RecursiveLockGuarded(const T& value) :
410
    d_value(value)
411
  {
412
  }
413

414
  explicit RecursiveLockGuarded(T&& value) :
415
    d_value(std::move(value))
416
  {
651✔
417
  }
651✔
418

419
  explicit RecursiveLockGuarded() = default;
420

421
  RecursiveLockGuardedTryHolder<T> try_lock()
422
  {
423
    return RecursiveLockGuardedTryHolder<T>(d_value, d_mutex);
424
  }
425

426
  RecursiveLockGuardedHolder<T> lock()
427
  {
35,108✔
428
    return RecursiveLockGuardedHolder<T>(d_value, d_mutex);
35,108✔
429
  }
35,108✔
430

431
  RecursiveLockGuardedHolder<const T> read_only_lock()
432
  {
433
    return RecursiveLockGuardedHolder<const T>(d_value, d_mutex);
434
  }
435

436
private:
437
  std::recursive_mutex d_mutex;
438
  T d_value;
439
};
440

441
template <typename T>
442
class SharedLockGuardedHolder
443
{
444
public:
445
  explicit SharedLockGuardedHolder(T& value, std::shared_mutex& mutex) :
446
    d_lock(mutex), d_value(value)
447
  {
6,847,886✔
448
  }
6,847,886✔
449

450
  T& operator*() const noexcept
451
  {
6,769,823✔
452
    return d_value;
6,769,823✔
453
  }
6,769,823✔
454

455
  T* operator->() const noexcept
456
  {
27,967,256✔
457
    return &d_value;
27,967,256✔
458
  }
27,967,256✔
459

460
private:
461
  std::lock_guard<std::shared_mutex> d_lock;
462
  T& d_value;
463
};
464

465
template <typename T>
466
class SharedLockGuardedTryHolder
467
{
468
public:
469
  explicit SharedLockGuardedTryHolder(T& value, std::shared_mutex& mutex) :
470
    d_lock(mutex, std::try_to_lock), d_value(value)
471
  {
3,189,254✔
472
  }
3,189,254✔
473

474
  T& operator*() const
475
  {
948,461✔
476
    if (!owns_lock()) {
948,461!
477
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
478
    }
479
    return d_value;
948,461✔
480
  }
948,461✔
481

482
  T* operator->() const
483
  {
3,383,625✔
484
    if (!owns_lock()) {
3,383,625!
485
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
486
    }
487
    return &d_value;
3,383,625✔
488
  }
3,383,625✔
489

490
  operator bool() const noexcept
491
  {
492
    return d_lock.owns_lock();
493
  }
494

495
  [[nodiscard]] bool owns_lock() const noexcept
496
  {
7,521,194✔
497
    return d_lock.owns_lock();
7,521,194✔
498
  }
7,521,194✔
499

500
private:
501
  std::unique_lock<std::shared_mutex> d_lock;
502
  T& d_value;
503
};
504

505
template <typename T>
506
class SharedLockGuardedNonExclusiveHolder
507
{
508
public:
509
  explicit SharedLockGuardedNonExclusiveHolder(const T& value, std::shared_mutex& mutex) :
510
    d_lock(mutex), d_value(value)
511
  {
49,209✔
512
  }
49,209✔
513

514
  const T& operator*() const noexcept
515
  {
5,981✔
516
    return d_value;
5,981✔
517
  }
5,981✔
518

519
  const T* operator->() const noexcept
520
  {
45,272✔
521
    return &d_value;
45,272✔
522
  }
45,272✔
523

524
private:
525
  std::shared_lock<std::shared_mutex> d_lock;
526
  const T& d_value;
527
};
528

529
template <typename T>
530
class SharedLockGuardedNonExclusiveTryHolder
531
{
532
public:
533
  explicit SharedLockGuardedNonExclusiveTryHolder(const T& value, std::shared_mutex& mutex) :
534
    d_lock(mutex, std::try_to_lock), d_value(value)
535
  {
3,982,297✔
536
  }
3,982,297✔
537

538
  const T& operator*() const
539
  {
1,189,443✔
540
    if (!owns_lock()) {
1,189,443!
541
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
542
    }
543
    return d_value;
1,189,443✔
544
  }
1,189,443✔
545

546
  const T* operator->() const
547
  {
5,267,448✔
548
    if (!owns_lock()) {
5,267,448!
549
      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
550
    }
551
    return &d_value;
5,267,448✔
552
  }
5,267,448✔
553

554
  operator bool() const noexcept
555
  {
556
    return d_lock.owns_lock();
557
  }
558

559
  [[nodiscard]] bool owns_lock() const noexcept
560
  {
10,440,344✔
561
    return d_lock.owns_lock();
10,440,344✔
562
  }
10,440,344✔
563

564
private:
565
  std::shared_lock<std::shared_mutex> d_lock;
566
  const T& d_value;
567
};
568

569
template <typename T>
570
class SharedLockGuarded
571
{
572
public:
573
  explicit SharedLockGuarded(const T& value) :
574
    d_value(value)
575
  {
6✔
576
  }
6✔
577

578
  explicit SharedLockGuarded(T&& value) :
579
    d_value(std::move(value))
580
  {
2,094✔
581
  }
2,094✔
582

583
  explicit SharedLockGuarded() = default;
40,113✔
584

585
  SharedLockGuardedTryHolder<T> try_write_lock()
586
  {
3,189,571✔
587
    return SharedLockGuardedTryHolder<T>(d_value, d_mutex);
3,189,571✔
588
  }
3,189,571✔
589

590
  SharedLockGuardedHolder<T> write_lock()
591
  {
6,847,886✔
592
    return SharedLockGuardedHolder<T>(d_value, d_mutex);
6,847,886✔
593
  }
6,847,886✔
594

595
  SharedLockGuardedNonExclusiveTryHolder<T> try_read_lock()
596
  {
3,994,968✔
597
    return SharedLockGuardedNonExclusiveTryHolder<T>(d_value, d_mutex);
3,994,968✔
598
  }
3,994,968✔
599

600
  SharedLockGuardedNonExclusiveHolder<T> read_lock()
601
  {
49,209✔
602
    return SharedLockGuardedNonExclusiveHolder<T>(d_value, d_mutex);
49,209✔
603
  }
49,209✔
604

605
private:
606
  std::shared_mutex d_mutex;
607
  T d_value;
608
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc