Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
wake_signal.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <atomic>
8#include <chrono>
9#include <cstdint>
10
11#if defined(__APPLE__)
12#include <dispatch/dispatch.h>
13#else
14#include <semaphore>
15#endif
16
17namespace pulp::runtime {
18
57 public:
58 using Clock = std::chrono::steady_clock;
59
60 WakeSignal() noexcept
61#if defined(__APPLE__)
62 : semaphore_(dispatch_semaphore_create(0))
63#endif
64 {
65 }
66
68#if defined(__APPLE__)
69 // libdispatch traps when a semaphore is released below its initial
70 // value, i.e. with a thread still waiting. Surplus posts leave the
71 // value above zero, which is allowed.
72 if (semaphore_ != nullptr)
73 dispatch_release(semaphore_);
74#endif
75 }
76
77 WakeSignal(const WakeSignal&) = delete;
78 WakeSignal& operator=(const WakeSignal&) = delete;
79
81 void notify() noexcept {
82 std::atomic_thread_fence(std::memory_order_seq_cst);
83 if (parked_.load(std::memory_order_relaxed)) {
84 posts_.fetch_add(1, std::memory_order_relaxed);
85 post();
86 }
87 }
88
92 template <typename Ready> bool wait_until(Ready&& ready, Clock::time_point deadline) {
93 for (;;) {
94 if (ready())
95 return true;
96 parked_.store(true, std::memory_order_relaxed);
97 std::atomic_thread_fence(std::memory_order_seq_cst);
98 if (ready()) {
99 parked_.store(false, std::memory_order_relaxed);
100 return true;
101 }
102 const auto now = Clock::now();
103 if (now >= deadline) {
104 parked_.store(false, std::memory_order_relaxed);
105 return false;
106 }
107 park(deadline - now);
108 parked_.store(false, std::memory_order_relaxed);
109 wakes_.fetch_add(1, std::memory_order_relaxed);
110 }
111 }
112
114 template <typename Ready> bool wait_for(Ready&& ready, Clock::duration timeout) {
115 return wait_until(static_cast<Ready&&>(ready), Clock::now() + timeout);
116 }
117
120 std::uint64_t posts() const noexcept {
121 return posts_.load(std::memory_order_relaxed);
122 }
123 std::uint64_t wakes() const noexcept {
124 return wakes_.load(std::memory_order_relaxed);
125 }
126
127 bool consumer_parked() const noexcept {
128 return parked_.load(std::memory_order_relaxed);
129 }
130
131 private:
132 void post() noexcept {
133#if defined(__APPLE__)
134 (void)dispatch_semaphore_signal(semaphore_);
135#else
136 semaphore_.release();
137#endif
138 }
139
140 void park(Clock::duration timeout) {
141#if defined(__APPLE__)
142 const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
143 (void)dispatch_semaphore_wait(semaphore_, dispatch_time(DISPATCH_TIME_NOW, ns));
144#else
145 (void)semaphore_.try_acquire_for(timeout);
146#endif
147 }
148
149 std::atomic<bool> parked_{false};
150 std::atomic<std::uint64_t> posts_{0};
151 std::atomic<std::uint64_t> wakes_{0};
152#if defined(__APPLE__)
153 dispatch_semaphore_t semaphore_ = nullptr;
154#else
155 std::counting_semaphore<> semaphore_{0};
156#endif
157};
158
159} // namespace pulp::runtime
WakeSignal() noexcept
Definition wake_signal.hpp:60
std::uint64_t posts() const noexcept
Diagnostics: kernel posts the producers made, and times the consumer woke from a park (a post or a ti...
Definition wake_signal.hpp:120
void notify() noexcept
Producer side, realtime safe. Call after the work is published.
Definition wake_signal.hpp:81
WakeSignal(const WakeSignal &)=delete
~WakeSignal()
Definition wake_signal.hpp:67
std::uint64_t wakes() const noexcept
Definition wake_signal.hpp:123
bool wait_for(Ready &&ready, Clock::duration timeout)
wait_until() with a deadline timeout from now.
Definition wake_signal.hpp:114
std::chrono::steady_clock Clock
Definition wake_signal.hpp:58
bool wait_until(Ready &&ready, Clock::time_point deadline)
Consumer side.
Definition wake_signal.hpp:92
bool consumer_parked() const noexcept
Diagnostics: whether the consumer is currently parked (or about to be).
Definition wake_signal.hpp:127
WakeSignal & operator=(const WakeSignal &)=delete
Definition generated_event_source.hpp:33