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 tt {
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
40 unfair_mutex mutex;
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 // The following load() is:
62 // - valid-and-equal to thread_id when the OWNER has the lock.
63 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
64 //
65 // This only works for comparing the owner with the current thread, it would
66 // not work to check the owner with a thread_id of another thread.
67 if (owner.load(std::memory_order::memory_order_acquire) == current_thread_id()) {
68 return count;
69 } else {
70 return 0;
71 }
72 }
73
77 [[nodiscard]] bool try_lock() noexcept {
78 // FIRST | OWNER | OTHER
79 ttlet thread_id = current_thread_id();
80
81 // The following load() is:
82 // - valid-and-equal to thread_id when the OWNER has the lock.
83 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
84 if (owner.load(std::memory_order::memory_order_acquire) == thread_id) {
85 // FIRST | OWNER
86 tt_axiom(count != 0);
87 ++count;
88
89 // OWNER
90 return true;
91
92 } else if (mutex.try_lock()) { // OTHER (inside the if expression)
93 // FIRST
94 tt_axiom(count == 0);
95 count = 1;
96 tt_axiom(owner == 0);
97 owner.store(thread_id, std::memory_order::memory_order_release);
98
99 return true;
100
101 } else {
102 // OTHER
103 return false;
104 }
105 }
106
123 void lock() noexcept {
124 // FIRST | OWNER | OTHER
125 ttlet thread_id = current_thread_id();
126
127 // The following load() is:
128 // - valid-and-equal to thread_id when the OWNER has the lock.
129 // - zero or valid-and-not-equal to thread_id when this is an OTHER thread.
130 if (owner.load(std::memory_order::memory_order_acquire) == thread_id) {
131 // FIRST | OWNER
132 tt_axiom(count != 0);
133 ++count;
134
135 // OWNER
136
137 } else {
138 // OTHER
139 mutex.lock();
140
141 // FIRST
142 tt_axiom(count == 0);
143 count = 1;
144 tt_axiom(owner == 0);
145 owner.store(thread_id, std::memory_order::memory_order_release);
146 }
147 }
148
149 void unlock() noexcept {
150 // FIRST | OWNER
151 tt_axiom(recurse_lock_count(), "Unlock must be called on the thread that locked the mutex");
152
153 if (--count == 0) {
154 // FIRST
155
156 // Only OTHER can execute in `lock()` or `try_lock()`,
157 // where it will either see the thread_id of FIRST or zero.
158 // In both cases the OTHER thread is detected correctly.
159 owner.store(0, std::memory_order::memory_order_release);
160
161 mutex.unlock();
162 // OTHER
163 }
164 // OWNER | OTHER
165 }
166
167};
168
169
170
171}
An unfair mutex This is a fast implementation of a mutex which does not fairly arbitrate between mult...
Definition unfair_mutex.hpp:32
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:62
An unfair recursive-mutex This is a fast implementation of a recursive-mutex which does not fairly ar...
Definition unfair_recursive_mutex.hpp:32
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:77
void lock() noexcept
Definition unfair_recursive_mutex.hpp:123
int recurse_lock_count() const noexcept
This function should be used in tt_axiom() to check if the lock is held by current thread.
Definition unfair_recursive_mutex.hpp:60
T load(T... args)
T store(T... args)