Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
buffers.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
6#include "async_task.hpp"
8#include "async_traits.hpp"
9#include "epoch_context.hpp"
10#include "shared_storage.hpp"
12
13#include <atomic>
14#include <concepts>
15#include <coroutine>
16#include <cstddef>
17#include <memory>
18#include <mutex>
19#include <new>
20#include <optional>
21#include <stdexcept>
22#include <tuple>
23#include <type_traits>
24#include <utility>
25
26namespace uni20::async
27{
28
29template <typename T> class Async;
30
31template <typename T> class ReadMaybeAwaiter;
32template <typename T> class ReadOrCancelAwaiter;
33template <typename T> class OwningReadAccessProxy;
34template <typename T> class OwningReadAwaiter;
35
53template <typename T> class ReadBuffer { //}: public AsyncAwaiter {
54 public:
55 using value_type = T;
56
59 ReadBuffer(EpochContextReader<T> reader) : reader_(std::move(reader)) {}
60
63 ReadBuffer(ReadBuffer const& other) : reader_(other.reader_) { this->copy_exception_sink_from(other); }
64
65 // No copy ctor here, although we could add one
66 ReadBuffer& operator=(ReadBuffer const&) = delete;
67
70 ReadBuffer(ReadBuffer&& other) noexcept : reader_(std::move(other.reader_))
71 {
72 this->move_exception_sink_from(other);
73 }
74
78 ReadBuffer& operator=(ReadBuffer&& other) noexcept
79 {
80 if (this != &other)
81 {
82 this->unregister_exception_sink(false);
83 reader_ = std::move(other.reader_);
84 this->move_exception_sink_from(other);
85 }
86 return *this;
87 }
88
90 ~ReadBuffer() noexcept { this->unregister_exception_sink(true); }
91
92#if UNI20_DEBUG_DAG
94 NodeInfo const* node() const { return reader_.node(); }
96 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
97#endif
98
101 ReadMaybeAwaiter<T const&> maybe() &;
102
105 ReadMaybeAwaiter<T> maybe() &&;
106
110 ReadOrCancelAwaiter<T const&> or_cancel() &;
111
115 ReadOrCancelAwaiter<T> or_cancel() &&;
116
119 [[nodiscard]] bool await_ready() const noexcept { return reader_.ready(); }
120
123 void await_suspend(BasicTask&& t) noexcept
124 {
125 TRACE_MODULE(ASYNC, "ReadBuffer::await_suspend()", this, t.coroutine_handle());
126 reader_.suspend(std::move(t));
127 }
128
131 [[nodiscard]] T const& await_resume() const& { return reader_.data(); }
132
134 [[nodiscard]] OwningReadAccessProxy<T> await_resume() &&;
135
143 void release() noexcept { reader_.release(); }
144
147 ReadBuffer&& transfer() & noexcept { return std::move(*this); }
148
151 ReadBuffer&& transfer() && noexcept { return std::move(*this); }
152
154 void wait() const { this->reader_.wait(); }
155
158 void wait(IAsyncScheduler& sched) const { this->reader_.wait(sched); }
159
160 // T get_wait() && { return T(reader_.get_wait()); } // TODO: can this use move semantics?
163 [[nodiscard]] T const& get_wait() const { return reader_.get_wait(); }
164
168 [[nodiscard]] T const& get_wait(IAsyncScheduler& sched) const { return reader_.get_wait(sched); }
169
171 auto operator co_await() & noexcept -> ReadBuffer& { return *this; }
172 auto operator co_await() const& noexcept -> ReadBuffer const& { return *this; }
173
175 auto operator co_await() && noexcept -> OwningReadAwaiter<T> { return OwningReadAwaiter<T>(std::move(reader_)); }
176
179 std::shared_ptr<EpochContext> epoch_context_shared() const noexcept { return reader_.epoch_context_shared(); }
180
184 void register_exception_sink(TaskPromiseBase& promise, bool explicit_sink) const
185 {
186 promise.register_exception_sink(exception_sink_, this->epoch_context_shared(), explicit_sink);
187 }
188
189 private:
192 void copy_exception_sink_from(ReadBuffer const& other)
193 {
194 if (other.exception_sink_.owner)
195 {
196 other.exception_sink_.owner->register_exception_sink(exception_sink_, other.exception_sink_.epoch,
197 other.exception_sink_.explicit_sink);
198 }
199 }
200
203 void move_exception_sink_from(ReadBuffer& other) noexcept
204 {
205 if (!other.exception_sink_.owner) return;
206 auto* owner = other.exception_sink_.owner;
207 auto epoch = std::move(other.exception_sink_.epoch);
208 bool const explicit_sink = other.exception_sink_.explicit_sink;
209 owner->unregister_exception_sink(other.exception_sink_, false);
210 owner->register_exception_sink(exception_sink_, std::move(epoch), explicit_sink);
211 }
212
215 void unregister_exception_sink(bool from_destructor) noexcept
216 {
217 if (!exception_sink_.owner) return;
218 exception_sink_.owner->unregister_exception_sink(exception_sink_, from_destructor);
219 }
220
221 EpochContextReader<T> reader_;
222 mutable TaskPromiseBase::ExceptionSinkNode exception_sink_{};
223};
224
227template <typename T> ReadBuffer<T>&& release(ReadBuffer<T>& in) { return std::move(in); }
228
231template <typename T> ReadBuffer<T>&& release(ReadBuffer<T>&& in) { return std::move(in); }
232
235template <typename T> class OwningReadAccessProxy {
236 public:
237 using value_type = T;
238
243 OwningReadAccessProxy& operator=(OwningReadAccessProxy&&) noexcept = delete;
244
247 [[nodiscard]] T const& get() const { return this->reader_.data(); }
248
249 operator T const&() const { return this->get(); }
250
251 T const* operator->() const { return std::addressof(this->get()); }
252
256 {
257 T value = this->get();
258 this->release();
259 return value;
260 }
261
263 void release() noexcept { this->reader_.release(); }
264
265 private:
268 explicit OwningReadAccessProxy(EpochContextReader<T>&& reader) : reader_(std::move(reader)) {}
269
270 friend class ReadBuffer<T>;
271 friend class OwningReadAwaiter<T>;
272 friend class ReadMaybeAwaiter<T>;
273 friend class ReadOrCancelAwaiter<T>;
274
275 EpochContextReader<T> reader_;
276};
277
279template <typename T> class OwningReadAwaiter {
280 public:
281 using value_type = T;
282
285 explicit OwningReadAwaiter(EpochContextReader<T>&& reader) : reader_(std::move(reader)) {}
286
289 [[nodiscard]] bool await_ready() const noexcept { return this->reader_.ready(); }
290
291#if UNI20_DEBUG_DAG
293 NodeInfo const* node() const { return reader_.node(); }
295 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
296#endif
297
300 void await_suspend(BasicTask&& t) noexcept
301 {
302 TRACE_MODULE(ASYNC, "OwningReadAwaiter::await_suspend()", this, t.coroutine_handle());
303 this->reader_.suspend(std::move(t));
304 }
305
308 [[nodiscard]] OwningReadAccessProxy<T> await_resume() { return OwningReadAccessProxy<T>(std::move(this->reader_)); }
309
310 private:
311 EpochContextReader<T> reader_;
312};
313
314template <typename T> [[nodiscard]] OwningReadAccessProxy<T> ReadBuffer<T>::await_resume() &&
315{
316 return OwningReadAccessProxy<T>(std::move(reader_));
317}
318
321template <typename T> class ReadMaybeAwaiter {
322 public:
323 using value_type = std::optional<OwningReadAccessProxy<T>>;
324
326 ReadMaybeAwaiter(ReadMaybeAwaiter&&) = default; // movable
327
330 [[nodiscard]] bool await_ready() const noexcept { return reader_.ready(); }
331
332#if UNI20_DEBUG_DAG
334 NodeInfo const* node() const { return reader_.node(); }
336 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
337#endif
338
341 void await_suspend(BasicTask&& t) noexcept
342 {
343 TRACE_MODULE(ASYNC, "ReadBuffer::await_suspend()", this, t.coroutine_handle());
344 this->reader_.suspend(std::move(t));
345 }
346
348 [[nodiscard]] value_type await_resume()
349 {
350 if (this->reader_.data_maybe() == nullptr)
351 {
352 this->reader_.release();
353 return std::nullopt;
354 }
355 return value_type(OwningReadAccessProxy<T>(std::move(this->reader_)));
356 }
357
358 private:
359 ReadMaybeAwaiter() = delete;
360 ReadMaybeAwaiter(ReadMaybeAwaiter const&) = delete;
361 ReadMaybeAwaiter& operator=(ReadMaybeAwaiter const&) = delete;
362 ReadMaybeAwaiter& operator=(ReadMaybeAwaiter&&) = delete;
363
364 ReadMaybeAwaiter(EpochContextReader<T>&& reader) : reader_(std::move(reader)) {}
365
366 friend ReadBuffer<T>;
367
368 EpochContextReader<T> reader_;
369};
370
373template <typename T> class ReadMaybeAwaiter<T const&> {
374 public:
375 using value_type = T const*;
376
378 ReadMaybeAwaiter(ReadMaybeAwaiter&&) = default; // movable
379
382 [[nodiscard]] bool await_ready() const noexcept { return reader_.ready(); }
383
384#if UNI20_DEBUG_DAG
386 NodeInfo const* node() const { return reader_.node(); }
388 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
389#endif
390
393 void await_suspend(BasicTask&& t) noexcept
394 {
395 TRACE_MODULE(ASYNC, "ReadBuffer::await_suspend()", this, t.coroutine_handle());
396 reader_.suspend(std::move(t));
397 }
398
400 [[nodiscard]] value_type await_resume() { return reader_.data_maybe(); }
401
402 private:
403 ReadMaybeAwaiter() = delete;
404 ReadMaybeAwaiter(ReadMaybeAwaiter const&) = delete;
405 ReadMaybeAwaiter& operator=(ReadMaybeAwaiter const&) = delete;
406 ReadMaybeAwaiter& operator=(ReadMaybeAwaiter&&) = delete;
407
408 ReadMaybeAwaiter(EpochContextReader<T>& reader) : reader_(reader) {}
409
410 friend ReadBuffer<T>;
411
412 EpochContextReader<T>& reader_;
413};
414
417template <typename T> class ReadOrCancelAwaiter {
418 public:
420
423
426 [[nodiscard]] bool await_ready() const noexcept
427 {
428 // we must suspend here, because it is a possible cancellation point
429 return false;
430 }
431
432#if UNI20_DEBUG_DAG
434 NodeInfo const* node() const { return reader_.node(); }
436 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
437#endif
438
441 void await_suspend(BasicTask&& t) noexcept { this->reader_.suspend(std::move(t)); }
442
446 [[nodiscard]] value_type await_resume()
447 {
448 T const* ptr = this->reader_.data_maybe();
449 if (ptr == nullptr)
450 {
451 this->reader_.release();
452 throw task_cancelled();
453 }
454 return value_type(std::move(this->reader_));
455 }
456
457 private:
458 ReadOrCancelAwaiter() = delete;
460 ReadOrCancelAwaiter& operator=(ReadOrCancelAwaiter const&) = delete;
461 ReadOrCancelAwaiter& operator=(ReadOrCancelAwaiter&&) = delete;
462
463 ReadOrCancelAwaiter(EpochContextReader<T>&& reader) : reader_(std::move(reader)) {}
464
465 friend ReadBuffer<T>;
466
467 EpochContextReader<T> reader_;
468};
469
472template <typename T> class ReadOrCancelAwaiter<T const&> {
473 public:
474 using value_type = T;
475
478
481 [[nodiscard]] bool await_ready() const noexcept
482 {
483 // we must suspend here, because it is a possible cancellation point
484 return false;
485 }
486
487#if UNI20_DEBUG_DAG
489 NodeInfo const* node() const { return reader_.node(); }
491 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Reader; }
492#endif
493
496 void await_suspend(BasicTask&& t) noexcept { reader_.suspend(std::move(t)); }
497
501 [[nodiscard]] T const& await_resume()
502 {
503 T const* ptr = reader_.data_maybe();
504 if (!ptr) throw task_cancelled();
505 return *ptr;
506 }
507
508 private:
509 ReadOrCancelAwaiter() = delete;
511 ReadOrCancelAwaiter& operator=(ReadOrCancelAwaiter const&) = delete;
512 ReadOrCancelAwaiter& operator=(ReadOrCancelAwaiter&&) = delete;
513
514 ReadOrCancelAwaiter(EpochContextReader<T>& reader) : reader_(reader) {}
515
516 friend ReadBuffer<T>;
517
518 EpochContextReader<T>& reader_;
519};
520
522{
523 return ReadMaybeAwaiter<T const&>(reader_);
524}
525
529template <typename T> ReadMaybeAwaiter<T> ReadBuffer<T>::maybe() && { return ReadMaybeAwaiter<T>(std::move(reader_)); }
530
532{
533 return ReadOrCancelAwaiter<T const&>(reader_);
534}
535
540{
541 return ReadOrCancelAwaiter<T>(std::move(reader_));
542}
543
544// Forward declaration of the proxy used for deferred writes
545template <typename T> class WriteAccessProxy;
546template <typename T> class OwningWriteAccessProxy;
547template <typename T> class OwningWriteAwaiter;
548template <typename T> class OwningStorageAccessProxy;
549template <typename T> class OwningStorageAwaiter;
550template <typename T> class OwningTakeAwaiter;
551template <typename T> class WriteAssignProxy;
552
553#if UNI20_DEBUG_ASYNC_TASKS
555struct WriteProxyLifetimeState
556{
557 std::atomic<bool> alive{true};
558};
559#endif
560
563template <typename T> class StorageAwaiter {
564 public:
567 StorageAwaiter(EpochContextWriter<T>* writer) : writer_(writer) {}
568
571 [[nodiscard]] bool await_ready() const noexcept { return writer_->ready(); }
572
573#if UNI20_DEBUG_DAG
575 NodeInfo const* node() const { return writer_ ? writer_->node() : nullptr; }
577 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
578#endif
579
582 void await_suspend(BasicTask&& t) noexcept
583 {
584 TRACE_MODULE(ASYNC, "StorageAwaiter::await_suspend()", this, t.coroutine_handle());
585 writer_->suspend(std::move(t));
586 }
587
591 {
592 writer_->resume();
593 return writer_->storage();
594 }
595
598 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
599 {
600 return writer_->epoch_context_shared();
601 }
602
603 private:
604 EpochContextWriter<T>* writer_; // by pointer, since we don't want to take ownership
605};
606
609template <typename T> class TakeAwaiter {
610 public:
613 TakeAwaiter(EpochContextWriter<T>* writer) : writer_(writer) {}
614
617 [[nodiscard]] bool await_ready() const noexcept { return writer_->ready(); }
618
619#if UNI20_DEBUG_DAG
621 NodeInfo const* node() const { return writer_ ? writer_->node() : nullptr; }
623 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
624#endif
625
628 void await_suspend(BasicTask&& t) noexcept
629 {
630 TRACE_MODULE(ASYNC, "TakeAwaiter::await_suspend()", this, t.coroutine_handle());
631 writer_->suspend(std::move(t));
632 }
633
636 [[nodiscard]] T await_resume()
637 {
638 writer_->resume();
639 return writer_->storage().take();
640 }
641
644 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
645 {
646 return writer_->epoch_context_shared();
647 }
648
649 private:
650 EpochContextWriter<T>* writer_; // by pointer, since we don't want to take ownership
651};
652
655template <typename T> class TakeReleaseAwaiter {
656 public:
659 TakeReleaseAwaiter(EpochContextWriter<T>* writer) : writer_(writer) {}
660
663 [[nodiscard]] bool await_ready() const noexcept { return writer_->ready(); }
664
665#if UNI20_DEBUG_DAG
667 NodeInfo const* node() const { return writer_ ? writer_->node() : nullptr; }
669 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
670#endif
671
674 void await_suspend(BasicTask&& t) noexcept
675 {
676 TRACE_MODULE(ASYNC, "TakeReleaseAwaiter::await_suspend()", this, t.coroutine_handle());
677 writer_->suspend(std::move(t));
678 }
679
682 [[nodiscard]] T await_resume()
683 {
684 writer_->resume();
685 T value = writer_->storage().take();
686 writer_->release();
687 return value;
688 }
689
692 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
693 {
694 return writer_->epoch_context_shared();
695 }
696
697 private:
698 EpochContextWriter<T>* writer_; // by pointer, since we don't want to take ownership
699};
700
703template <typename T> class OwningStorageAccessProxy {
704 public:
706
711 OwningStorageAccessProxy& operator=(OwningStorageAccessProxy&&) noexcept = delete;
712
715 shared_storage<T>& get() { return writer_.storage(); }
718 shared_storage<T> const& get() const { return writer_.storage(); }
719
720 operator shared_storage<T>&() { return this->get(); }
721 operator shared_storage<T> const&() const { return this->get(); }
722
723 shared_storage<T>* operator->() { return std::addressof(this->get()); }
724 shared_storage<T> const* operator->() const { return std::addressof(this->get()); }
725
728 T take() { return writer_.storage().take(); }
729
733 {
734 T value = this->take();
735 this->release();
736 return value;
737 }
738
740 void release() noexcept { writer_.release(); }
741
742 private:
745 explicit OwningStorageAccessProxy(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
746
747 friend class OwningStorageAwaiter<T>;
748
749 EpochContextWriter<T> writer_;
750};
751
754template <typename T> class OwningStorageAwaiter {
755 public:
757
760 explicit OwningStorageAwaiter(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
761
764 [[nodiscard]] bool await_ready() const noexcept { return writer_.ready(); }
765
766#if UNI20_DEBUG_DAG
768 NodeInfo const* node() const { return writer_.node(); }
770 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
771#endif
772
775 void await_suspend(BasicTask&& t) noexcept
776 {
777 TRACE_MODULE(ASYNC, "OwningStorageAwaiter::await_suspend()", this, t.coroutine_handle());
778 writer_.suspend(std::move(t));
779 }
780
784 {
785 writer_.resume();
786 return OwningStorageAccessProxy<T>(std::move(writer_));
787 }
788
791 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
792 {
793 return writer_.epoch_context_shared();
794 }
795
796 private:
797 EpochContextWriter<T> writer_;
798};
799
802template <typename T> class OwningTakeAwaiter {
803 public:
806 explicit OwningTakeAwaiter(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
807
810 [[nodiscard]] bool await_ready() const noexcept { return writer_.ready(); }
811
812#if UNI20_DEBUG_DAG
814 NodeInfo const* node() const { return writer_.node(); }
816 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
817#endif
818
821 void await_suspend(BasicTask&& t) noexcept
822 {
823 TRACE_MODULE(ASYNC, "OwningTakeAwaiter::await_suspend()", this, t.coroutine_handle());
824 writer_.suspend(std::move(t));
825 }
826
829 [[nodiscard]] T await_resume()
830 {
831 writer_.resume();
832 T value = writer_.storage().take();
833 writer_.release();
834 return value;
835 }
836
839 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
840 {
841 return writer_.epoch_context_shared();
842 }
843
844 private:
845 EpochContextWriter<T> writer_;
846};
847
859template <typename T> class WriteBuffer {
860 public:
861 using value_type = T;
862 using element_type = std::conditional_t<is_async_alias_v<T>, T const&, T&>;
863
866 explicit WriteBuffer(EpochContextWriter<T> writer) : writer_(std::move(writer)) {}
867
869 ~WriteBuffer() noexcept
870 {
871 this->invalidate_proxy_state();
872 this->unregister_exception_sink(true);
873 }
874
875 WriteBuffer(WriteBuffer const&) = delete;
877
880 WriteBuffer(WriteBuffer&& other) noexcept : writer_(std::move(other.writer_))
881 {
882 this->move_exception_sink_from(other);
883 other.invalidate_proxy_state();
884 }
885
890 {
891 if (this != &other)
892 {
893 this->invalidate_proxy_state();
894 this->unregister_exception_sink(false);
895 writer_ = std::move(other.writer_);
896 this->move_exception_sink_from(other);
897 this->reset_proxy_state();
898 other.invalidate_proxy_state();
899 }
900 return *this;
901 }
902
903#if UNI20_DEBUG_DAG
904 NodeInfo const* node() const { return writer_.node(); }
906 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
907#endif
908
911 [[nodiscard]] bool await_ready() const noexcept { return writer_.ready(); }
912
915 void await_suspend(BasicTask&& t) noexcept
916 {
917 TRACE_MODULE(ASYNC, "WriteBuffer::await_suspend()", this, t.coroutine_handle());
918 writer_.suspend(std::move(t));
919 }
920
924 {
925 writer_.resume();
926 return WriteAccessProxy<T>(&writer_
927#if UNI20_DEBUG_ASYNC_TASKS
928 ,
929 proxy_state_
930#endif
931 );
932 }
933
936 [[nodiscard]] WriteAccessProxy<T> await_resume() const&
937 {
938 auto* writer = const_cast<EpochContextWriter<T>*>(&writer_);
939 writer->resume();
940 return WriteAccessProxy<T>(writer
941#if UNI20_DEBUG_ASYNC_TASKS
942 ,
943 proxy_state_
944#endif
945 );
946 }
947
951 {
952 writer_.resume();
953 return OwningWriteAccessProxy<T>(std::move(writer_));
954 }
955
957 void release() noexcept
958 {
959 this->invalidate_proxy_state();
960 writer_.release();
961 }
962
965 WriteBuffer&& transfer() & noexcept
966 {
967 this->invalidate_proxy_state();
968 return std::move(*this);
969 }
970
973 WriteBuffer&& transfer() && noexcept
974 {
975 this->invalidate_proxy_state();
976 return std::move(*this);
977 }
978
983 template <typename... Args>
984 requires(!is_async_alias_v<T> && std::constructible_from<T, Args...>)
985 T& emplace_assert(Args&&... args)
986 {
987 DEBUG_CHECK(writer_.ready(), "WriteBuffer must be immediately writable");
988 writer_.emplace(std::forward<Args>(args)...);
989 return writer_.data();
990 }
991
994 auto take() &
995 requires(!is_async_alias_v<T>)
996 {
997 return TakeAwaiter<T>(&writer_);
998 }
999
1003 requires(!is_async_alias_v<T>)
1004 {
1005 this->invalidate_proxy_state();
1006 return OwningTakeAwaiter<T>(std::move(writer_));
1007 }
1008
1012 requires(!is_async_alias_v<T>)
1013 {
1014 this->invalidate_proxy_state();
1015 return TakeReleaseAwaiter<T>(&writer_);
1016 }
1017
1021 requires(!is_async_alias_v<T>)
1022 {
1023 this->invalidate_proxy_state();
1024 return OwningTakeAwaiter<T>(std::move(writer_));
1025 }
1026
1029 auto storage() &
1030 requires(!is_async_alias_v<T>)
1031 {
1032 return StorageAwaiter<T>(&writer_);
1033 }
1034
1038 requires(!is_async_alias_v<T>)
1039 {
1040 this->invalidate_proxy_state();
1041 return OwningStorageAwaiter<T>(std::move(writer_));
1042 }
1043
1046 std::shared_ptr<EpochContext> epoch_context_shared() const noexcept { return writer_.epoch_context_shared(); }
1047
1051 void register_exception_sink(TaskPromiseBase& promise, bool explicit_sink) const
1052 {
1053 promise.register_exception_sink(exception_sink_, this->epoch_context_shared(), explicit_sink);
1054 }
1055
1059 requires(!is_async_alias_v<T>)
1060 {
1061 return writer_.move_from_wait();
1062 }
1063
1067 template <typename U>
1068 requires(!is_async_alias_v<T> || detail::async_alias_assignment_source<T, U>)
1069 void write(U&& val)
1070 {
1071 async_assign(std::move(*this), std::forward<U>(val));
1072 }
1073
1077 template <typename U>
1078 void write_assert(U&& val)
1079 requires((!is_async_alias_v<T> && std::assignable_from<T&, U &&>) || detail::async_alias_assignable_from<T, U &&>)
1080 {
1081 DEBUG_CHECK(writer_.ready(), "WriteBuffer must be immediately writable");
1082 if constexpr (is_async_alias_v<T>)
1083 detail::assign_async_alias(writer_.data(), std::forward<U>(val));
1084 else
1085 writer_.data() = std::forward<U>(val);
1086 }
1087
1091 template <typename U>
1092 void write_move_assert(U&& val)
1093 requires(!is_async_alias_v<T> && std::assignable_from<T&, U &&>)
1094 {
1095 DEBUG_CHECK(writer_.ready(), "WriteBuffer must be immediately writable");
1096 writer_.data() = std::move(val);
1097 }
1098
1101 [[nodiscard]] WriteAssignProxy<T> write();
1102
1106 template <typename U>
1107 requires(!is_async_alias_v<T>)
1108 void write_move(U&& val)
1109 {
1110 async_move(std::move(*this), std::move(val));
1111 }
1112
1115 auto operator co_await() & noexcept -> WriteBuffer& { return *this; }
1118 auto operator co_await() const& noexcept -> WriteBuffer const& { return *this; }
1121 auto operator co_await() && noexcept -> OwningWriteAwaiter<T>
1122 {
1123 this->invalidate_proxy_state();
1124 return OwningWriteAwaiter<T>(std::move(writer_));
1125 }
1126
1127 private:
1130 void move_exception_sink_from(WriteBuffer& other) noexcept
1131 {
1132 if (!other.exception_sink_.owner) return;
1133 auto* owner = other.exception_sink_.owner;
1134 auto epoch = std::move(other.exception_sink_.epoch);
1135 bool const explicit_sink = other.exception_sink_.explicit_sink;
1136 owner->unregister_exception_sink(other.exception_sink_, false);
1137 owner->register_exception_sink(exception_sink_, std::move(epoch), explicit_sink);
1138 }
1139
1142 void unregister_exception_sink(bool from_destructor) noexcept
1143 {
1144 if (!exception_sink_.owner) return;
1145 exception_sink_.owner->unregister_exception_sink(exception_sink_, from_destructor);
1146 }
1147
1149 void invalidate_proxy_state() noexcept
1150 {
1151#if UNI20_DEBUG_ASYNC_TASKS
1152 if (proxy_state_) proxy_state_->alive.store(false, std::memory_order_release);
1153#endif
1154 }
1155
1157 void reset_proxy_state() noexcept
1158 {
1159#if UNI20_DEBUG_ASYNC_TASKS
1160 proxy_state_ = std::make_shared<WriteProxyLifetimeState>();
1161#endif
1162 }
1163
1164 EpochContextWriter<T> writer_;
1165 mutable TaskPromiseBase::ExceptionSinkNode exception_sink_{};
1166#if UNI20_DEBUG_ASYNC_TASKS
1167 std::shared_ptr<WriteProxyLifetimeState> proxy_state_{std::make_shared<WriteProxyLifetimeState>()};
1168#endif
1169
1170 // Duplicating a WriteBuffer is no longer possible with the refactored EpochContext.
1171 // To re-enable this function we would need to make the
1172 // friend WriteBuffer dup(WriteBuffer& wb) { return WriteBuffer(wb.writer_); }
1173};
1174
1175// For a ReadBuffer, we add the node to the ReadDependencies
1180template <typename T> void ProcessCoroutineArgument(TaskPromiseBase* promise, ReadBuffer<T> const& x)
1181{
1182#if UNI20_DEBUG_DAG
1183 promise->ReadDependencies.push_back(x.node());
1184#endif
1185}
1186
1187// For a WriteBuffer, we add the node to the WriteDependencies
1192template <typename T> void ProcessCoroutineArgument(TaskPromiseBase* promise, WriteBuffer<T> const& x)
1193{
1194#if UNI20_DEBUG_DAG
1195 promise->WriteDependencies.push_back(x.node());
1196#endif
1197 x.register_exception_sink(*promise, false);
1198}
1199
1200template <typename T> class Defer;
1201
1203template <typename B>
1205 requires(B& buffer, TaskPromiseBase& promise) { buffer.register_exception_sink(promise, true); };
1206
1210 public:
1213 explicit PropagateExceptionsAwaiter(Buffers&... buffers) : buffers_(std::addressof(buffers)...) {}
1214
1217 [[nodiscard]] bool await_ready() const noexcept { return true; }
1218
1222 BasicTask await_suspend(BasicTask&& task) noexcept { return std::move(task); }
1223
1225 void await_resume() noexcept {}
1226
1230 {
1231 std::apply([&](auto*... buffers) { (buffers->register_exception_sink(promise, true), ...); }, buffers_);
1232 }
1233
1234 private:
1235 std::tuple<Buffers*...> buffers_;
1236};
1237
1241template <exception_sink_buffer... Buffers> auto propagate_exceptions_to(Buffers&... buffers)
1242{
1243 return PropagateExceptionsAwaiter<Buffers...>(buffers...);
1244}
1245
1246namespace detail
1247{
1248
1250template <typename T, typename U>
1251concept write_proxy_assignable_source = std::constructible_from<T, U&&> && std::is_assignable_v<T&, U&&>;
1252
1254template <typename T, typename U>
1255 requires write_proxy_assignable_source<T, U>
1256void assign_write_proxy_value(EpochContextWriter<T>& writer, U&& source)
1257{
1258 if (writer.storage().constructed())
1259 writer.data() = std::forward<U>(source);
1260 else
1261 writer.emplace(std::forward<U>(source));
1262}
1263
1264} // namespace detail
1265
1268template <typename T> class WriteAccessProxy {
1269 public:
1270 using value_type = T;
1271
1273 WriteAccessProxy(WriteAccessProxy const&) noexcept = default;
1275 WriteAccessProxy(WriteAccessProxy&&) noexcept = default;
1276 WriteAccessProxy& operator=(WriteAccessProxy&&) = delete;
1277
1284 template <typename U>
1285 requires(!std::same_as<std::remove_cvref_t<U>, WriteAccessProxy>) &&
1286 ((!is_async_alias_v<T> && detail::write_proxy_assignable_source<T, U>) ||
1287 detail::async_alias_assignable_from<T, U &&>)
1288 WriteAccessProxy& operator=(U&& u)
1289 {
1290 if constexpr (is_async_alias_v<T>)
1291 detail::assign_async_alias(this->writer().data(), std::forward<U>(u));
1292 else
1293 detail::assign_write_proxy_value(this->writer(), std::forward<U>(u));
1294 return *this;
1295 }
1296
1301 template <typename... Args>
1302 requires(!is_async_alias_v<T> && std::constructible_from<T, Args...>)
1303 T& emplace(Args&&... args)
1304 {
1305 return this->writer().emplace(std::forward<Args>(args)...);
1306 }
1307
1312 template <typename U>
1313 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value += std::forward<U>(x); }
1315 {
1316 auto& storage = this->writer().storage();
1317 if (storage.constructed())
1318 {
1319 this->get() += std::forward<U>(x);
1320 }
1321 else if constexpr (std::constructible_from<T, U&&>)
1322 {
1323 storage.emplace(std::forward<U>(x));
1324 }
1325 else
1326 {
1327 this->get() += std::forward<U>(x);
1328 }
1329 return *this;
1330 }
1331
1336 template <typename U>
1337 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value -= std::forward<U>(x); }
1339 {
1340 auto& storage = this->writer().storage();
1341 if (storage.constructed())
1342 {
1343 this->get() -= std::forward<U>(x);
1344 }
1345 else if constexpr (requires { -std::declval<U&&>(); } &&
1346 std::constructible_from<T, decltype(-std::declval<U&&>())>)
1347 {
1348 auto neg = -std::forward<U>(x);
1349 storage.emplace(std::move(neg));
1350 }
1351 else
1352 {
1353 this->get() -= std::forward<U>(x);
1354 }
1355 return *this;
1356 }
1357
1362 template <typename U>
1363 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value *= std::forward<U>(x); }
1365 {
1366 this->get() *= std::forward<U>(x);
1367 return *this;
1368 }
1369
1374 template <typename U>
1375 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value /= std::forward<U>(x); }
1377 {
1378 this->get() /= std::forward<U>(x);
1379 return *this;
1380 }
1381
1385 requires(!is_async_alias_v<T>)
1386 {
1387 return this->writer().storage().take();
1388 }
1389
1393 requires(!is_async_alias_v<T>)
1394 {
1395 T value = this->take();
1396 this->release();
1397 return value;
1398 }
1399
1401 void release() noexcept
1402 {
1403#if UNI20_DEBUG_ASYNC_TASKS
1404 if (auto state = proxy_state_.lock(); state)
1405 {
1406 state->alive.store(false, std::memory_order_release);
1407 }
1408#endif
1409 if (writer_)
1410 {
1411 writer_->release();
1412 writer_ = nullptr;
1413 }
1414 }
1415
1418 [[nodiscard]] T& get() const
1419 requires(!is_async_alias_v<T>)
1420 {
1421 return this->writer().data();
1422 }
1423
1426 [[nodiscard]] T const& get() const
1427 requires(is_async_alias_v<T>)
1428 {
1429 return this->writer().data();
1430 }
1431
1432 operator T&() const
1433 requires(!is_async_alias_v<T>)
1434 {
1435 return this->get();
1436 }
1437
1438 operator T const&() const
1439 requires(is_async_alias_v<T>)
1440 {
1441 return this->get();
1442 }
1443
1444 T* operator->() const
1445 requires(!is_async_alias_v<T>)
1446 {
1447 return std::addressof(this->get());
1448 }
1449
1450 T const* operator->() const
1451 requires(is_async_alias_v<T>)
1452 {
1453 return std::addressof(this->get());
1454 }
1455
1456 private:
1459#if UNI20_DEBUG_ASYNC_TASKS
1461#endif
1463#if UNI20_DEBUG_ASYNC_TASKS
1464 ,
1465 std::shared_ptr<WriteProxyLifetimeState> proxy_state
1466#endif
1467 ) noexcept
1468 : writer_(writer)
1469#if UNI20_DEBUG_ASYNC_TASKS
1470 ,
1471 proxy_state_(std::move(proxy_state))
1472#endif
1473 {}
1474
1477 [[nodiscard]] EpochContextWriter<T>& writer() const
1478 {
1479#if UNI20_DEBUG_ASYNC_TASKS
1480 auto state = proxy_state_.lock();
1481 DEBUG_CHECK(state && state->alive.load(std::memory_order_acquire),
1482 "WriteAccessProxy used after WriteBuffer moved, released, or destroyed");
1483#endif
1484 DEBUG_CHECK(writer_, "WriteAccessProxy has no writer");
1485 return *writer_;
1486 }
1487
1488 friend class WriteBuffer<T>;
1489
1490 EpochContextWriter<T>* writer_;
1491#if UNI20_DEBUG_ASYNC_TASKS
1492 std::weak_ptr<WriteProxyLifetimeState> proxy_state_;
1493#endif
1494};
1495
1498template <typename T> class OwningWriteAccessProxy {
1499 public:
1500 using value_type = T;
1501
1506 OwningWriteAccessProxy& operator=(OwningWriteAccessProxy&&) noexcept = delete;
1507
1514 template <typename U>
1515 requires(!std::same_as<std::remove_cvref_t<U>, OwningWriteAccessProxy>) &&
1516 ((!is_async_alias_v<T> && detail::write_proxy_assignable_source<T, U>) ||
1517 detail::async_alias_assignable_from<T, U &&>)
1518 OwningWriteAccessProxy& operator=(U&& u)
1519 {
1520 if constexpr (is_async_alias_v<T>)
1521 detail::assign_async_alias(writer_.data(), std::forward<U>(u));
1522 else
1523 detail::assign_write_proxy_value(writer_, std::forward<U>(u));
1524 return *this;
1525 }
1526
1531 template <typename... Args>
1532 requires(!is_async_alias_v<T> && std::constructible_from<T, Args...>)
1533 T& emplace(Args&&... args)
1534 {
1535 return writer_.emplace(std::forward<Args>(args)...);
1536 }
1537
1542 template <typename U>
1543 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value += std::forward<U>(x); }
1545 {
1546 auto& storage = writer_.storage();
1547 if (storage.constructed())
1548 {
1549 this->get() += std::forward<U>(x);
1550 }
1551 else if constexpr (std::constructible_from<T, U&&>)
1552 {
1553 storage.emplace(std::forward<U>(x));
1554 }
1555 else
1556 {
1557 this->get() += std::forward<U>(x);
1558 }
1559 return *this;
1560 }
1561
1566 template <typename U>
1567 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value -= std::forward<U>(x); }
1569 {
1570 auto& storage = writer_.storage();
1571 if (storage.constructed())
1572 {
1573 this->get() -= std::forward<U>(x);
1574 }
1575 else if constexpr (requires { -std::declval<U&&>(); } &&
1576 std::constructible_from<T, decltype(-std::declval<U&&>())>)
1577 {
1578 auto neg = -std::forward<U>(x);
1579 storage.emplace(std::move(neg));
1580 }
1581 else
1582 {
1583 this->get() -= std::forward<U>(x);
1584 }
1585 return *this;
1586 }
1587
1592 template <typename U>
1593 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value *= std::forward<U>(x); }
1595 {
1596 this->get() *= std::forward<U>(x);
1597 return *this;
1598 }
1599
1604 template <typename U>
1605 requires(!is_async_alias_v<T>) && requires(T& value, U&& x) { value /= std::forward<U>(x); }
1607 {
1608 this->get() /= std::forward<U>(x);
1609 return *this;
1610 }
1611
1614 T& get()
1615 requires(!is_async_alias_v<T>)
1616 {
1617 return writer_.data();
1618 }
1621 T const& get() const { return writer_.data(); }
1622
1626 requires(!is_async_alias_v<T>)
1627 {
1628 return writer_.storage().take();
1629 }
1630
1634 requires(!is_async_alias_v<T>)
1635 {
1636 T value = this->take();
1637 this->release();
1638 return value;
1639 }
1640
1642 void release() noexcept { writer_.release(); }
1643
1644 operator T&()
1645 requires(!is_async_alias_v<T>)
1646 {
1647 return this->get();
1648 }
1649 operator T const&() const { return this->get(); }
1650
1652 requires(!is_async_alias_v<T>)
1653 {
1654 return std::addressof(this->get());
1655 }
1656 T const* operator->() const { return std::addressof(this->get()); }
1657
1658 private:
1661 explicit OwningWriteAccessProxy(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
1662
1663 friend class WriteBuffer<T>;
1664 friend class OwningWriteAwaiter<T>;
1665
1666 EpochContextWriter<T> writer_;
1667};
1668
1670template <typename T> class OwningWriteAwaiter {
1671 public:
1674 explicit OwningWriteAwaiter(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
1675
1678 [[nodiscard]] bool await_ready() const noexcept { return writer_.ready(); }
1679
1680#if UNI20_DEBUG_DAG
1682 NodeInfo const* node() const { return writer_.node(); }
1684 static constexpr auto debug_task_role() noexcept { return uni20::TaskRegistry::EpochTaskRole::Writer; }
1685#endif
1686
1689 void await_suspend(BasicTask&& task) noexcept
1690 {
1691 TRACE_MODULE(ASYNC, "OwningWriteAwaiter::await_suspend()", this, task.coroutine_handle());
1692 writer_.suspend(std::move(task));
1693 }
1694
1698 {
1699 writer_.resume();
1700 return OwningWriteAccessProxy<T>(std::move(writer_));
1701 }
1702
1705 [[nodiscard]] std::shared_ptr<EpochContext> epoch_context_shared() const noexcept
1706 {
1707 return writer_.epoch_context_shared();
1708 }
1709
1710 private:
1711 EpochContextWriter<T> writer_;
1712};
1713
1715template <typename T> using WriteAwaitProxy = WriteAccessProxy<T>;
1717template <typename T> using OwningWriteProxy = OwningWriteAccessProxy<T>;
1718
1720template <typename T> class WriteAssignProxy {
1721 public:
1722 using value_type = T;
1723
1727 WriteAssignProxy(WriteAssignProxy&&) noexcept = default;
1728 WriteAssignProxy& operator=(WriteAssignProxy&&) noexcept = delete;
1729
1733 template <typename U>
1734 requires(!std::same_as<std::remove_cvref_t<U>, WriteAssignProxy>) &&
1735 (!is_async_alias_v<T> || detail::async_alias_assignment_source<T, U>)
1736 void operator=(U&& u)
1737 {
1738 async_assign(WriteBuffer<T>(std::move(writer_)), std::forward<U>(u));
1739 }
1740
1741 private:
1744 explicit WriteAssignProxy(EpochContextWriter<T>&& writer) : writer_(std::move(writer)) {}
1745
1746 friend class WriteBuffer<T>;
1747
1748 EpochContextWriter<T> writer_;
1749};
1750
1752template <typename T> using WriteProxy = WriteAssignProxy<T>;
1753
1757template <typename T> WriteAssignProxy<T> WriteBuffer<T>::write() { return WriteAssignProxy<T>(std::move(writer_)); }
1758
1759} // namespace uni20::async
Promise-neutral coroutine task ownership and the public AsyncTask type.
Defines shared and concrete task coroutine promises.
Payload classification and write-through customization for asynchronous values.
Move-only promise-neutral ownership claim for a Uni20 coroutine.
Definition async_task.hpp:103
RAII handle representing the writer of a value in an EpochContext.
Definition epoch_context.hpp:874
RAII-scoped representation of a writer’s participation in an epoch.
Definition epoch_context.hpp:1048
void resume() const noexcept
Definition epoch_context.hpp:1121
Scheduler interface for initial AsyncTask submission and host-side progress.
Definition scheduler.hpp:89
Records node information for DAG/debugging visualization.
Definition async_node.hpp:171
Owning proxy returned by co_await read_buffer.transfer().
Definition buffers.hpp:235
T const & get() const
Access the referenced value.
Definition buffers.hpp:247
OwningReadAccessProxy(OwningReadAccessProxy &&) noexcept=default
T const * operator->() const
Definition buffers.hpp:251
T get_release()
Copy the value and release the read epoch in one call.
Definition buffers.hpp:255
OwningReadAccessProxy & operator=(OwningReadAccessProxy const &)=delete
OwningReadAccessProxy(OwningReadAccessProxy const &)=delete
void release() noexcept
Release the read epoch held by this proxy.
Definition buffers.hpp:263
T value_type
Definition buffers.hpp:237
Awaiter that transfers a read handle into an owning read proxy.
Definition buffers.hpp:279
void await_suspend(BasicTask &&t) noexcept
Suspend until the read epoch becomes available.
Definition buffers.hpp:300
T value_type
Definition buffers.hpp:281
OwningReadAwaiter(EpochContextReader< T > &&reader)
Construct from an owning epoch reader.
Definition buffers.hpp:285
OwningReadAccessProxy< T > await_resume()
Resume and transfer reader ownership to an owning read proxy.
Definition buffers.hpp:308
bool await_ready() const noexcept
Reports whether the read epoch is immediately ready.
Definition buffers.hpp:289
Owning proxy providing access to writer storage.
Definition buffers.hpp:703
shared_storage< T > * operator->()
Definition buffers.hpp:723
shared_storage< T > const * operator->() const
Definition buffers.hpp:724
T take()
Move the stored value out of writer storage.
Definition buffers.hpp:728
void release() noexcept
Release the writer epoch held by this proxy.
Definition buffers.hpp:740
T take_release()
Move the stored value out and release the writer epoch.
Definition buffers.hpp:732
shared_storage< T > & get()
Returns mutable access to the underlying shared storage.
Definition buffers.hpp:715
shared_storage< T > const & get() const
Returns const access to the underlying shared storage.
Definition buffers.hpp:718
OwningStorageAccessProxy(OwningStorageAccessProxy &&) noexcept=default
OwningStorageAccessProxy & operator=(OwningStorageAccessProxy const &)=delete
OwningStorageAccessProxy(OwningStorageAccessProxy const &)=delete
Awaiter that yields an owning storage-access proxy.
Definition buffers.hpp:754
OwningStorageAccessProxy< T > await_resume()
Resume and transfer writer ownership to a storage proxy.
Definition buffers.hpp:783
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:775
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:764
OwningStorageAwaiter(EpochContextWriter< T > &&writer)
Construct from an owning writer.
Definition buffers.hpp:760
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:791
Awaiter that takes and releases a value from an owning writer.
Definition buffers.hpp:802
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:810
T await_resume()
Resume, move out the value, and release the writer epoch.
Definition buffers.hpp:829
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:839
OwningTakeAwaiter(EpochContextWriter< T > &&writer)
Construct from an owning writer.
Definition buffers.hpp:806
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:821
Owning proxy returned by co_await write_buffer.transfer().
Definition buffers.hpp:1498
T * operator->()
Definition buffers.hpp:1651
OwningWriteAccessProxy & operator*=(U &&x)
In-place *= update.
Definition buffers.hpp:1594
OwningWriteAccessProxy & operator-=(U &&x)
In-place -= update; emplaces negated value when unconstructed.
Definition buffers.hpp:1568
T & emplace(Args &&... args)
Construct or replace the underlying value in place.
Definition buffers.hpp:1533
T const * operator->() const
Definition buffers.hpp:1656
T take()
Move out the stored value.
Definition buffers.hpp:1625
OwningWriteAccessProxy(OwningWriteAccessProxy const &)=delete
T take_release()
Move out the value and release the writer epoch.
Definition buffers.hpp:1633
T & get()
Access mutable stored value.
Definition buffers.hpp:1614
OwningWriteAccessProxy & operator+=(U &&x)
In-place += update; emplaces when storage is unconstructed.
Definition buffers.hpp:1544
T const & get() const
Access const stored value.
Definition buffers.hpp:1621
T value_type
Definition buffers.hpp:1500
OwningWriteAccessProxy(OwningWriteAccessProxy &&) noexcept=default
OwningWriteAccessProxy & operator/=(U &&x)
In-place /= update.
Definition buffers.hpp:1606
OwningWriteAccessProxy & operator=(OwningWriteAccessProxy const &)=delete
void release() noexcept
Release the writer epoch held by this proxy.
Definition buffers.hpp:1642
Awaiter that transfers a writer handle into an owning write proxy.
Definition buffers.hpp:1670
OwningWriteAwaiter(EpochContextWriter< T > &&writer)
Construct from an owning writer.
Definition buffers.hpp:1674
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:1678
void await_suspend(BasicTask &&task) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:1689
OwningWriteAccessProxy< T > await_resume()
Resume and transfer writer ownership to an owning write proxy.
Definition buffers.hpp:1697
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:1705
Awaiter that registers explicit exception sinks and then resumes immediately.
Definition buffers.hpp:1209
PropagateExceptionsAwaiter(Buffers &... buffers)
Construct from references to sink buffers.
Definition buffers.hpp:1213
void register_exception_sinks(TaskPromiseBase &promise) const
Register all configured buffers as explicit exception sinks.
Definition buffers.hpp:1229
BasicTask await_suspend(BasicTask &&task) noexcept
Pass through the currently-running task unchanged.
Definition buffers.hpp:1222
void await_resume() noexcept
No-op resume hook.
Definition buffers.hpp:1225
bool await_ready() const noexcept
Always ready; no suspension required.
Definition buffers.hpp:1217
RAII handle for reading the value from an Async container at a given epoch.
Definition buffers.hpp:53
void await_suspend(BasicTask &&t) noexcept
Suspend this coroutine and enqueue for resumption.
Definition buffers.hpp:123
ReadMaybeAwaiter< T const & > maybe() &
Returns a ReadMaybeAwaiter.
Definition buffers.hpp:521
void release() noexcept
Manually release the epoch reader before awaitable destruction.
Definition buffers.hpp:143
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:179
T value_type
Definition buffers.hpp:55
ReadBuffer && transfer() &&noexcept
Transfer this read buffer into the owning-await path.
Definition buffers.hpp:151
ReadBuffer && transfer() &noexcept
Transfer this read buffer into the owning-await path.
Definition buffers.hpp:147
void register_exception_sink(TaskPromiseBase &promise, bool explicit_sink) const
Register this buffer as an exception sink with a promise.
Definition buffers.hpp:184
T const & get_wait(IAsyncScheduler &sched) const
Block using an explicit scheduler until the read value is available.
Definition buffers.hpp:168
ReadBuffer(ReadBuffer &&other) noexcept
Move constructor.
Definition buffers.hpp:70
T const & get_wait() const
Block until the read value is available.
Definition buffers.hpp:163
ReadBuffer(EpochContextReader< T > reader)
Construct a read buffer tied to a reader context.
Definition buffers.hpp:59
ReadOrCancelAwaiter< T const & > or_cancel() &
Returns a ReadOrCancelAwaiter.
Definition buffers.hpp:531
void wait() const
Block until the read value is available.
Definition buffers.hpp:154
bool await_ready() const noexcept
Check if the value is already ready to be read.
Definition buffers.hpp:119
void wait(IAsyncScheduler &sched) const
Block using an explicit scheduler until the read value is available.
Definition buffers.hpp:158
ReadBuffer & operator=(ReadBuffer &&other) noexcept
Move assignment.
Definition buffers.hpp:78
ReadBuffer & operator=(ReadBuffer const &)=delete
ReadBuffer(ReadBuffer const &other)
Copy constructor; duplicates read handle and exception sink registration.
Definition buffers.hpp:63
T const & await_resume() const &
Resume execution and return the stored value.
Definition buffers.hpp:131
~ReadBuffer() noexcept
Destructor unregisters any attached exception sink.
Definition buffers.hpp:90
T const * value_type
Definition buffers.hpp:375
value_type await_resume()
Resume execution and return a pointer to stored value, or nullptr.
Definition buffers.hpp:400
void await_suspend(BasicTask &&t) noexcept
Suspend this coroutine and enqueue for resumption.
Definition buffers.hpp:393
ReadMaybeAwaiter(ReadMaybeAwaiter &&)=default
Move constructor.
bool await_ready() const noexcept
Check if the value is already ready to be read.
Definition buffers.hpp:382
Awaiter returning an optional owning read proxy.
Definition buffers.hpp:321
ReadMaybeAwaiter(ReadMaybeAwaiter &&)=default
Move constructor.
bool await_ready() const noexcept
Check if the value is already ready to be read.
Definition buffers.hpp:330
void await_suspend(BasicTask &&t) noexcept
Suspend this coroutine and enqueue for resumption.
Definition buffers.hpp:341
value_type await_resume()
Resume execution and return an optional owning read proxy.
Definition buffers.hpp:348
std::optional< OwningReadAccessProxy< T > > value_type
Definition buffers.hpp:323
ReadOrCancelAwaiter(ReadOrCancelAwaiter &&)=default
Move constructor.
void await_suspend(BasicTask &&t) noexcept
Suspend this coroutine and enqueue for resumption.
Definition buffers.hpp:496
T const & await_resume()
Resume execution and return a borrowed reference to the stored value.
Definition buffers.hpp:501
bool await_ready() const noexcept
Check if the value is already ready to be read.
Definition buffers.hpp:481
Awaiter returning owning read access or throwing task_cancelled.
Definition buffers.hpp:417
void await_suspend(BasicTask &&t) noexcept
Suspend this coroutine and enqueue for resumption.
Definition buffers.hpp:441
bool await_ready() const noexcept
Check if the value is already ready to be read.
Definition buffers.hpp:426
OwningReadAccessProxy< T > value_type
Definition buffers.hpp:419
value_type await_resume()
Resume execution and return an owning read proxy.
Definition buffers.hpp:446
ReadOrCancelAwaiter(ReadOrCancelAwaiter &&)=default
Move constructor.
Awaiter returning direct access to a writer's shared storage object.
Definition buffers.hpp:563
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:582
StorageAwaiter(EpochContextWriter< T > *writer)
Construct from a non-owning writer pointer.
Definition buffers.hpp:567
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:571
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:598
shared_storage< T > & await_resume()
Resume and return mutable access to shared storage.
Definition buffers.hpp:590
Awaiter that takes the writer value without releasing the writer epoch.
Definition buffers.hpp:609
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:617
TakeAwaiter(EpochContextWriter< T > *writer)
Construct from a non-owning writer pointer.
Definition buffers.hpp:613
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:644
T await_resume()
Resume and move the stored value out of writer storage.
Definition buffers.hpp:636
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:628
Awaiter that takes a value and immediately releases the writer epoch.
Definition buffers.hpp:655
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:674
TakeReleaseAwaiter(EpochContextWriter< T > *writer)
Construct from a non-owning writer pointer.
Definition buffers.hpp:659
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:663
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:692
T await_resume()
Resume, move out the value, and release the writer epoch.
Definition buffers.hpp:682
Shared non-polymorphic implementation for Uni20 task promises.
Definition async_task_promise.hpp:99
void register_exception_sink(ExceptionSinkNode &node, std::shared_ptr< EpochContext > epoch, bool explicit_sink)
Register one exception propagation sink with this promise.
Definition async_task_promise.hpp:239
void unregister_exception_sink(ExceptionSinkNode &node, bool from_destructor) noexcept
Unregister one exception propagation sink from this promise.
Definition async_task_promise.hpp:256
Non-owning proxy returned by co_await on an lvalue WriteBuffer<T>.
Definition buffers.hpp:1268
WriteAccessProxy(WriteAccessProxy &&) noexcept=default
void release() noexcept
Release the writer epoch held by this proxy.
Definition buffers.hpp:1401
WriteAccessProxy & operator*=(U &&x)
In-place *= update.
Definition buffers.hpp:1364
T value_type
Definition buffers.hpp:1270
T const * operator->() const
Definition buffers.hpp:1450
T & get() const
Access an independent stored value mutably.
Definition buffers.hpp:1418
T & emplace(Args &&... args)
Construct or replace the underlying value in place.
Definition buffers.hpp:1303
WriteAccessProxy & operator-=(U &&x)
In-place -= update; emplaces negated value when unconstructed.
Definition buffers.hpp:1338
WriteAccessProxy & operator+=(U &&x)
In-place += update; emplaces when storage is unconstructed.
Definition buffers.hpp:1314
T take()
Move out the stored value.
Definition buffers.hpp:1384
T * operator->() const
Definition buffers.hpp:1444
WriteAccessProxy(WriteAccessProxy const &) noexcept=default
WriteAccessProxy & operator/=(U &&x)
In-place /= update.
Definition buffers.hpp:1376
WriteAccessProxy & operator=(WriteAccessProxy const &)=delete
T const & get() const
Inspect an async alias descriptor without permitting retargeting.
Definition buffers.hpp:1426
T take_release()
Move out the value and release the writer epoch.
Definition buffers.hpp:1392
Assignment-only proxy returned by WriteBuffer::write().
Definition buffers.hpp:1720
WriteAssignProxy(WriteAssignProxy const &)=delete
T value_type
Definition buffers.hpp:1722
WriteAssignProxy & operator=(WriteAssignProxy const &)=delete
WriteAssignProxy(WriteAssignProxy &&) noexcept=default
Exclusive mutable-access buffer for one Async<T> epoch.
Definition buffers.hpp:859
void write_move_assert(U &&val)
Move-assign immediately when the writer is known to be ready.
Definition buffers.hpp:1092
OwningWriteAccessProxy< T > await_resume() &&
Resume and return an owning mutable write proxy.
Definition buffers.hpp:950
bool await_ready() const noexcept
Reports whether the writer epoch is immediately writable.
Definition buffers.hpp:911
auto take() &&-> OwningTakeAwaiter< T > requires(!is_async_alias_v< T >)
Awaiter that takes the stored value from an rvalue buffer.
Definition buffers.hpp:1002
void register_exception_sink(TaskPromiseBase &promise, bool explicit_sink) const
Register this buffer as an exception sink with a promise.
Definition buffers.hpp:1051
auto take_release() &&-> OwningTakeAwaiter< T > requires(!is_async_alias_v< T >)
Awaiter that takes the value and releases the writer epoch from an rvalue buffer.
Definition buffers.hpp:1020
T move_from_wait()
Wait for writability and move out the contained value.
Definition buffers.hpp:1058
T value_type
Definition buffers.hpp:861
std::shared_ptr< EpochContext > epoch_context_shared() const noexcept
Returns the epoch context used for exception propagation.
Definition buffers.hpp:1046
T & emplace_assert(Args &&... args)
Construct the destination value in-place when immediately writable.
Definition buffers.hpp:985
WriteAssignProxy< T > write()
Build an assignment-only proxy that consumes this writer.
Definition buffers.hpp:1757
auto storage() &&-> OwningStorageAwaiter< T > requires(!is_async_alias_v< T >)
Awaiter yielding owning access to writer shared storage.
Definition buffers.hpp:1037
auto storage() &
Awaiter yielding direct access to writer shared storage.
Definition buffers.hpp:1029
WriteBuffer && transfer() &&noexcept
Transfer this write buffer into the owning-await path.
Definition buffers.hpp:973
void release() noexcept
Release the writer epoch held by this buffer.
Definition buffers.hpp:957
WriteAccessProxy< T > await_resume() const &
Resume and return a non-owning mutable write proxy.
Definition buffers.hpp:936
void write(U &&val)
Schedule assignment into this write buffer.
Definition buffers.hpp:1069
auto take() &
Awaiter that takes the stored value.
Definition buffers.hpp:994
WriteBuffer & operator=(WriteBuffer &&other) noexcept
Move assignment.
Definition buffers.hpp:889
std::conditional_t< is_async_alias_v< T >, T const &, T & > element_type
Definition buffers.hpp:862
WriteAccessProxy< T > await_resume() &
Resume and return a non-owning mutable write proxy.
Definition buffers.hpp:923
void write_move(U &&val)
Schedule move-assignment into this write buffer.
Definition buffers.hpp:1108
WriteBuffer & operator=(WriteBuffer const &)=delete
WriteBuffer(WriteBuffer const &)=delete
auto take_release() &
Awaiter that takes the value and releases the writer epoch.
Definition buffers.hpp:1011
~WriteBuffer() noexcept
Destructor unregisters exception sink and invalidates debug proxy state.
Definition buffers.hpp:869
WriteBuffer && transfer() &noexcept
Transfer this write buffer into the owning-await path.
Definition buffers.hpp:965
void await_suspend(BasicTask &&t) noexcept
Suspend until the writer epoch becomes writable.
Definition buffers.hpp:915
WriteBuffer(EpochContextWriter< T > writer)
Construct a write buffer tied to a writer context.
Definition buffers.hpp:866
WriteBuffer(WriteBuffer &&other) noexcept
Move constructor.
Definition buffers.hpp:880
void write_assert(U &&val)
Assign immediately when the writer is known to be ready.
Definition buffers.hpp:1078
A lightweight, thread-safe, reference-counted storage for a single object.
Definition shared_storage.hpp:94
Raised when an AsyncTask is cancelled before completion.
Definition async_errors.hpp:44
Concept for buffers that can register explicit exception sinks.
Definition buffers.hpp:1204
locale_t value
Definition data_table.cpp:49
Manages one “generation” of write/read ordering in an Async<T>
void async_move(WriteBuffer< T > lhs, U &&rhs)
Definition async_ops.hpp:369
void async_assign(WriteBuffer< T > destination, U &&source)
Assign an awaitable value into an existing independent-value timeline.
Definition async_ops.hpp:310
Definition qnum.hpp:403
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
ReadBuffer< T > && release(ReadBuffer< T > &in)
Adaptor for transferring ReadBuffer ownership into an await expression.
Definition buffers.hpp:227
auto propagate_exceptions_to(Buffers &... buffers)
Register buffers that should receive unhandled coroutine exceptions.
Definition buffers.hpp:1241
constexpr bool is_async_alias_v
True when T is an owner-bound async alias descriptor.
Definition async_traits.hpp:53
void ProcessCoroutineArgument(TaskPromiseBase *promise, T const &value)
Process a coroutine argument for debug metadata (default no-op).
Definition async_task_promise.hpp:946
Reference-counted optional in-place storage used by async buffers.
TaskPromiseBase * owner
Definition async_task_promise.hpp:104
bool explicit_sink
Definition async_task_promise.hpp:108
std::shared_ptr< EpochContext > epoch
Definition async_task_promise.hpp:107
#define DEBUG_CHECK(cond,...)
Definition trace.hpp:608
#define TRACE_MODULE(m,...)
Definition trace.hpp:149