29template <
typename T>
class Futex {
35 Futex(std::atomic<T> &futex) : futex_(futex) {}
37 ~Futex() { notify_all_(-EIDRM); }
39 CONDY_DELETE_COPY_MOVE(
Futex);
42 struct [[nodiscard]] WaitSender;
53 WaitSender
wait(T old)
noexcept {
return {*
this, old}; }
60 WaitFinishHandleBase *handle =
nullptr;
62 std::lock_guard<std::mutex> lock(mutex_);
63 handle = wait_awaiters_.pop_front();
66 handle->set_result(0);
78 class WaitFinishHandleBase;
79 template <
typename Receiver>
class WaitFinishHandle;
81 bool cancel_wait_(WaitFinishHandleBase *handle)
noexcept {
82 std::lock_guard<std::mutex> lock(mutex_);
83 return wait_awaiters_.remove(handle);
86 int32_t request_wait_(WaitFinishHandleBase *handle, T old)
noexcept {
87 std::lock_guard<std::mutex> lock(mutex_);
88 auto val = futex_.load(std::memory_order_relaxed);
92 wait_awaiters_.push_back(handle);
97 void notify_all_(int32_t result)
noexcept {
100 std::lock_guard<std::mutex> lock(mutex_);
101 handles = std::move(wait_awaiters_);
103 while (
auto *handle = handles.pop_front()) {
104 handle->set_result(result);
111 detail::IntrusiveDoubleList<WaitFinishHandleBase,
112 &WaitFinishHandleBase::link_entry_>;
114 mutable std::mutex mutex_;
115 HandleList wait_awaiters_;
116 std::atomic<T> &futex_;
120class Futex<T>::WaitFinishHandleBase :
public detail::WorkInvoker {
122 void schedule() noexcept {
123 assert(runtime_ !=
nullptr);
124 runtime_->schedule_internal(
this);
127 void set_result(int32_t result)
noexcept { result_ = result; }
130 detail::DoubleLinkEntry link_entry_;
133 Runtime *runtime_ =
nullptr;
134 int32_t result_ = -ENOTRECOVERABLE;
138template <
typename Receiver>
139class Futex<T>::WaitFinishHandle
140 :
public detail::InvokerAdapter<WaitFinishHandle<Receiver>,
141 WaitFinishHandleBase> {
143 using Base = detail::InvokerAdapter<WaitFinishHandle, WaitFinishHandleBase>;
145 WaitFinishHandle(
Futex &futex, Receiver receiver)
146 : futex_(futex), receiver_(std::move(receiver)) {}
148 void start(Runtime *runtime, T old)
noexcept {
149 this->runtime_ = runtime;
150 int32_t r = futex_.request_wait_(
this, old);
152 std::move(receiver_)(r);
155 runtime->pend_work_internal();
157 auto stop_token = receiver_.get_stop_token();
158 if (stop_token.stop_possible()) {
159 stop_callback_.emplace(std::move(stop_token), Cancellation{
this});
163 void invoke() noexcept {
164 stop_callback_.reset();
165 assert(this->runtime_ !=
nullptr);
166 this->runtime_->resume_work_internal();
167 std::move(receiver_)(this->result_);
171 void cancel_() noexcept {
172 if (futex_.cancel_wait_(
this)) {
174 this->result_ = -ECANCELED;
175 assert(this->runtime_ !=
nullptr);
176 this->runtime_->schedule_internal(
this);
180 struct Cancellation {
181 WaitFinishHandle *self;
182 void operator()() noexcept { self->cancel_(); }
185 using StopCallbackType =
186 detail::stop_callback_t<detail::stop_token_t<Receiver>, Cancellation>;
191 std::optional<StopCallbackType> stop_callback_;
194template <
typename T>
struct Futex<T>::WaitSender {
196 using CondySender = void;
197 using ReturnType = int32_t;
199 WaitSender(
Futex &futex, T old) : futex_(futex), old_(old) {}
201 template <
typename Receiver>
auto connect_impl(Receiver receiver)
noexcept {
202 return OperationState<Receiver>(futex_, old_, std::move(receiver));
206 template <
typename Receiver>
207 class OperationState :
public WaitFinishHandle<Receiver> {
209 using Base = WaitFinishHandle<Receiver>;
210 OperationState(Futex &futex, T old, Receiver receiver)
211 : Base(futex, std::move(receiver)), old_(old) {}
213 void start(
unsigned int )
noexcept {
214 auto *runtime = detail::Context::current().runtime();
215 Base::start(runtime, old_);
User-space "futex" implementation for efficient synchronization between coroutines.
WaitSender wait(T old) noexcept
Wait if the futex value equals to the specified old value. The awaiting coroutine will be suspended u...
Futex(std::atomic< T > &futex)
Construct a new Futex object.
void notify_all() noexcept
Notify all awaiting coroutines.
void notify_one() noexcept
Notify one awaiting coroutine, if any.
Intrusive single-linked and double-linked list implementations.
Polymorphic invocation utilities.
The main namespace for the Condy library.
Runtime type for running the io_uring event loop.
Internal utility classes and functions used by Condy.