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 <atomic>
11#include <memory>
12#include <thread>
13
14namespace tt {
15struct unfair_lock_wrap;
16
33public:
34 unfair_mutex() noexcept {}
35 unfair_mutex(unfair_mutex const &) = delete;
36 unfair_mutex(unfair_mutex &&) = delete;
37 unfair_mutex &operator=(unfair_mutex const &) = delete;
38 unfair_mutex &operator=(unfair_mutex &&) = delete;
39
40 void lock() noexcept
41 {
42 tt_axiom(semaphore.load() <= 2);
43
44 // Switch to 1 means there are no waiters.
45 uint32_t expected = 0;
46 if (!semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
47 [[unlikely]] lock_contented(expected);
48 }
49#if TT_BUILD_TYPE == TT_BT_DEBUG
50 locking_thread = current_thread_id();
51#endif
52 tt_axiom(semaphore.load() <= 2);
53 }
54
62 bool try_lock() noexcept {
63 tt_axiom(semaphore.load() <= 2);
64
65 // Switch to 1 means there are no waiters.
66 uint32_t expected = 0;
67 if (!semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
68 tt_axiom(semaphore.load() <= 2);
69 [[unlikely]] return false;
70 }
71#if TT_BUILD_TYPE == TT_BT_DEBUG
72 locking_thread = current_thread_id();
73#endif
74 tt_axiom(semaphore.load() <= 2);
75 return true;
76 }
77
78 void unlock() noexcept {
79 tt_axiom(semaphore.load() <= 2);
80
81 if (semaphore.fetch_sub(1, std::memory_order::relaxed) != 1) {
82 [[unlikely]] semaphore.store(0, std::memory_order::release);
83
84 semaphore.notify_one();
85 } else {
86 atomic_thread_fence(std::memory_order::release);
87 }
88#if TT_BUILD_TYPE == TT_BT_DEBUG
89 locking_thread = 0;
90#endif
91
92 tt_axiom(semaphore.load() <= 2);
93 }
94
95private:
96 /*
97 * semaphore value:
98 * 0 - Unlocked, no other thread is waiting.
99 * 1 - Locked, no other thread is waiting.
100 * 2 - Locked, zero or more threads are waiting.
101 */
102 std::atomic<uint32_t> semaphore = 0;
103
104#if TT_BUILD_TYPE == TT_BT_DEBUG
105 thread_id locking_thread;
106#endif
107
108 tt_no_inline void lock_contented(uint32_t expected) noexcept
109 {
110 tt_axiom(semaphore.load() <= 2);
111
112 do {
113 ttlet should_wait = expected == 2;
114
115 // Set to 2 when we are waiting.
116 expected = 1;
117 if (should_wait || semaphore.compare_exchange_strong(expected, 2)) {
118#if TT_BUILD_TYPE == TT_BT_DEBUG
119 // This check only works because locking_thread can never be the current
120 // thread id. It is either the thread that made the lock, or it is zero.
121 tt_assert(locking_thread != current_thread_id());
122#endif
123 tt_axiom(semaphore.load() <= 2);
124 semaphore.wait(2);
125 }
126
127 tt_axiom(semaphore.load() <= 2);
128 // Set to 2 when acquiring the lock, so that during unlock we wake other waiting threads.
129 expected = 0;
130 } while (!semaphore.compare_exchange_strong(expected, 2));
131 }
132};
133
134}
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
T compare_exchange_strong(T... args)
T fetch_sub(T... args)
T load(T... args)
T store(T... args)