2
2
#define __NBL_DEMOTE_PROMOTE_WRITER_READERS_LOCK_H_INCLUDED__
3
3
4
4
#include < thread>
5
+ #include < concepts>
5
6
6
7
// TODO: Bring back proper memory semantics on fetch/store
7
8
// TODO: CRTP/F-Bound on `perform_under_locked_state`?
@@ -41,6 +42,33 @@ namespace impl
41
42
};
42
43
} // namespace impl
43
44
45
+ enum DPWR_LOCK_DEBUG_STAGES
46
+ {
47
+ // To ensure correctness during debug, the callback at `BEFORE_STATE_UPDATE` MUST make the virtual thread that called it run again. This is so that during debug
48
+ // only the virtual thread holding the flipLock progresses
49
+ BEFORE_STATE_UPDATE,
50
+ AFTER_STATE_UPDATE,
51
+ PREEMPTED
52
+ };
53
+
54
+ namespace impl
55
+ {
56
+ template <typename T>
57
+ concept DPWRLDebugCallback = requires (T t, DPWR_LOCK_DEBUG_STAGES stage) {
58
+ { t (stage) } -> std::same_as<void >;
59
+ };
60
+
61
+ class DPWRLVoidDebugCallback
62
+ {
63
+ void operator ()(DPWR_LOCK_DEBUG_STAGES) {};
64
+ };
65
+ }
66
+
67
+ template <impl::DPWRLDebugCallback DebugCallback = impl::DPWRLVoidDebugCallback>
68
+ /* *
69
+ * @brief By default it has no debug callback. You can provide a debug callback for every stage of the lock's loop (see `perform_under_locked_state` and `DPWR_LOCK_DEBUG_STAGES`)
70
+ * as a functional struct that overloads `void operator(DPWR_LOCK_DEBUG_STAGES)`
71
+ */
44
72
class demote_promote_writer_readers_lock
45
73
{
46
74
public:
@@ -49,7 +77,9 @@ class demote_promote_writer_readers_lock
49
77
// Limit on how many threads can be launched concurrently that try to read/write from/to the resource behind this lock
50
78
constexpr static inline state_lock_value_t MaxActors = 1023 ;
51
79
80
+ template <impl::DPWRLDebugCallback>
52
81
friend class dp_read_lock_guard ;
82
+ template <impl::DPWRLDebugCallback>
53
83
friend class dp_write_lock_guard ;
54
84
55
85
/* *
@@ -234,6 +264,8 @@ class demote_promote_writer_readers_lock
234
264
235
265
private:
236
266
267
+ constexpr static inline bool usingDebugCallback = std::is_same_v<DebugCallback, impl::DPWRLVoidDebugCallback>;
268
+
237
269
struct DefaultPreemptionCheck
238
270
{
239
271
bool operator ()(state_lock_value_t oldState)
@@ -268,18 +300,27 @@ class demote_promote_writer_readers_lock
268
300
const state_lock_value_t newState = wasPreempted ? preempted (oldState) : success (oldState);
269
301
// new state must unlock the state lock
270
302
assert (!(newState & flipLock));
303
+ if (usingDebugCallback) DebugCallback (DPWR_LOCK_DEBUG_STAGES::BEFORE_STATE_UPDATE);
271
304
state.store (newState);
305
+ if (usingDebugCallback) DebugCallback (DPWR_LOCK_DEBUG_STAGES::AFTER_STATE_UPDATE);
272
306
if (wasPreempted)
273
307
{
274
- if (spinCount > SpinsBeforeYield)
275
- std::this_thread::yield ();
308
+ if (usingDebugCallback)
309
+ {
310
+ DebugCallback (DPWR_LOCK_DEBUG_STAGES::PREEMPTED);
311
+ }
312
+ else
313
+ {
314
+ if (spinCount > SpinsBeforeYield)
315
+ std::this_thread::yield ();
316
+ }
276
317
continue ;
277
318
}
278
319
break ;
279
320
}
280
321
}
281
322
282
- state_lock_t state;
323
+ state_lock_t state = {} ;
283
324
284
325
constexpr static inline state_lock_value_t flipLock = impl::DPWRLStateSemantics{ .currentReaders = 0 , .pendingWriters = 0 , .pendingUpgrades = 0 , .writing = false , .stateLocked = true };
285
326
constexpr static inline state_lock_value_t writingMask = impl::DPWRLStateSemantics{ .currentReaders = 0 , .pendingWriters = 0 , .pendingUpgrades = 0 , .writing = true , .stateLocked = false };
@@ -291,11 +332,13 @@ class demote_promote_writer_readers_lock
291
332
292
333
namespace impl
293
334
{
335
+ template <impl::DPWRLDebugCallback DebugCallback = impl::DPWRLVoidDebugCallback>
294
336
class dpwr_lock_guard_base
295
337
{
296
338
dpwr_lock_guard_base () : m_lock(nullptr ) {}
297
339
298
340
public:
341
+ using dpwr_lock_t = typename demote_promote_writer_readers_lock<DebugCallback>;
299
342
dpwr_lock_guard_base& operator =(const dpwr_lock_guard_base&) = delete ;
300
343
dpwr_lock_guard_base (const dpwr_lock_guard_base&) = delete ;
301
344
@@ -311,54 +354,64 @@ namespace impl
311
354
}
312
355
313
356
protected:
314
- dpwr_lock_guard_base (demote_promote_writer_readers_lock & lk) noexcept : m_lock(&lk) {}
357
+ dpwr_lock_guard_base (dpwr_lock_t & lk) noexcept : m_lock(&lk) {}
315
358
316
- demote_promote_writer_readers_lock * m_lock;
359
+ dpwr_lock_t * m_lock;
317
360
};
318
361
} // namespace impl
319
362
320
- class dp_read_lock_guard : public impl ::dpwr_lock_guard_base
363
+ template <impl::DPWRLDebugCallback DebugCallback = impl::DPWRLVoidDebugCallback>
364
+ class dp_read_lock_guard : public impl ::dpwr_lock_guard_base<DebugCallback>
321
365
{
366
+ using base_t = impl::dpwr_lock_guard_base<DebugCallback>;
367
+ using dpwr_lock_t = demote_promote_writer_readers_lock<DebugCallback>;
368
+ using dp_write_lock_guard_t = dp_write_lock_guard<DebugCallback>;
322
369
public:
323
- dp_read_lock_guard (demote_promote_writer_readers_lock & lk, std::adopt_lock_t ) : impl::dpwr_lock_guard_base (lk) {}
324
- explicit dp_read_lock_guard (demote_promote_writer_readers_lock & lk) : dp_read_lock_guard(lk, std::adopt_lock_t ())
370
+ dp_read_lock_guard (dpwr_lock_t & lk, std::adopt_lock_t ) : base_t (lk) {}
371
+ explicit dp_read_lock_guard (dpwr_lock_t & lk) : dp_read_lock_guard(lk, std::adopt_lock_t ())
325
372
{
326
- m_lock->read_lock ();
373
+ this -> m_lock ->read_lock ();
327
374
}
328
- explicit dp_read_lock_guard (dp_write_lock_guard && wl);
375
+ explicit dp_read_lock_guard (dp_write_lock_guard_t && wl);
329
376
330
377
~dp_read_lock_guard ()
331
378
{
332
- if (m_lock)
333
- m_lock->read_unlock ();
379
+ if (this -> m_lock )
380
+ this -> m_lock ->read_unlock ();
334
381
}
335
382
};
336
383
337
- class dp_write_lock_guard : public impl ::dpwr_lock_guard_base
384
+ template <impl::DPWRLDebugCallback DebugCallback = impl::DPWRLVoidDebugCallback>
385
+ class dp_write_lock_guard : public impl ::dpwr_lock_guard_base<DebugCallback>
338
386
{
387
+ using base_t = impl::dpwr_lock_guard_base<DebugCallback>;
388
+ using dpwr_lock_t = demote_promote_writer_readers_lock<DebugCallback>;
389
+ using dp_read_lock_guard_t = dp_read_lock_guard<DebugCallback>;
339
390
public:
340
- dp_write_lock_guard (demote_promote_writer_readers_lock & lk, std::adopt_lock_t ) : impl::dpwr_lock_guard_base (lk) {}
341
- explicit dp_write_lock_guard (demote_promote_writer_readers_lock & lk) : dp_write_lock_guard(lk, std::adopt_lock_t ())
391
+ dp_write_lock_guard (dpwr_lock_t & lk, std::adopt_lock_t ) : base_t (lk) {}
392
+ explicit dp_write_lock_guard (dpwr_lock_t & lk) : dp_write_lock_guard(lk, std::adopt_lock_t ())
342
393
{
343
- m_lock->write_lock ();
394
+ this -> m_lock ->write_lock ();
344
395
}
345
- explicit dp_write_lock_guard (dp_read_lock_guard && rl);
396
+ explicit dp_write_lock_guard (dp_read_lock_guard_t && rl);
346
397
347
398
~dp_write_lock_guard ()
348
399
{
349
- if (m_lock)
350
- m_lock->write_unlock ();
400
+ if (this -> m_lock )
401
+ this -> m_lock ->write_unlock ();
351
402
}
352
403
};
353
404
354
- inline dp_read_lock_guard::dp_read_lock_guard (dp_write_lock_guard&& wl) : impl::dpwr_lock_guard_base(std::move(wl))
405
+ template <impl::DPWRLDebugCallback DebugCallback>
406
+ inline dp_read_lock_guard<DebugCallback>::dp_read_lock_guard(dp_write_lock_guard<DebugCallback>&& wl) : impl::dpwr_lock_guard_base(std::move(wl))
355
407
{
356
- m_lock->downgrade ();
408
+ this -> m_lock ->downgrade ();
357
409
}
358
410
359
- inline dp_write_lock_guard::dp_write_lock_guard (dp_read_lock_guard&& rl) : impl::dpwr_lock_guard_base(std::move(rl))
411
+ template <impl::DPWRLDebugCallback DebugCallback>
412
+ inline dp_write_lock_guard<DebugCallback>::dp_write_lock_guard(dp_read_lock_guard<DebugCallback>&& rl) : impl::dpwr_lock_guard_base(std::move(rl))
360
413
{
361
- m_lock->upgrade ();
414
+ this -> m_lock ->upgrade ();
362
415
}
363
416
364
417
} // namespace nbl::system
0 commit comments