Skip to content

Commit a6e2ffa

Browse files
committed
Add debug callbacks to new locks
1 parent 3a996fe commit a6e2ffa

File tree

1 file changed

+76
-23
lines changed

1 file changed

+76
-23
lines changed

include/nbl/system/demote_promote_writer_readers_lock.h

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
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+
*/
4472
class demote_promote_writer_readers_lock
4573
{
4674
public:
@@ -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

235265
private:
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

292333
namespace 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>;
322369
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())
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>;
339390
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())
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

Comments
 (0)