chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
85 / 90
94.4%
Branches:
38 / 41
92.7%
Functions:
13 / 14
92.9%

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
2/2
✓ Branch 1 taken 37034 times.
✓ Branch 2 taken 1007265 times.
1044299 if (!lock.m_mutex.try_lock()) {
24
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 36868 times.
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
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 36868 times.
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
2/2
✓ Branch 1 taken 163410 times.
✓ Branch 2 taken 87252 times.
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
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ 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
2/2
✓ Branch 0 taken 418203 times.
✓ Branch 1 taken 789506 times.
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
2/2
✓ Branch 0 taken 1207709 times.
✓ Branch 1 taken 260572 times.
1468281 if (m_lock == nullptr) {
64 return;
65 }
66
2/2
✓ Branch 0 taken 418203 times.
✓ Branch 1 taken 789506 times.
1207709 const StoreLock& lock = *std::exchange(m_lock, nullptr);
67 1207709 const bool outermost = lock.m_depth == 1;
68
2/2
✓ Branch 0 taken 418203 times.
✓ Branch 1 taken 789506 times.
1207709 if (outermost) {
69 418203 lock.RunDeferred();
70 }
71
2/2
✓ Branch 0 taken 418203 times.
✓ Branch 1 taken 789506 times.
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
6/6
✓ Branch 1 taken 418209 times.
✓ Branch 2 taken 789506 times.
✓ Branch 3 taken 958 times.
✓ Branch 4 taken 417251 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 952 times.
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
2/2
✓ Branch 0 taken 87252 times.
✓ Branch 1 taken 163410 times.
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
1/1
✓ Branch 1 taken 127855 times.
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
2/2
✓ Branch 0 taken 101049 times.
✓ Branch 1 taken 418209 times.
519258 while (HasDeferred()) {
115 101049 std::vector<std::function<void()>> writes;
116 101049 {
117
1/1
✓ Branch 1 taken 101049 times.
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
2/2
✓ Branch 0 taken 127855 times.
✓ Branch 1 taken 101049 times.
228904 for (const std::function<void()>& write : writes) {
122 127855 try {
123
1/1
✓ Branch 1 taken 127854 times.
127855 write();
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 } catch (const std::exception& error) {
125
1/1
✓ Branch 2 taken 1 time.
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
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 225090 times.
225092 if (!HeldByThisThread()) {
133 2 throw Error("{} without holding the scene's lock; every public scene call must take it",
134
1/1
✓ Branch 2 taken 2 times.
2 what);
135 }
136 225090 }
137
138 }
139