-
Notifications
You must be signed in to change notification settings - Fork 1
Debug Locks
Include CLibUtilsQTR/DebugLocks.hpp and give each mutex a distinct tag:
#include <CLibUtilsQTR/DebugLocks.hpp>
struct InventoryMutexTag {
static constexpr auto name = "inventory";
};
std::shared_mutex inventory_mutex;
void UpdateInventory() {
clib_utilsQTR::DebugUniqueLock<InventoryMutexTag> lock{inventory_mutex};
// Update the protected inventory.
}Use DebugSharedLock<Tag> for shared access. Both guards release the mutex on
destruction and support unlock() for early release. They reject same-thread
recursive acquisition, shared/unique conversion while locked, and unlocking
without ownership. Normal contention between threads waits for the mutex.
Use the same tag for every guard of a given mutex, and a different tag for each
other mutex. DebugLockHeld<Tag>() reports whether the calling thread holds a
guard with that tag.
To enforce an ordering rule, a tag can provide
static void CheckLockOrder(const std::source_location& where). The guard calls
it before acquiring the mutex. Check other tags with DebugLockHeld<OtherTag>()
and call ReportLockViolation<Tag>("reason", where) to reject an acquisition.
Violations print the tag's name, reason, and source location to standard error
and abort. To use your own logger, provide
static void ReportViolation(const char* name, const char* reason, const std::source_location& where)
on the tag. ReportLockViolation aborts after that callback returns.
These guards perform their checks regardless of NDEBUG. Projects that want
them only in Debug builds can select standard locks in their Release code.