WE Core
Loading...
Searching...
No Matches
AudioSpinMutex.h
Go to the documentation of this file.
1/*
2 * File: AudioSpinMutex.h
3 *
4 * Created: 22/10/2021
5 *
6 * This file is part of WECore.
7 *
8 * WECore is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * WECore is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with WECore. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#pragma once
23
24#include <array>
25#include <thread>
26#include <atomic>
27
28// Some useful links about these instructions in notes/splnlock-instructions.txt
29#if defined(__x86_64__) || defined(_M_AMD64)
30 #include <emmintrin.h>
31 #define CPU_PAUSE _mm_pause();
32#elif defined(__aarch64__) || defined(_M_ARM64)
33 #define CPU_PAUSE __asm__ __volatile__("yield" ::: "memory");
34#else
35 #error Unsupported architecture
36#endif
37
38namespace WECore {
39
47 public:
48 AudioSpinMutex() = default;
49 ~AudioSpinMutex() = default;
50
56 void lock() {
57 constexpr std::array iterations = {5, 10, 3000};
58
59 for (int i = 0; i < iterations[0]; ++i) {
60 if (tryLock()) {
61 return;
62 }
63 }
64
65 for (int i = 0; i < iterations[1]; ++i) {
66 if (tryLock()) {
67 return;
68 }
69
70 CPU_PAUSE
71 }
72
73 while (true) {
74 for (int i = 0; i < iterations[2]; ++i) {
75 if (tryLock()) {
76 return;
77 }
78
79 CPU_PAUSE
80 CPU_PAUSE
81 CPU_PAUSE
82 CPU_PAUSE
83 CPU_PAUSE
84 CPU_PAUSE
85 CPU_PAUSE
86 CPU_PAUSE
87 CPU_PAUSE
88 CPU_PAUSE
89 }
90
91 // Waiting longer than we should, let's give other threads
92 // a chance to recover
93 std::this_thread::yield();
94 }
95 }
96
102 bool tryLock() {
103 return !flag.test_and_set(std::memory_order_acquire);
104 }
105
109 void unlock() {
110 flag.clear(std::memory_order_release);
111 }
112
113 private:
114 std::atomic_flag flag = ATOMIC_FLAG_INIT;
115 };
116
118 public:
120
122 if (_isLocked) {
123 _mutex.unlock();
124 }
125 }
126
127 void unlock() {
128 if (_isLocked) {
129 _mutex.unlock();
130 _isLocked = false;
131 }
132 }
133
134 bool isLocked() { return _isLocked; }
135
136 protected:
138
139 // Keeps track of whether this lock is still holding the mutex. Must always check this
140 // internally before calling unlock on the mutex, otherwise we might unlock it, then the lock
141 // is taken by someone else, then we unlock it again.
143 };
144
149 public:
151 _mutex.lock();
152 _isLocked = true;
153 }
154 };
155
160 public:
163 }
164 };
165}
AudioSpinLockBase(AudioSpinMutex &mutex)
AudioSpinLock(AudioSpinMutex &mutex)
std::atomic_flag flag
AudioSpinTryLock(AudioSpinMutex &mutex)