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