22#define __NBL_DEMOTE_PROMOTE_WRITER_READERS_LOCK_H_INCLUDED__
33
44#include < thread>
5+ #include < concepts>
56
67// TODO: Bring back proper memory semantics on fetch/store
78// TODO: CRTP/F-Bound on `perform_under_locked_state`?
@@ -41,6 +42,33 @@ namespace impl
4142 };
4243} // namespace impl
4344
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+ */
4472class demote_promote_writer_readers_lock
4573{
4674public:
@@ -49,7 +77,9 @@ class demote_promote_writer_readers_lock
4977 // Limit on how many threads can be launched concurrently that try to read/write from/to the resource behind this lock
5078 constexpr static inline state_lock_value_t MaxActors = 1023 ;
5179
80+ template <impl::DPWRLDebugCallback>
5281 friend class dp_read_lock_guard ;
82+ template <impl::DPWRLDebugCallback>
5383 friend class dp_write_lock_guard ;
5484
5585 /* *
@@ -234,6 +264,8 @@ class demote_promote_writer_readers_lock
234264
235265private:
236266
267+ constexpr static inline bool usingDebugCallback = std::is_same_v<DebugCallback, impl::DPWRLVoidDebugCallback>;
268+
237269 struct DefaultPreemptionCheck
238270 {
239271 bool operator ()(state_lock_value_t oldState)
@@ -268,18 +300,27 @@ class demote_promote_writer_readers_lock
268300 const state_lock_value_t newState = wasPreempted ? preempted (oldState) : success (oldState);
269301 // new state must unlock the state lock
270302 assert (!(newState & flipLock));
303+ if (usingDebugCallback) DebugCallback (DPWR_LOCK_DEBUG_STAGES::BEFORE_STATE_UPDATE);
271304 state.store (newState);
305+ if (usingDebugCallback) DebugCallback (DPWR_LOCK_DEBUG_STAGES::AFTER_STATE_UPDATE);
272306 if (wasPreempted)
273307 {
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+ }
276317 continue ;
277318 }
278319 break ;
279320 }
280321 }
281322
282- state_lock_t state;
323+ state_lock_t state = {} ;
283324
284325 constexpr static inline state_lock_value_t flipLock = impl::DPWRLStateSemantics{ .currentReaders = 0 , .pendingWriters = 0 , .pendingUpgrades = 0 , .writing = false , .stateLocked = true };
285326 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
291332
292333namespace impl
293334{
335+ template <impl::DPWRLDebugCallback DebugCallback = impl::DPWRLVoidDebugCallback>
294336 class dpwr_lock_guard_base
295337 {
296338 dpwr_lock_guard_base () : m_lock(nullptr ) {}
297339
298340 public:
341+ using dpwr_lock_t = typename demote_promote_writer_readers_lock<DebugCallback>;
299342 dpwr_lock_guard_base& operator =(const dpwr_lock_guard_base&) = delete ;
300343 dpwr_lock_guard_base (const dpwr_lock_guard_base&) = delete ;
301344
@@ -311,54 +354,64 @@ namespace impl
311354 }
312355
313356 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) {}
315358
316- demote_promote_writer_readers_lock * m_lock;
359+ dpwr_lock_t * m_lock;
317360 };
318361} // namespace impl
319362
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>
321365{
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>;
322369public:
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 ())
325372 {
326- m_lock->read_lock ();
373+ this -> m_lock ->read_lock ();
327374 }
328- explicit dp_read_lock_guard (dp_write_lock_guard && wl);
375+ explicit dp_read_lock_guard (dp_write_lock_guard_t && wl);
329376
330377 ~dp_read_lock_guard ()
331378 {
332- if (m_lock)
333- m_lock->read_unlock ();
379+ if (this -> m_lock )
380+ this -> m_lock ->read_unlock ();
334381 }
335382};
336383
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>
338386{
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>;
339390public:
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 ())
342393 {
343- m_lock->write_lock ();
394+ this -> m_lock ->write_lock ();
344395 }
345- explicit dp_write_lock_guard (dp_read_lock_guard && rl);
396+ explicit dp_write_lock_guard (dp_read_lock_guard_t && rl);
346397
347398 ~dp_write_lock_guard ()
348399 {
349- if (m_lock)
350- m_lock->write_unlock ();
400+ if (this -> m_lock )
401+ this -> m_lock ->write_unlock ();
351402 }
352403};
353404
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))
355407{
356- m_lock->downgrade ();
408+ this -> m_lock ->downgrade ();
357409}
358410
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))
360413{
361- m_lock->upgrade ();
414+ this -> m_lock ->upgrade ();
362415}
363416
364417} // namespace nbl::system
0 commit comments