summaryrefslogtreecommitdiffstats
path: root/libcxx/test/std/experimental/task/manual_reset_event.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/std/experimental/task/manual_reset_event.hpp')
-rw-r--r--libcxx/test/std/experimental/task/manual_reset_event.hpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/libcxx/test/std/experimental/task/manual_reset_event.hpp b/libcxx/test/std/experimental/task/manual_reset_event.hpp
new file mode 100644
index 00000000000..cfd53227495
--- /dev/null
+++ b/libcxx/test/std/experimental/task/manual_reset_event.hpp
@@ -0,0 +1,127 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_TEST_EXPERIMENTAL_TASK_MANUAL_RESET_EVENT
+#define _LIBCPP_TEST_EXPERIMENTAL_TASK_MANUAL_RESET_EVENT
+
+#include <experimental/coroutine>
+#include <atomic>
+
+// manual_reset_event is a coroutine synchronisation tool that allows one
+// coroutine to await the event object and if the event was not crrently
+// in the 'set' state then will suspend the awaiting coroutine until some
+// thread calls .set() on the event.
+class manual_reset_event
+{
+ friend class _Awaiter;
+
+ class _Awaiter
+ {
+ public:
+
+ _Awaiter(const manual_reset_event* __event) noexcept
+ : __event_(__event)
+ {}
+
+ bool await_ready() const noexcept
+ {
+ return __event_->is_set();
+ }
+
+ bool await_suspend(std::experimental::coroutine_handle<> __coro) noexcept
+ {
+ _LIBCPP_ASSERT(
+ __event_->__state_.load(std::memory_order_relaxed) !=
+ __State::__not_set_waiting_coroutine,
+ "This manual_reset_event already has another coroutine awaiting it. "
+ "Only one awaiting coroutine is supported."
+ );
+
+ __event_->__awaitingCoroutine_ = __coro;
+
+ // If the compare-exchange fails then this means that the event was
+ // already 'set' and so we should not suspend - this code path requires
+ // 'acquire' semantics so we have visibility of writes prior to the
+ // .set() operation that transitioned the event to the 'set' state.
+ // If the compare-exchange succeeds then this needs 'release' semantics
+ // so that a subsequent call to .set() has visibility of our writes
+ // to the coroutine frame and to __event_->__awaitingCoroutine_ after
+ // reading our write to __event_->__state_.
+ _State oldState = _State::__not_set;
+ return __event_->__state_.compare_exchange_strong(
+ oldState,
+ _State::__not_set_waiting_coroutine,
+ std::memory_order_release,
+ std::memory_order_acquire);
+ }
+
+ void await_resume() const noexcept {}
+
+ private:
+ const manual_reset_event* __event_;
+ };
+
+public:
+
+ manual_reset_event(bool __initiallySet = false) noexcept
+ : __state_(__initiallySet ? _State::__set : _State::__not_set)
+ {}
+
+ bool is_set() const noexcept
+ {
+ return __state_.load(std::memory_order_acquire) == _State::__set;
+ }
+
+ void set() noexcept
+ {
+ // Needs to be 'acquire' in case the old value was a waiting coroutine
+ // so that we have visibility of the writes to the coroutine frame in
+ // the current thrad before we resume it.
+ // Also needs to be 'release' in case the old value was 'not-set' so that
+ // another thread that subsequently awaits the
+ _State oldState = __state_.exchange(_State::__set, std::memory_order_acq_rel);
+ if (oldState == _State::__not_set_waiting_coroutine)
+ {
+ _VSTD::exchange(__awaitingCoroutine_, {}).resume();
+ }
+ }
+
+ void reset() noexcept
+ {
+ _LIBCPP_ASSERT(
+ __state_.load(std::memory_order_relaxed) != _State::__not_set_waiting_coroutine,
+ "Illegal to call reset() if a coroutine is currently awaiting the event.");
+
+ // Note, we use 'relaxed' memory order here since it considered a
+ // data-race to call reset() concurrently either with operator co_await()
+ // or with set().
+ __state_.store(_State::__not_set, std::memory_order_relaxed);
+ }
+
+ auto operator co_await() const noexcept
+ {
+ return _Awaiter{ this };
+ }
+
+private:
+
+ enum class _State {
+ __not_set,
+ __not_set_waiting_coroutine,
+ __set
+ };
+
+ // TODO: Can we combine these two members into a single std::atomic<void*>?
+ mutable std::atomic<_State> __state_;
+ mutable std::experimental::coroutine_handle<> __awaitingCoroutine_;
+
+};
+
+#endif
OpenPOWER on IntegriCloud