HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
unfair_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 "required.hpp"
8#include "thread.hpp"
9#include "assert.hpp"
10#include "dead_lock_detector.hpp"
11#include <atomic>
12#include <memory>
13#include <thread>
14
15namespace tt {
16
32template<bool UseDeadLockDetector>
34public:
35 constexpr unfair_mutex_impl() noexcept {}
36 unfair_mutex_impl(unfair_mutex_impl const &) = delete;
38 unfair_mutex_impl &operator=(unfair_mutex_impl const &) = delete;
39 unfair_mutex_impl &operator=(unfair_mutex_impl &&) = delete;
40
41 ~unfair_mutex_impl() requires (UseDeadLockDetector)
42 {
44 }
45
46 ~unfair_mutex_impl() = default;
47
48 void lock() noexcept
49 {
50 if constexpr (UseDeadLockDetector) {
51 ttlet other = dead_lock_detector::lock(this);
52 tt_axiom(other != this, "Mutex already locked.");
53 tt_axiom(other == nullptr, "Potential dead-lock.");
54 }
55
56 tt_axiom(semaphore.load() <= 2);
57
58 // Switch to 1 means there are no waiters.
59 uint32_t expected = 0;
60 if (!semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
61 [[unlikely]] lock_contended(expected);
62 }
63
64 tt_axiom(semaphore.load() <= 2);
65 }
66
74 [[nodiscard]] bool try_lock() noexcept {
75 if constexpr (UseDeadLockDetector) {
76 ttlet other = dead_lock_detector::lock(this);
77 tt_axiom(other != this, "Mutex already locked.");
78 tt_axiom(other == nullptr, "Potential dead-lock.");
79 }
80
81 tt_axiom(semaphore.load() <= 2);
82
83 // Switch to 1 means there are no waiters.
84 uint32_t expected = 0;
85 if (!semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
86 tt_axiom(semaphore.load() <= 2);
87
88 if constexpr (UseDeadLockDetector) {
89 tt_axiom(dead_lock_detector::unlock(this), "Unlocking mutex out of order.");
90 }
91
92 [[unlikely]] return false;
93 }
94
95 tt_axiom(semaphore.load() <= 2);
96 return true;
97 }
98
99 void unlock() noexcept {
100 if constexpr (UseDeadLockDetector) {
101 tt_axiom(dead_lock_detector::unlock(this), "Unlocking mutex out of order.");
102 }
103
104 tt_axiom(semaphore.load() <= 2);
105
106 if (semaphore.fetch_sub(1, std::memory_order::relaxed) != 1) {
107 [[unlikely]] semaphore.store(0, std::memory_order::release);
108
109 semaphore.notify_one();
110 } else {
111 atomic_thread_fence(std::memory_order::release);
112 }
113
114 tt_axiom(semaphore.load() <= 2);
115 }
116
117private:
118 /*
119 * semaphore value:
120 * 0 - Unlocked, no other thread is waiting.
121 * 1 - Locked, no other thread is waiting.
122 * 2 - Locked, zero or more threads are waiting.
123 */
124 std::atomic<uint32_t> semaphore = 0;
125
126 tt_no_inline void lock_contended(uint32_t expected) noexcept
127 {
128 tt_axiom(semaphore.load() <= 2);
129
130 do {
131 ttlet should_wait = expected == 2;
132
133 // Set to 2 when we are waiting.
134 expected = 1;
135 if (should_wait || semaphore.compare_exchange_strong(expected, 2)) {
136 tt_axiom(semaphore.load() <= 2);
137 semaphore.wait(2);
138 }
139
140 tt_axiom(semaphore.load() <= 2);
141 // Set to 2 when acquiring the lock, so that during unlock we wake other waiting threads.
142 expected = 0;
143 } while (!semaphore.compare_exchange_strong(expected, 2));
144 }
145};
146
147#if TT_BUILD_TYPE == TT_BT_DEBUG
148using unfair_mutex = unfair_mutex_impl<true>;
149#else
150using unfair_mutex = unfair_mutex_impl<false>;
151#endif
152
153}
static void * lock(void *object) noexcept
Lock an object on this thread.
static void remove_object(void *object) noexcept
Remove the object from the detection.
static bool unlock(void *object) noexcept
Unlock an object on this thread.
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:74
T atomic_thread_fence(T... args)
T compare_exchange_strong(T... args)
T fetch_sub(T... args)
T load(T... args)
T store(T... args)