Condy v1.9
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
futex.hpp
Go to the documentation of this file.
1
6
7#pragma once
8
14#include "condy/runtime.hpp"
15#include <atomic>
16#include <cerrno>
17#include <optional>
18#ifdef CONDY_DETAIL_HAS_EXECUTION
20#endif
21
22namespace condy {
23
33template <typename T> class Futex {
34public:
39 Futex(std::atomic<T> &futex) : futex_(futex) {}
40
41 ~Futex() { notify_all_(-EIDRM); }
42
43 CONDY_DELETE_COPY_MOVE(Futex);
44
45private:
46 struct [[nodiscard]] WaitSenderImpl;
47
48public:
49#ifdef CONDY_DETAIL_HAS_EXECUTION
50 using WaitSender = detail::StandardSender<WaitSenderImpl>;
51#else
52 using WaitSender = WaitSenderImpl;
53#endif
64 WaitSender wait(T old) noexcept { return {*this, old}; }
65
70 void notify_one() noexcept {
71 WaitFinishHandleBase *handle = nullptr;
72 {
73 std::lock_guard<std::mutex> lock(mutex_);
74 handle = wait_awaiters_.pop_front();
75 }
76 if (handle) {
77 handle->set_result(0);
78 handle->schedule();
79 }
80 }
81
86 void notify_all() noexcept { notify_all_(0); }
87
88private:
89 class WaitFinishHandleBase;
90 template <typename Receiver> class WaitFinishHandle;
91
92 bool cancel_wait_(WaitFinishHandleBase *handle) noexcept {
93 std::lock_guard<std::mutex> lock(mutex_);
94 return wait_awaiters_.remove(handle);
95 }
96
97 int32_t request_wait_(WaitFinishHandleBase *handle, T old) noexcept {
98 std::lock_guard<std::mutex> lock(mutex_);
99 auto val = futex_.load(std::memory_order_relaxed);
100 if (val != old) {
101 return 0; // No need to wait
102 }
103 wait_awaiters_.push_back(handle);
104 return -EAGAIN; // Need to wait
105 }
106
107private:
108 void notify_all_(int32_t result) noexcept {
109 HandleList handles;
110 {
111 std::lock_guard<std::mutex> lock(mutex_);
112 handles = std::move(wait_awaiters_);
113 }
114 while (auto *handle = handles.pop_front()) {
115 handle->set_result(result);
116 handle->schedule();
117 }
118 }
119
120private:
121 using HandleList =
122 detail::IntrusiveDoubleList<WaitFinishHandleBase,
123 &WaitFinishHandleBase::link_entry_>;
124
125 mutable std::mutex mutex_;
126 HandleList wait_awaiters_;
127 std::atomic<T> &futex_;
128};
129
130template <typename T>
131class Futex<T>::WaitFinishHandleBase : public detail::WorkInvoker {
132public:
133 void schedule() noexcept {
134 assert(runtime_ != nullptr);
135 runtime_->schedule_internal(this);
136 }
137
138 void set_result(int32_t result) noexcept { result_ = result; }
139
140public:
141 detail::DoubleLinkEntry link_entry_;
142
143protected:
144 Runtime *runtime_ = nullptr;
145 int32_t result_ = -ENOTRECOVERABLE; // Internal error if not set
146};
147
148template <typename T>
149template <typename Receiver>
150class Futex<T>::WaitFinishHandle
151 : public detail::InvokerAdapter<WaitFinishHandle<Receiver>,
152 WaitFinishHandleBase> {
153public:
154 using Base = detail::InvokerAdapter<WaitFinishHandle, WaitFinishHandleBase>;
155
156 WaitFinishHandle(Futex &futex, Receiver receiver)
157 : futex_(futex), receiver_(std::move(receiver)) {}
158
159 void start(Runtime *runtime, T old) noexcept {
160 this->runtime_ = runtime;
161 int32_t r = futex_.request_wait_(this, old);
162 if (r != -EAGAIN) {
163 std::move(receiver_)(r);
164 return;
165 }
166 runtime->pend_work_internal();
167
168 auto stop_token = receiver_.get_stop_token();
169 if (stop_token.stop_possible()) {
170 stop_callback_.emplace(std::move(stop_token), Cancellation{this});
171 }
172 }
173
174 void invoke() noexcept {
175 stop_callback_.reset();
176 assert(this->runtime_ != nullptr);
177 this->runtime_->resume_work_internal();
178 std::move(receiver_)(this->result_);
179 }
180
181private:
182 void cancel_() noexcept {
183 if (futex_.cancel_wait_(this)) {
184 // Successfully canceled
185 this->result_ = -ECANCELED;
186 assert(this->runtime_ != nullptr);
187 this->runtime_->schedule_internal(this);
188 }
189 }
190
191 struct Cancellation {
192 WaitFinishHandle *self;
193 void operator()() noexcept { self->cancel_(); }
194 };
195
196 using StopCallbackType =
197 detail::stop_callback_t<detail::stop_token_t<Receiver>, Cancellation>;
198
199private:
200 Futex &futex_;
201 Receiver receiver_;
202 std::optional<StopCallbackType> stop_callback_;
203};
204
205template <typename T> struct Futex<T>::WaitSenderImpl {
206public:
207 using CondySender = void;
208 using ReturnType = int32_t;
209
210 WaitSenderImpl(Futex &futex, T old) : futex_(futex), old_(old) {}
211
212 template <typename Receiver> auto connect_impl(Receiver receiver) noexcept {
213 return OperationState<Receiver>(futex_, old_, std::move(receiver));
214 }
215
216private:
217 template <typename Receiver>
218 class OperationState : public WaitFinishHandle<Receiver> {
219 public:
220 using Base = WaitFinishHandle<Receiver>;
221 OperationState(Futex &futex, T old, Receiver receiver)
222 : Base(futex, std::move(receiver)), old_(old) {}
223
224 void start(unsigned int /*flags*/) noexcept {
225 auto *runtime = detail::Context::current().runtime();
226 Base::start(runtime, old_);
227 }
228
229 private:
230 T old_;
231 };
232
233private:
234 Futex &futex_;
235 T old_;
236};
237
238} // namespace condy
User-space "futex" implementation for efficient synchronization between coroutines.
Definition futex.hpp:33
WaitSender wait(T old) noexcept
Wait if the futex value equals to the specified old value. The awaiting coroutine will be suspended u...
Definition futex.hpp:64
Futex(std::atomic< T > &futex)
Construct a new Futex object.
Definition futex.hpp:39
void notify_all() noexcept
Notify all awaiting coroutines.
Definition futex.hpp:86
void notify_one() noexcept
Notify one awaiting coroutine, if any.
Definition futex.hpp:70
Internal sender/receiver adapters for std::execution integration.
Detection and aliasing of the available std::execution backend.
Intrusive single-linked and double-linked list implementations.
Polymorphic invocation utilities.
The main namespace for the Condy library.
Definition condy.hpp:38
Runtime type for running the io_uring event loop.
Internal utility classes and functions used by Condy.