7 #include <condition_variable>
31 constexpr T &operator* ()
const {
return *_ptr;}
32 constexpr T *operator->()
const {
return _ptr;}
33 constexpr
operator T &()
const {
return *_ptr;}
34 constexpr T *get()
const {
return *_ptr;}
36 explicit constexpr
operator bool()
const {
return _ptr !=
nullptr;}
45 return a._ptr == b._ptr;
53 concept is_pointer_like = (requires(T v) {
55 } && std::is_move_constructible_v<T>);
63 struct unwrap_pointer_like_def {
using type = T;};
64 template<is_po
inter_like T>
65 struct unwrap_pointer_like_def<T> {
using type = pointer_value<T>;};
67 using unwrap_pointer_like=
typename unwrap_pointer_like_def<T>::type;
84 {v.begin() == v.end()};
85 {v.begin()}->iterator_type;
86 {v.end()}->iterator_type;
87 typename std::decay_t<T>::value_type;
114 :
all_of(lst.begin(), lst.end()) {}
120 template<container_type Container>
122 :
all_of(cont.begin(), cont.end()) {}
130 template<iterator_type Iter>
135 if constexpr(is_pointer_like<decltype(*from)>) {
136 (*from)->then([
this]{finish();});
138 (*from).then([
this]{finish();});
148 std::atomic<unsigned int> _count = {1};
157 all_of(std::initializer_list<X>) -> all_of<X>;
158 template<container_type T>
159 all_of(T) -> all_of<unwrap_pointer_like<typename T::value_type> >;
181 using future_ptr = std::add_pointer_t<T>;
184 using result_notify_type =
typename result_promise_type::notify;
196 using iterator_category = std::random_access_iterator_tag;
197 using value_type =
typename result_future_type::value_type;
198 using reference = std::add_lvalue_reference<result_future_type>;
199 using difference_type = std::ptrdiff_t;
206 _tmp._prom = _tmp._fut.get_promise();
207 _owner->charge(
this);
211 void operator()(result_promise_type prom)
const {
212 _tmp._prom = std::move(prom);
213 _owner->charge(
this);
230 return static_cast<std::ptrdiff_t
>(_index)-
static_cast<std::ptrdiff_t
>(other._index);
240 result_promise_type _prom;
241 result_future_type _fut;
243 tmp(tmp &&other):_prom(std::move(other._prom)) {}
244 tmp &operator=(tmp &&other) {
245 _prom = std::move(other._prom);
250 when_each *_owner =
nullptr;
251 std::size_t _index = 0;
253 friend class when_each;
276 template<container_type Container>
285 template<iterator_type Iter>
287 std::lock_guard _(_mx);
289 if constexpr(is_pointer_like<decltype(*from)>) {
290 future_ptr ptr = &(*(*from));
291 _awaiting.push_back(ptr);
292 trace::awaiting_ref(*ptr,
this);
294 future_ptr ptr = &(*from);
295 _awaiting.push_back(ptr);
296 trace::awaiting_ref(*ptr,
this);
300 _sep = _awaiting.size();
301 for (std::size_t i = 0; i < _sep; ++i) {
302 future_ptr ptr = _awaiting[i];
303 if (!ptr->set_callback([
this, ptr]{
304 return finish(ptr).symmetric_transfer();
306 _awaiting.push_back(ptr);
319 std::vector<future_ptr> _awaiting;
320 std::basic_string<const iterator *> _iters;
322 mutable std::mutex _mx;
323 std::condition_variable *_cond =
nullptr;
325 auto remain_pending() {
326 return 2*_sep - _awaiting.size();
329 result_notify_type finish(future_ptr ptr) {
330 std::lock_guard _(_mx);
331 auto idx = _awaiting.size() - _sep;
332 _awaiting.push_back(ptr);
333 result_promise_type proms;
334 _iters.erase( std::remove_if(_iters.begin(), _iters.end(), [&](
const iterator *it){
335 if (it->_index == idx) {
336 proms += it->_tmp._prom;
341 if (_cond) _cond->notify_all();;
342 if (proms)
return ptr->forward_to(proms);
347 bool charge(
const iterator *it) {
348 result_notify_type ntf;
349 std::lock_guard _(_mx);
350 auto idx = it->_index+_sep;
351 if (idx < _awaiting.size()) {
352 ntf = _awaiting[idx]->forward_to(it->_tmp._prom);
355 _iters.push_back(it);
360 if (remain_pending()) {
361 std::unique_lock lk(_mx);
362 std::size_t found_remain = 0;
363 for (std::size_t i = 0; i < _sep; ++i) {
364 if (_awaiting[i]->set_callback([]{})) ++found_remain;
366 if (remain_pending() != found_remain) {
367 std::condition_variable cond;
370 return remain_pending() == found_remain;
379 when_each(std::initializer_list<X>) -> when_each<X>;
380 template<container_type T>
381 when_each(T) -> when_each<unwrap_pointer_like<typename T::value_type> >;
400 :
any_of(lst.begin(), lst.end()) {}
402 template<container_type Container>
404 :
any_of(cont.begin(), cont.end()) {}
406 template<iterator_type Iter>
407 any_of(Iter from, Iter to)
408 :_each_of(std::move(from), std::move(to)) {
410 _iter = _each_of.begin();
411 _iter(this->get_promise());
424 template<container_type T>
441 using std::deque<T>::deque;
451 template<std::invocable Fn>
453 static_assert(std::is_same_v<std::invoke_result_t<Fn>, T>,
"Return value type mismatch");
466 template<std::invocable Fn>
468 static_assert(std::is_same_v<std::invoke_result_t<Fn>, T>,
"Return value type mismatch");
472 using std::deque<T>::push_back;
473 using std::deque<T>::push_front;
476 template<
typename X,
typename Y> requires std::constructible_from<T, async<X,Y> >
478 (*this) << [&]()->T{
return x;};
481 template<
typename X,
typename Y> requires std::constructible_from<T, async<X,Y> >
483 [&]()->T{
return x;} >> (*this);
all_of(std::initializer_list< pointer_wrapper< T > > lst)
construct using initializer list
all_of(Iter from, Iter to)
construct using iterator pair
all_of(Container &&cont)
construct using a container
this awaitable is resolved, when all objects specified by constructor are resolved
Awaitable returns result of a first complete object.
COROUTINE: Coroutine for asynchronous operation.
promise_t get_promise()
Retrieve promise and begin evaluation.
Contains future value of T, can be co_awaited in coroutine.
Wraps object into pointer.
friend void operator>>(Fn &&fn, task_list &lst)
Redirect return value to the task list.
requires std::constructible_from< T, async< X, Y > > void push_back(async< X, Y > x)
Start async function and store result to the list (as last item)
requires std::constructible_from< T, async< X, Y > > void push_front(async< X, Y > x)
Start async function and store result to the list (as first item)
void operator<<(Fn &&fn)
Redirect return value to the task list.
helps to create task list: list of started task as list of futures
bool operator==(const iterator &other) const
comparison
std::ptrdiff_t operator-(const iterator &other) const
difference
iterator operator++(int)
go to next item
iterator()=default
default construct - note such iterator cannot be used for iterating
iterator & operator+=(std::ptrdiff_t n)
skip n items
result_future_type & operator*() const
retrieve item. Note that result is awaitable reference. You need co_await the result;
iterator & operator-=(std::ptrdiff_t n)
skip n items
iterator & operator--()
go to previous item
iterator operator--(int)
go to previous item
iterator & operator++()
go to next item
iterator for when_each - it always return awaitable reference (future)
when_each(Iter from, Iter to)
construct using iterator pair
iterator end()
retrieve end iterator
iterator begin()
retrieve iterator
when_each(Container &cont)
construct using a container
when_each(std::initializer_list< pointer_wrapper< T > > lst)
construct using initializer list
Process all futures in order of completion.
concept iterator_type
any iterator
std::decay_t< decltype(*std::declval< T >())> pointer_value
determines type of pointer value
concept container_type
any container (must have begin and end)