1 // Copyright 2017 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include <fbl/function.h> 8 #include <lib/async/wait.h> 9 10 #include <utility> 11 12 namespace async { 13 14 // Holds context for an asynchronous wait and its handler, with RAII semantics. 15 // Automatically cancels the wait when it goes out of scope. 16 // 17 // After successfully beginning the wait, the client is responsible for retaining 18 // the structure in memory (and unmodified) until the wait's handler runs, the wait 19 // is successfully canceled, or the dispatcher shuts down. Thereafter, the wait 20 // may be begun again or destroyed. 21 // 22 // This class must only be used with single-threaded asynchronous dispatchers 23 // and must only be accessed on the dispatch thread since it lacks internal 24 // synchronization of its state. 25 // 26 // Concrete implementations: |async::Wait|, |async::WaitMethod|. 27 // Please do not create subclasses of WaitBase outside of this library. 28 class WaitBase { 29 protected: 30 explicit WaitBase(zx_handle_t object, zx_signals_t trigger, 31 async_wait_handler_t* handler); 32 ~WaitBase(); 33 34 WaitBase(const WaitBase&) = delete; 35 WaitBase(WaitBase&&) = delete; 36 WaitBase& operator=(const WaitBase&) = delete; 37 WaitBase& operator=(WaitBase&&) = delete; 38 39 public: 40 // Gets or sets the object to wait for signals on. object()41 zx_handle_t object() const { return wait_.object; } set_object(zx_handle_t object)42 void set_object(zx_handle_t object) { wait_.object = object; } 43 44 // Gets or set of signals to wait for. trigger()45 zx_signals_t trigger() const { return wait_.trigger; } set_trigger(zx_signals_t trigger)46 void set_trigger(zx_signals_t trigger) { wait_.trigger = trigger; } 47 48 // Returns true if the wait has begun and not yet completed or been canceled. is_pending()49 bool is_pending() const { return dispatcher_ != nullptr; } 50 51 // Begins asynchronously waiting for the object to receive one or more of 52 // the trigger signals. Invokes the handler when the wait completes. 53 // 54 // The wait's handler will be invoked exactly once unless the wait is canceled. 55 // When the dispatcher is shutting down (being destroyed), the handlers of 56 // all remaining waits will be invoked with a status of |ZX_ERR_CANCELED|. 57 // 58 // Returns |ZX_OK| if the wait was successfully begun. 59 // Returns |ZX_ERR_ACCESS_DENIED| if the object does not have |ZX_RIGHT_WAIT|. 60 // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down. 61 // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher. 62 zx_status_t Begin(async_dispatcher_t* dispatcher); 63 64 // Cancels the wait. 65 // 66 // If successful, the wait's handler will not run. 67 // 68 // Returns |ZX_OK| if the wait was pending and it has been successfully 69 // canceled; its handler will not run again and can be released immediately. 70 // Returns |ZX_ERR_NOT_FOUND| if there was no pending wait either because it 71 // already completed, or had not been started. 72 // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher. 73 zx_status_t Cancel(); 74 75 protected: 76 template <typename T> Dispatch(async_wait * wait)77 static T* Dispatch(async_wait* wait) { 78 static_assert(offsetof(WaitBase, wait_) == 0, ""); 79 auto self = reinterpret_cast<WaitBase*>(wait); 80 self->dispatcher_ = nullptr; 81 return static_cast<T*>(self); 82 } 83 84 private: 85 async_wait_t wait_; 86 async_dispatcher_t* dispatcher_ = nullptr; 87 }; 88 89 // An asynchronous wait whose handler is bound to a |async::Wait::Handler| function. 90 // 91 // Prefer using |async::WaitMethod| instead for binding to a fixed class member 92 // function since it is more efficient to dispatch. 93 class Wait final : public WaitBase { 94 public: 95 // Handles completion of asynchronous wait operations. 96 // 97 // The |status| is |ZX_OK| if the wait was satisfied and |signal| is non-null. 98 // The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before 99 // the task's handler ran or the task was canceled. 100 using Handler = fbl::Function<void(async_dispatcher_t* dispatcher, 101 async::Wait* wait, 102 zx_status_t status, 103 const zx_packet_signal_t* signal)>; 104 105 explicit Wait(zx_handle_t object = ZX_HANDLE_INVALID, 106 zx_signals_t trigger = ZX_SIGNAL_NONE, 107 Handler handler = nullptr); 108 ~Wait(); 109 set_handler(Handler handler)110 void set_handler(Handler handler) { handler_ = std::move(handler); } has_handler()111 bool has_handler() const { return !!handler_; } 112 113 private: 114 static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, 115 zx_status_t status, const zx_packet_signal_t* signal); 116 117 Handler handler_; 118 }; 119 120 // An asynchronous wait whose handler is bound to a fixed class member function. 121 // 122 // Usage: 123 // 124 // class Foo { 125 // void Handle(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, 126 // const zx_packet_signal_t* signal) { ... } 127 // async::WaitMethod<Foo, &Foo::Handle> wait_{this}; 128 // }; 129 template <class Class, 130 void (Class::*method)(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, 131 const zx_packet_signal_t* signal)> 132 class WaitMethod final : public WaitBase { 133 public: 134 explicit WaitMethod(Class* instance, 135 zx_handle_t object = ZX_HANDLE_INVALID, 136 zx_signals_t trigger = ZX_SIGNAL_NONE) 137 : WaitBase(object, trigger, &WaitMethod::CallHandler), 138 instance_(instance) {} 139 ~WaitMethod() = default; 140 141 private: CallHandler(async_dispatcher_t * dispatcher,async_wait_t * wait,zx_status_t status,const zx_packet_signal_t * signal)142 static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait, 143 zx_status_t status, const zx_packet_signal_t* signal) { 144 auto self = Dispatch<WaitMethod>(wait); 145 (self->instance_->*method)(dispatcher, self, status, signal); 146 } 147 148 Class* const instance_; 149 }; 150 151 } // namespace async 152