3 #include "prepared_coro.h"
7 #include <unordered_map>
23 class abstract_condition_awaiter {
25 prepared_coro notify() noexcept {
26 n.store(
true, std::memory_order_relaxed);
28 return prepared_coro(_h);
30 virtual bool test(
const void *addr) noexcept = 0;
31 virtual const void *get_addr() noexcept = 0;
32 virtual ~abstract_condition_awaiter() = default;
33 abstract_condition_awaiter *_next =
nullptr;
35 std::coroutine_handle<> _h;
36 std::atomic<
bool> n = {
false};
44 constexpr
bool isPrime(std::size_t n)
47 if (n <= 1)
return false;
48 if (n <= 3)
return true;
52 if (n%2 == 0 || n%3 == 0)
return false;
54 for (std::size_t i=5; i*i<=n; i=i+6)
55 if (n%i == 0 || n%(i+2) == 0)
61 constexpr std::size_t next_prime_twice_than(std::size_t x) {
77 bool reg_awaiter(
const void *addr, abstract_condition_awaiter *awt) {
78 trace::awaiting_ref(*
reinterpret_cast<const std::uintptr_t *
>(addr), awt);
79 std::lock_guard _(_mx);
80 if (awt->test(addr))
return false;
81 insert_item(addr, awt);
85 template<std::invocable<prepared_coro> Fn>
86 void notify_addr(
const void *addr, Fn &&fn) {
87 auto done_lst = get_list_to_notify(addr);
90 done_lst = done_lst->_next;
95 static awaiter_map instance;
100 std::vector<abstract_condition_awaiter *> _hashtable;
101 std::size_t _count_keys;
104 abstract_condition_awaiter * &map_address(
const void *address) {
105 std::uintptr_t n = std::bit_cast<std::uintptr_t>(address);
106 auto pos = n % _hashtable.size();
107 return _hashtable[pos];
110 void insert_item(
const void *address, abstract_condition_awaiter *awt) {
112 auto &b = map_address(address);
115 if (awt->_next ==
nullptr) ++_count_keys;
118 abstract_condition_awaiter *get_list_to_notify(
const void *addr) {
119 abstract_condition_awaiter *new_lst =
nullptr;
120 abstract_condition_awaiter *done_lst =
nullptr;
122 std::lock_guard _(_mx);
123 if (_hashtable.size() == 0)
return nullptr;
124 auto &b = map_address(addr);
125 abstract_condition_awaiter *lst = b;
126 if (lst ==
nullptr)
return nullptr;
127 while (lst !=
nullptr) {
130 if (tmp->test(addr)) {
131 tmp->_next = done_lst;
134 tmp->_next = new_lst;
148 auto sz =_hashtable.size();
149 if (_count_keys * 2 >= sz) {
150 std::size_t newsz = _details::next_prime_twice_than(std::max<std::size_t>(sz, 16));
151 std::vector<abstract_condition_awaiter *> tmp(newsz,
nullptr);
152 std::swap(tmp, _hashtable);
158 insert_item(x->get_addr(), x);
168 inline awaiter_map awaiter_map::instance;
194 template<
typename T,
typename Pred>
195 class condition :
public _details::abstract_condition_awaiter {
198 static_assert(std::is_invocable_r_v<bool, Pred, T &> || std::is_invocable_r_v<bool, Pred>);
205 condition(T &var, Pred &&pred):_variable(var),_predicate(std::forward<Pred>(pred)) {}
215 if constexpr (std::is_invocable_r_v<bool, Pred>) {
218 return _predicate(_variable);
230 if (_exp) std::rethrow_exception(_exp);
238 return _details::awaiter_map::instance.reg_awaiter(&_variable,
this);
251 _details::awaiter_map::instance.reg_awaiter(&_variable,
this)) {
262 std::exception_ptr _exp;
264 virtual bool test(
const void *addr) noexcept
override{
268 _exp = std::current_exception();
272 virtual const void *get_addr() noexcept
override {
294 _details::awaiter_map::instance.notify_addr(&var,[](
auto){});
312 template<
typename T, std::invocable<prepared_coro> Fn>
314 _details::awaiter_map::instance.notify_addr(&var,
scheduler);
330 template<
typename T,
typename Pred>
bool await_suspend(std::coroutine_handle<> h)
co_await support
condition(T &var, Pred &&pred)
constructor
T & await_resume() const
co_await support
T & wait()
Synchronous waiting, allows to condition be used in normal function.
bool await_ready() const
co_await support
void condition_sync_wait(T &var, Pred &&pred)
Perform synchronous waiting with condition.
void notify_condition(const T &var) noexcept
notifies variable about change in the condition.