HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
unfair_recursive_mutex.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "unfair_mutex.hpp"
8#include "thread.hpp"
9#include <thread>
10
11namespace hi::inline v1 {
12
33 /* Thread annotation syntax.
34 *
35 * FIRST - The thread that acquires/acquired the mutex
36 * OWNER - The FIRST thread that recursively requests a lock.
37 * OTHER - Another thread while the mutex is held.
38 */
39
41
42 // FIRST=write, OWNER|OTHER=read
43 std::atomic<thread_id> owner = 0;
44
45 // FIRST=write, OWNER=increment, FIRST|OWNER=decrement
46 uint32_t count = 0;
47
48public:
50 unfair_recursive_mutex &operator=(unfair_recursive_mutex const &) = delete;
51
52 unfair_recursive_mutex() = default;
53 ~unfair_recursive_mutex() = default;
54
60 [[nodiscard]] int recurse_lock_count() const noexcept
61 {
62 // The following load() is:
63 // - valid-and-equal to thread_id when the OWNER has the lock.
64 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
65 //
66 // This only works for comparing the owner with the current thread, it would
67 // not work to check the owner with a thread_id of another thread.
68 if (owner.load(std::memory_order::acquire) == current_thread_id()) {
69 return count;
70 } else {
71 return 0;
72 }
73 }
74
78 [[nodiscard]] bool try_lock() noexcept
79 {
80 // FIRST | OWNER | OTHER
81 hilet thread_id = current_thread_id();
82
83 // The following load() is:
84 // - valid-and-equal to thread_id when the OWNER has the lock.
85 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
86 if (owner.load(std::memory_order::acquire) == thread_id) {
87 // FIRST | OWNER
88 hi_axiom(count != 0);
89 ++count;
90
91 // OWNER
92 return true;
93
94 } else if (mutex.try_lock()) { // OTHER (inside the if expression)
95 // FIRST
96 hi_axiom(count == 0);
97 count = 1;
98 hi_axiom(owner == 0);
99 owner.store(thread_id, std::memory_order::release);
100
101 return true;
102
103 } else {
104 // OTHER
105 return false;
106 }
107 }
108
125 void lock() noexcept
126 {
127 // FIRST | OWNER | OTHER
128 hilet thread_id = current_thread_id();
129
130 // The following load() is:
131 // - valid-and-equal to thread_id when the OWNER has the lock.
132 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
133 if (owner.load(std::memory_order::acquire) == thread_id) {
134 // FIRST | OWNER
135 hi_axiom(count != 0);
136 ++count;
137
138 // OWNER
139
140 } else {
141 // OTHER
142 mutex.lock();
143
144 // FIRST
145 hi_axiom(count == 0);
146 count = 1;
147 hi_axiom(owner == 0);
148 owner.store(thread_id, std::memory_order::release);
149 }
150 }
151
152 void unlock() noexcept
153 {
154 // FIRST | OWNER
155
156 // Unlock must be called on the thread that locked the mutex
157 hi_axiom(recurse_lock_count());
158
159 if (--count == 0) {
160 // FIRST
161
162 // Only OTHER can execute in `lock()` or `try_lock()`,
163 // where it will either see the thread_id of FIRST or zero.
164 // In both cases the OTHER thread is detected correctly.
165 owner.store(0, std::memory_order::release);
166
167 mutex.unlock();
168 // OTHER
169 }
170 // OWNER | OTHER
171 }
172};
173
174} // namespace hi::inline v1
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
An unfair mutex This is a fast implementation of a mutex which does not fairly arbitrate between mult...
Definition unfair_mutex.hpp:33
bool try_lock() noexcept
When try_lock() is called from a thread that already owns the lock it will return false.
Definition unfair_mutex.hpp:81
An unfair recursive-mutex This is a fast implementation of a recursive-mutex which does not fairly ar...
Definition unfair_recursive_mutex.hpp:32
int recurse_lock_count() const noexcept
This function should be used in hi_axiom() to check if the lock is held by current thread.
Definition unfair_recursive_mutex.hpp:60
void lock() noexcept
Definition unfair_recursive_mutex.hpp:125
bool try_lock() noexcept
When try_lock() is called on a thread that already holds the lock true is returned.
Definition unfair_recursive_mutex.hpp:78
T load(T... args)
T store(T... args)