OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
audio_spin_mutex.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
4#include <ossia/detail/mutex.hpp>
5
6#include <array>
7#include <atomic>
8#include <thread>
9
10// Adapted from bind9:
11// https://gitlab.isc.org/isc-projects/bind9/-/blob/main/lib/isc/rwlock.c
12#if defined(__EMSCRIPTEN__)
13// TODO once we support asyncify
14#define ossia_rwlock_pause()
15#elif defined(__x86_64__) || defined(_M_X64)
16#include <immintrin.h>
17#define ossia_rwlock_pause() _mm_pause()
18#elif defined(__i386__)
19#define ossia_rwlock_pause() __asm__ __volatile__("rep; nop")
20#elif defined(__ia64__)
21#define ossia_rwlock_pause() __asm__ __volatile__("hint @pause")
22#elif defined(__aarch64__)
23#define ossia_rwlock_pause() __asm__ __volatile__("dmb ishst\n\tyield" ::: "memory")
24#elif defined(__arm__)
25#define ossia_rwlock_pause() __asm__ __volatile__("yield")
26#elif defined(__sparc) || defined(__sparc__)
27#define ossia_rwlock_pause() __asm__ __volatile__("pause")
28#elif defined(__ppc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) \
29 || defined(_ARCH_PWR2) || defined(_POWER)
30#define ossia_rwlock_pause() __asm__ __volatile__("or 27,27,27")
31#elif defined(#elif defined(__riscv)
32#define ossia_rwlock_pause() __asm__ __volatile__("pause"))
33#elif defined(_MSC_VER)
34#include <windows.h>
35#define ossia_rwlock_pause() YieldProcessor()
36#else
37#define ossia_rwlock_pause()
38#endif
39
40namespace ossia
41{
42
43// Code adapted from Timur Doumler's great article:
44// https://timur.audio/using-locks-in-real-time-audio-processing-safely
45struct TS_CAPABILITY("mutex") audio_spin_mutex
46{
47 void lock() noexcept TS_ACQUIRE()
48 {
49 // approx. 5x5 ns (= 25 ns), 10x40 ns (= 400 ns), and 3000x350 ns
50 // (~ 1 ms), respectively, when measured on a 2.9 GHz Intel i9
51 constexpr std::array iterations = {5, 10, 3000};
52
53 for(int i = 0; i < iterations[0]; ++i)
54 {
55 if(try_lock())
56 return;
57 }
58
59 for(int i = 0; i < iterations[1]; ++i)
60 {
61 if(try_lock())
62 return;
63
64 ossia_rwlock_pause();
65 }
66
67 while(true)
68 {
69 for(int i = 0; i < iterations[2]; ++i)
70 {
71 if(try_lock())
72 return;
73
74 ossia_rwlock_pause();
75 ossia_rwlock_pause();
76 ossia_rwlock_pause();
77 ossia_rwlock_pause();
78 ossia_rwlock_pause();
79 ossia_rwlock_pause();
80 ossia_rwlock_pause();
81 ossia_rwlock_pause();
82 ossia_rwlock_pause();
83 ossia_rwlock_pause();
84 }
85
86 // waiting longer than we should, let's give other threads
87 // a chance to recover
88 std::this_thread::yield();
89 }
90 }
91
92 bool try_lock() TS_TRY_ACQUIRE(true)
93 {
94 return !locked.load(std::memory_order_relaxed)
95 && !locked.exchange(true, std::memory_order_acquire);
96 }
97
98 void unlock() TS_RELEASE() { locked.store(false, std::memory_order_release); }
99
100 const auto& operator!() const { return *this; }
101
102private:
103 std::atomic<bool> locked{false};
104};
105}
Definition git_info.h:7