| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_ASYNC_CPP_WAIT_H_ |
| #define LIB_ASYNC_CPP_WAIT_H_ |
| |
| #include <lib/async/wait.h> |
| #include <lib/fit/function.h> |
| #include <zircon/assert.h> |
| |
| #include <utility> |
| |
| namespace async { |
| |
| // Holds context for an asynchronous wait and its handler, with RAII semantics. |
| // Automatically cancels the wait when it goes out of scope. |
| // |
| // After successfully beginning the wait, the client is responsible for retaining |
| // the structure in memory (and unmodified) until the wait's handler runs, the wait |
| // is successfully canceled, or the dispatcher shuts down. Thereafter, the wait |
| // may be begun again or destroyed. |
| // |
| // This class must only be used with single-threaded asynchronous dispatchers |
| // and must only be accessed on the dispatch thread since it lacks internal |
| // synchronization of its state. |
| // |
| // Concrete implementations: |async::Wait|, |async::WaitMethod|. |
| // Please do not create subclasses of WaitBase outside of this library. |
| class WaitBase { |
| protected: |
| explicit WaitBase(zx_handle_t object, zx_signals_t trigger, uint32_t options, |
| async_wait_handler_t* handler); |
| ~WaitBase(); |
| |
| WaitBase(const WaitBase&) = delete; |
| WaitBase(WaitBase&&) = delete; |
| WaitBase& operator=(const WaitBase&) = delete; |
| WaitBase& operator=(WaitBase&&) = delete; |
| |
| public: |
| // Gets or sets the object to wait for signals on. |
| zx_handle_t object() const { return wait_.object; } |
| void set_object(zx_handle_t object) { wait_.object = object; } |
| |
| // Gets or sets the signals to wait for. |
| zx_signals_t trigger() const { return wait_.trigger; } |
| void set_trigger(zx_signals_t trigger) { wait_.trigger = trigger; } |
| |
| // Gets or sets the options to wait with. See zx_object_wait_async(). |
| uint32_t options() const { return wait_.options; } |
| void set_options(uint32_t options) { wait_.options = options; } |
| |
| // Returns true if the wait has begun and not yet completed or been canceled. |
| bool is_pending() const { return dispatcher_ != nullptr; } |
| |
| // Begins asynchronously waiting for the object to receive one or more of |
| // the trigger signals. Invokes the handler when the wait completes. |
| // |
| // The wait's handler will be invoked exactly once unless the wait is canceled. |
| // When the dispatcher is shutting down (being destroyed), the handlers of |
| // all remaining waits will be invoked with a status of |ZX_ERR_CANCELED|. |
| // |
| // Returns |ZX_OK| if the wait was successfully begun. |
| // Returns |ZX_ERR_ACCESS_DENIED| if the object does not have |ZX_RIGHT_WAIT|. |
| // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down. |
| // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher. |
| zx_status_t Begin(async_dispatcher_t* dispatcher); |
| |
| // Cancels the wait. |
| // |
| // If successful, the wait's handler will not run. |
| // |
| // Returns |ZX_OK| if the wait was pending and it has been successfully |
| // canceled; its handler will not run again and can be released immediately. |
| // Returns |ZX_ERR_NOT_FOUND| if there was no pending wait either because it |
| // already completed, or had not been started. |
| // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher. |
| zx_status_t Cancel(); |
| |
| protected: |
| template <typename T> |
| static T* Dispatch(async_wait* wait) { |
| static_assert(offsetof(WaitBase, wait_) == 0, ""); |
| auto self = reinterpret_cast<WaitBase*>(wait); |
| self->dispatcher_ = nullptr; |
| return static_cast<T*>(self); |
| } |
| |
| private: |
| async_wait_t wait_; |
| async_dispatcher_t* dispatcher_ = nullptr; |
| }; |
| |
| // An asynchronous wait whose handler is bound to a |async::Wait::Handler| function. |
| // |
| // Prefer using |async::WaitMethod| instead for binding to a fixed class member |
| // function since it is more efficient to dispatch. |
| class Wait final : public WaitBase { |
| public: |
| // Handles completion of asynchronous wait operations. |
| // |
| // The |status| is |ZX_OK| if the wait was satisfied and |signal| is non-null. |
| // The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before |
| // the task's handler ran or the task was canceled. |
| using Handler = fit::function<void(async_dispatcher_t* dispatcher, async::Wait* wait, |
| zx_status_t status, const zx_packet_signal_t* signal)>; |
| |
| explicit Wait(zx_handle_t object = ZX_HANDLE_INVALID, zx_signals_t trigger = ZX_SIGNAL_NONE, |
| uint32_t options = 0, Handler handler = nullptr); |
| |
| ~Wait(); |
| |
| void set_handler(Handler handler) { handler_ = std::move(handler); } |
| bool has_handler() const { return !!handler_; } |
| |
| private: |
| static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, zx_status_t status, |
| const zx_packet_signal_t* signal); |
| |
| Handler handler_; |
| }; |
| |
| // An asynchronous wait whose handler is bound to a |async::WaitOnce::Handler| function, but that |
| // handler can only be called once, at which point the handler is destroyed. |
| // |
| // This type of wait is particularly useful for handlers that will delete the wait object itself |
| // since the handler will be moved to the stack prior to being called. |
| class WaitOnce final : public WaitBase { |
| public: |
| // Handles completion of asynchronous wait operations. |
| // |
| // The |status| is |ZX_OK| if the wait was satisfied and |signal| is non-null. |
| // The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before |
| // the task's handler ran or the task was canceled. |
| using Handler = fit::function<void(async_dispatcher_t* dispatcher, async::WaitOnce* wait, |
| zx_status_t status, const zx_packet_signal_t* signal)>; |
| |
| explicit WaitOnce(zx_handle_t object = ZX_HANDLE_INVALID, zx_signals_t trigger = ZX_SIGNAL_NONE, |
| uint32_t options = 0); |
| |
| ~WaitOnce(); |
| |
| // Begins asynchronously waiting for the object to receive one or more of |
| // the trigger signals. Invokes the handler when the wait completes. |
| // |
| // The wait's handler will be invoked exactly once unless the wait is canceled. |
| // When the dispatcher is shutting down (being destroyed), the handlers of |
| // all remaining waits will be invoked with a status of |ZX_ERR_CANCELED|. |
| // |
| // As the handler is destroyed on each invocation, a new handler must be supplied at each call |
| // to Begin(). |
| // |
| // Returns |ZX_OK| if the wait was successfully begun. |
| // Returns |ZX_ERR_ACCESS_DENIED| if the object does not have |ZX_RIGHT_WAIT|. |
| // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down. |
| // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher. |
| zx_status_t Begin(async_dispatcher_t* dispatcher, Handler handler); |
| |
| private: |
| // Hide the base Begin() signature in favor of the one that requires a handler. |
| using WaitBase::Begin; |
| |
| static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, zx_status_t status, |
| const zx_packet_signal_t* signal); |
| |
| Handler handler_; |
| }; |
| |
| // An asynchronous wait whose handler is bound to a fixed class member function. |
| // |
| // Usage: |
| // |
| // class Foo { |
| // void Handle(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, |
| // const zx_packet_signal_t* signal) { ... } |
| // async::WaitMethod<Foo, &Foo::Handle> wait_{this}; |
| // }; |
| template <class Class, void (Class::*method)(async_dispatcher_t* dispatcher, async::WaitBase* wait, |
| zx_status_t status, const zx_packet_signal_t* signal)> |
| class WaitMethod final : public WaitBase { |
| public: |
| explicit WaitMethod(Class* instance, zx_handle_t object = ZX_HANDLE_INVALID, |
| zx_signals_t trigger = ZX_SIGNAL_NONE, uint32_t options = 0) |
| : WaitBase(object, trigger, options, &WaitMethod::CallHandler), instance_(instance) {} |
| |
| ~WaitMethod() { |
| // See comment in WaitBase::~WaitBase re. why Cancel() happens in sub-class. |
| // |
| // For WaitMethod, the Cancel() is here instead of in ~WaitBase just to keep the destruction |
| // sequencing consistent across WaitBase sub-classes. |
| (void)Cancel(); |
| ZX_DEBUG_ASSERT(!is_pending()); |
| // ~WaitBase |
| } |
| |
| private: |
| static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| auto self = Dispatch<WaitMethod>(wait); |
| (self->instance_->*method)(dispatcher, self, status, signal); |
| } |
| |
| Class* const instance_; |
| }; |
| |
| } // namespace async |
| |
| #endif // LIB_ASYNC_CPP_WAIT_H_ |