OSSIA
Open Scenario System for Interactive Application
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(_M_ARM64)
19 #include <intrin.h>
20 #define ossia_rwlock_pause() __yield()
21 #elif defined(__i386__)
22 #define ossia_rwlock_pause() __asm__ __volatile__("rep; nop")
23 #elif defined(__ia64__)
24 #define ossia_rwlock_pause() __asm__ __volatile__("hint @pause")
25 #elif defined(__arm__)
26 #define ossia_rwlock_pause() __asm__ __volatile__("yield")
27 #elif defined(__sparc) || defined(__sparc__)
28 #define ossia_rwlock_pause() __asm__ __volatile__("pause")
29 #elif defined(__ppc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) \
30  || defined(_ARCH_PWR2) || defined(_POWER)
31 #define ossia_rwlock_pause() __asm__ volatile("or 27,27,27")
32 #elif defined(_MSC_VER)
33 #include <windows.h>
34 #define ossia_rwlock_pause() YieldProcessor()
35 #else
36 #define ossia_rwlock_pause()
37 #endif
38 
39 namespace ossia
40 {
41 
42 // Code adapted from Timur Doumler's great article:
43 // https://timur.audio/using-locks-in-real-time-audio-processing-safely
44 struct TS_CAPABILITY("mutex") audio_spin_mutex
45 {
46  void lock() noexcept TS_ACQUIRE()
47  {
48  // approx. 5x5 ns (= 25 ns), 10x40 ns (= 400 ns), and 3000x350 ns
49  // (~ 1 ms), respectively, when measured on a 2.9 GHz Intel i9
50  constexpr std::array iterations = {5, 10, 3000};
51 
52  for(int i = 0; i < iterations[0]; ++i)
53  {
54  if(try_lock())
55  return;
56  }
57 
58  for(int i = 0; i < iterations[1]; ++i)
59  {
60  if(try_lock())
61  return;
62 
63  ossia_rwlock_pause();
64  }
65 
66  while(true)
67  {
68  for(int i = 0; i < iterations[2]; ++i)
69  {
70  if(try_lock())
71  return;
72 
73  ossia_rwlock_pause();
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  }
84 
85  // waiting longer than we should, let's give other threads
86  // a chance to recover
87  std::this_thread::yield();
88  }
89  }
90 
91  bool try_lock() TS_TRY_ACQUIRE(true)
92  {
93  return !locked.load(std::memory_order_relaxed)
94  && !locked.exchange(true, std::memory_order_acquire);
95  }
96 
97  void unlock() TS_RELEASE() { locked.store(false, std::memory_order_release); }
98 
99  const auto& operator!() const { return *this; }
100 
101 private:
102  std::atomic<bool> locked{false};
103 };
104 }
Definition: git_info.h:7