Skip to content
LeviMC
English
AutoLightDark
LeviLamina
Loading...
Searching...
No Matches
ConditionVariable.h
1#pragma once
2
3#include <chrono>
4#include <coroutine>
5#include <functional>
6#include <limits>
7#include <memory>
8#include <mutex>
9#include <optional>
10#include <type_traits>
11#include <variant>
12
13#include "ll/api/base/CompilerPredefine.h"
14#include "ll/api/coro/Executor.h"
15#include "ll/api/data/IntrusiveList.h"
16
17namespace ll::coro {
18class ConditionVariable {
19 struct Waiter : data::IntrusiveListNode<> {
20 ConditionVariable& cv;
21 bool (*pred)(Waiter&);
22 ExecutorRef exec{};
23 std::coroutine_handle<> handle;
24 std::shared_ptr<data::CancellableCallback> timer;
25 bool fired{};
26 bool result{true};
27
28 Waiter(ConditionVariable& cv, bool (*pred)(Waiter&)) noexcept : cv(cv), pred(pred) {}
29
30 ~Waiter() {
31 if (is_linked()) {
32 std::lock_guard lock{cv.mutex};
33 unlink();
34 if (timer) timer->cancel();
35 }
36 }
37
38 bool satisfied() { return pred && pred(*this); }
39
40 void onTimeout() {
41 std::unique_lock lock{cv.mutex};
42 bool linked = is_linked();
43 if (linked) unlink();
44 fired = true;
45 result = satisfied();
46 lock.unlock();
47 if (linked) handle.resume();
48 }
49
50 bool suspend(std::coroutine_handle<> h, std::optional<Duration> dur) {
51 handle = h;
52 if (dur) timer = exec->executeAfter([this] { onTimeout(); }, *dur);
53 std::lock_guard lock{cv.mutex};
54 if (fired) return false;
55 if (satisfied() && (!timer || timer->cancel())) return false;
56 cv.waiters.push_back(*this);
57 return true;
58 }
59 };
60
61 template <class Pred>
62 class Awaiter : Waiter {
63 static constexpr bool hasPred = !std::is_same_v<Pred, std::monostate>;
64
65 std::optional<Duration> dur;
66 LL_NO_UNIQUE_ADDRESS Pred pred;
67
68 static bool test(Waiter& w) {
69 if constexpr (hasPred) return std::invoke(static_cast<Awaiter&>(w).pred);
70 else return false;
71 }
72
73 public:
74 Awaiter(ConditionVariable& cv, Pred pred, std::optional<Duration> dur)
75 : Waiter(cv, hasPred ? &test : nullptr),
76 dur(dur),
77 pred(std::move(pred)) {}
78
79 void setExecutor(ExecutorRef ex) { this->exec = ex; }
80
81 bool await_ready() {
82 if (this->satisfied()) return true;
83 if (!dur || *dur > Duration{0}) return false;
84 this->result = false;
85 return true;
86 }
87 bool await_suspend(std::coroutine_handle<> h) { return this->suspend(h, dur); }
88 bool await_resume() const noexcept { return this->result; }
89 };
90
91 std::mutex mutex;
93
94 template <class Pred>
95 auto make(Pred&& pred, std::optional<Duration> dur = {}) {
96 return Awaiter<std::decay_t<Pred>>{*this, std::forward<Pred>(pred), dur};
97 }
98
99 size_t notify(size_t limit, bool inplace) {
101 size_t count = 0;
102 {
103 std::lock_guard lock{mutex};
104 for (auto it = waiters.begin(); it != waiters.end() && count < limit;) {
105 auto& w = *it;
106 ++it;
107 if (w.pred && !w.pred(w)) continue;
108 if (w.timer && !w.timer->cancel()) continue;
109 woken.push_back(w);
110 ++count;
111 }
112 }
113 while (!woken.empty()) {
114 auto& w = woken.front();
115 woken.pop_front();
116 if (inplace) w.handle.resume();
117 else w.exec->execute(w.handle);
118 }
119 return count;
120 }
121
122public:
123 ConditionVariable() = default;
124
125 ConditionVariable(ConditionVariable const&) = delete;
126 ConditionVariable& operator=(ConditionVariable const&) = delete;
127
128 [[nodiscard]] auto wait() { return make(std::monostate{}); }
129
130 template <std::predicate Pred>
131 [[nodiscard]] auto wait(Pred&& pred) {
132 return make(std::forward<Pred>(pred));
133 }
134
135 template <class R, class P>
136 [[nodiscard]] auto waitFor(std::chrono::duration<R, P> dur) {
137 return make(std::monostate{}, std::chrono::duration_cast<Duration>(dur));
138 }
139
140 template <class R, class P, std::predicate Pred>
141 [[nodiscard]] auto waitFor(std::chrono::duration<R, P> dur, Pred&& pred) {
142 return make(std::forward<Pred>(pred), std::chrono::duration_cast<Duration>(dur));
143 }
144
145 template <class C, class D, class... Pred>
146 [[nodiscard]] auto waitUntil(std::chrono::time_point<C, D> time, Pred&&... pred) {
147 return waitFor(time - C::now(), std::forward<Pred>(pred)...);
148 }
149
150 bool notifyOne(bool inplace = false) { return notify(1, inplace) != 0; }
151
152 size_t notifyAll(bool inplace = false) { return notify(std::numeric_limits<size_t>::max(), inplace); }
153};
154} // namespace ll::coro
Definition IntrusiveList.h:38
Definition IntrusiveList.h:64