diff options
Diffstat (limited to 'libcxx/test/std/experimental/task/manual_reset_event.hpp')
| -rw-r--r-- | libcxx/test/std/experimental/task/manual_reset_event.hpp | 127 |
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 |

