stage/store_lock.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/stage/store_lock.h" | ||
| 3 | |||
| 4 | #include <exception> | ||
| 5 | #include <utility> | ||
| 6 | |||
| 7 | #include "chrono_batch_render/util/error.h" | ||
| 8 | #include "chrono_batch_render/util/log.h" | ||
| 9 | |||
| 10 | namespace chrono::batch_render { | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | |||
| 14 | std::atomic<StoreLock::BlockingWait> g_blockingWait{nullptr}; | ||
| 15 | |||
| 16 | } | ||
| 17 | |||
| 18 | 4 | void StoreLock::SetBlockingWait(BlockingWait blockingWait) { | |
| 19 | 4 | g_blockingWait.store(blockingWait, std::memory_order_release); | |
| 20 | 4 | } | |
| 21 | |||
| 22 | 1044299 | StoreLock::Guard::Guard(const StoreLock& lock) : m_lock(&lock) { | |
| 23 | 1044299 | if (!lock.m_mutex.try_lock()) { | |
| 24 | 37034 | const BlockingWait blockingWait = g_blockingWait.load(std::memory_order_acquire); | |
| 25 | 73902 | const auto wait = [](void* mutex) { static_cast<std::recursive_mutex*>(mutex)->lock(); }; | |
| 26 | 37034 | if (blockingWait) { | |
| 27 | 166 | blockingWait(wait, &lock.m_mutex); | |
| 28 | } else { | ||
| 29 | 36868 | wait(&lock.m_mutex); | |
| 30 | } | ||
| 31 | } | ||
| 32 | 1044299 | Enter(); | |
| 33 | 1044299 | } | |
| 34 | |||
| 35 | 250662 | StoreLock::Guard::Guard(const StoreLock& lock, std::try_to_lock_t) { | |
| 36 | 250662 | if (lock.m_mutex.try_lock()) { | |
| 37 | 163410 | m_lock = &lock; | |
| 38 | 163410 | Enter(); | |
| 39 | } | ||
| 40 | 250662 | } | |
| 41 | |||
| 42 | 1468281 | StoreLock::Guard::~Guard() { | |
| 43 | 1468281 | Leave(); | |
| 44 | 1468281 | } | |
| 45 | |||
| 46 | 172044 | StoreLock::Guard::Guard(Guard&& other) noexcept : m_lock(std::exchange(other.m_lock, nullptr)) {} | |
| 47 | |||
| 48 | ✗ | StoreLock::Guard& StoreLock::Guard::operator=(Guard&& other) noexcept { | |
| 49 | ✗ | if (this != &other) { | |
| 50 | ✗ | Leave(); | |
| 51 | ✗ | m_lock = std::exchange(other.m_lock, nullptr); | |
| 52 | } | ||
| 53 | ✗ | return *this; | |
| 54 | } | ||
| 55 | |||
| 56 | 1207709 | void StoreLock::Guard::Enter() { | |
| 57 | 1207709 | if (m_lock->m_depth++ == 0) { | |
| 58 | 418203 | m_lock->m_owner.store(std::this_thread::get_id(), std::memory_order_relaxed); | |
| 59 | } | ||
| 60 | 1207709 | } | |
| 61 | |||
| 62 | 1468281 | void StoreLock::Guard::Leave() noexcept { | |
| 63 | 1468281 | if (m_lock == nullptr) { | |
| 64 | return; | ||
| 65 | } | ||
| 66 | 1207709 | const StoreLock& lock = *std::exchange(m_lock, nullptr); | |
| 67 | 1207709 | const bool outermost = lock.m_depth == 1; | |
| 68 | 1207709 | if (outermost) { | |
| 69 | 418203 | lock.RunDeferred(); | |
| 70 | } | ||
| 71 | 1207709 | if (--lock.m_depth == 0) { | |
| 72 | 418203 | lock.m_owner.store(std::thread::id(), std::memory_order_relaxed); | |
| 73 | } | ||
| 74 | 1207709 | lock.m_mutex.unlock(); | |
| 75 | 2415424 | while (outermost && lock.HasDeferred() && lock.m_mutex.try_lock()) { | |
| 76 | 6 | lock.m_depth = 1; | |
| 77 | 6 | lock.m_owner.store(std::this_thread::get_id(), std::memory_order_relaxed); | |
| 78 | 6 | lock.RunDeferred(); | |
| 79 | 6 | lock.m_depth = 0; | |
| 80 | 6 | lock.m_owner.store(std::thread::id(), std::memory_order_relaxed); | |
| 81 | 6 | lock.m_mutex.unlock(); | |
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | 250662 | std::optional<StoreLock::Guard> StoreLock::TryLock() const { | |
| 86 | 250662 | Guard guard(*this, std::try_to_lock); | |
| 87 | 250662 | if (!guard.OwnsLock()) { | |
| 88 | 87252 | return std::nullopt; | |
| 89 | } | ||
| 90 | 163410 | return guard; | |
| 91 | 250662 | } | |
| 92 | |||
| 93 | 127855 | void StoreLock::Defer(std::function<void()> write) const { | |
| 94 | 127855 | { | |
| 95 | 127855 | const std::lock_guard lock(m_deferredMutex); | |
| 96 | 127855 | m_deferred.push_back(std::move(write)); | |
| 97 | 127855 | m_hasDeferred.store(true, std::memory_order_release); | |
| 98 | 127855 | } | |
| 99 | 127855 | [[maybe_unused]] const std::optional<Guard> guard = TryLock(); | |
| 100 | 127855 | } | |
| 101 | |||
| 102 | 7637 | bool StoreLock::CanDefer() const { | |
| 103 | 7637 | const std::lock_guard lock(m_deferredMutex); | |
| 104 | 7637 | return m_deferred.size() < kMaxDeferred; | |
| 105 | 7637 | } | |
| 106 | |||
| 107 | 353 | void StoreLock::DropDeferred() const { | |
| 108 | 353 | const std::lock_guard lock(m_deferredMutex); | |
| 109 | 353 | m_deferred.clear(); | |
| 110 | 353 | m_hasDeferred.store(false, std::memory_order_release); | |
| 111 | 353 | } | |
| 112 | |||
| 113 | 418209 | void StoreLock::RunDeferred() const { | |
| 114 | 519258 | while (HasDeferred()) { | |
| 115 | 101049 | std::vector<std::function<void()>> writes; | |
| 116 | 101049 | { | |
| 117 | 101049 | const std::lock_guard lock(m_deferredMutex); | |
| 118 | 101049 | writes.swap(m_deferred); | |
| 119 | 101049 | m_hasDeferred.store(false, std::memory_order_release); | |
| 120 | 101049 | } | |
| 121 | 228904 | for (const std::function<void()>& write : writes) { | |
| 122 | 127855 | try { | |
| 123 | 127855 | write(); | |
| 124 | 1 | } catch (const std::exception& error) { | |
| 125 | 1 | Log(LogLevel::Warning, "a deferred scene write failed: {}", error.what()); | |
| 126 | 1 | } | |
| 127 | } | ||
| 128 | 101049 | } | |
| 129 | 418209 | } | |
| 130 | |||
| 131 | 225092 | void StoreLock::Check(const char* what) const { | |
| 132 | 225092 | if (!HeldByThisThread()) { | |
| 133 | 2 | throw Error("{} without holding the scene's lock; every public scene call must take it", | |
| 134 | 2 | what); | |
| 135 | } | ||
| 136 | 225090 | } | |
| 137 | |||
| 138 | } | ||
| 139 |