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/task.h>
9 #include <lib/zx/time.h>
10 
11 #include <utility>
12 
13 namespace async {
14 
15 // Posts a task to invoke |handler| with a deadline of now.
16 //
17 // The handler will not run if the dispatcher shuts down before it comes due.
18 //
19 // Returns |ZX_OK| if the task was successfully posted.
20 // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
21 // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
22 zx_status_t PostTask(async_dispatcher_t* dispatcher, fbl::Closure handler);
23 
24 // Posts a task to invoke |handler| with a deadline expressed as a |delay| from now.
25 //
26 // The handler will not run if the dispatcher shuts down before it comes due.
27 //
28 // Returns |ZX_OK| if the task was successfully posted.
29 // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
30 // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
31 zx_status_t PostDelayedTask(async_dispatcher_t* dispatcher, fbl::Closure handler, zx::duration delay);
32 
33 // Posts a task to invoke |handler| with the specified |deadline|.
34 //
35 // The handler will not run if the dispatcher shuts down before it comes due.
36 //
37 // Returns |ZX_OK| if the task was successfully posted.
38 // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
39 // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
40 zx_status_t PostTaskForTime(async_dispatcher_t* dispatcher, fbl::Closure handler, zx::time deadline);
41 
42 // Holds context for a task and its handler, with RAII semantics.
43 // Automatically cancels the task when it goes out of scope.
44 //
45 // After successfully posting the task, the client is responsible for retaining
46 // the structure in memory (and unmodified) until the task's handler runs, the task
47 // is successfully canceled, or the dispatcher shuts down.  Thereafter, the task
48 // may be posted again or destroyed.
49 //
50 // This class must only be used with single-threaded asynchronous dispatchers
51 // and must only be accessed on the dispatch thread since it lacks internal
52 // synchronization of its state.
53 //
54 // Concrete implementations: |async::Task|, |async::TaskMethod|,
55 //   |async::TaskClosure|, |async::TaskClosureMethod|.
56 // Please do not create subclasses of TaskBase outside of this library.
57 class TaskBase {
58 protected:
59     explicit TaskBase(async_task_handler_t* handler);
60     ~TaskBase();
61 
62     TaskBase(const TaskBase&) = delete;
63     TaskBase(TaskBase&&) = delete;
64     TaskBase& operator=(const TaskBase&) = delete;
65     TaskBase& operator=(TaskBase&&) = delete;
66 
67 public:
68     // Returns true if the task has been posted and has not yet executed or been canceled.
is_pending()69     bool is_pending() const { return dispatcher_ != nullptr; }
70 
71     // The last deadline with which the task was posted, or |zx::time::infinite()|
72     // if it has never been posted.
last_deadline()73     zx::time last_deadline() const { return zx::time(task_.deadline); }
74 
75     // Posts a task to invoke the handler with a deadline of now.
76     //
77     // Returns |ZX_OK| if the task was successfully posted.
78     // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down or if the
79     // task is already pending.
80     // Returns |ZX_ERR_ALREADY_EXISTS| if the task is already pending.
81     // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
82     zx_status_t Post(async_dispatcher_t* dispatcher);
83 
84     // Posts a task to invoke the handler with a deadline expressed as a |delay| from now.
85     //
86     // Returns |ZX_OK| if the task was successfully posted.
87     // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down or if the
88     // task is already pending.
89     // Returns |ZX_ERR_ALREADY_EXISTS| if the task is already pending.
90     // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
91     zx_status_t PostDelayed(async_dispatcher_t* dispatcher, zx::duration delay);
92 
93     // Posts a task to invoke the handler with the specified |deadline|.
94     //
95     // The |deadline| must be expressed in the time base used by the asynchronous
96     // dispatcher (usually |ZX_CLOCK_MONOTONIC| except in unit tests).
97     // See |async_now()| for details.
98     //
99     // Returns |ZX_OK| if the task was successfully posted.
100     // Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down or if the
101     // task is already pending.
102     // Returns |ZX_ERR_ALREADY_EXISTS| if the task is already pending.
103     // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
104     zx_status_t PostForTime(async_dispatcher_t* dispatcher, zx::time deadline);
105 
106     // Cancels the task.
107     //
108     // If successful, the task's handler will not run.
109     //
110     // Returns |ZX_OK| if the task was pending and it has been successfully
111     // canceled; its handler will not run again and can be released immediately.
112     // Returns |ZX_ERR_NOT_FOUND| if task was not pending either because its
113     // handler already ran, or the task had not been posted.
114     // Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
115     zx_status_t Cancel();
116 
117 protected:
118     template <typename T>
Dispatch(async_task_t * task)119     static T* Dispatch(async_task_t* task) {
120         static_assert(offsetof(TaskBase, task_) == 0, "");
121         auto self = reinterpret_cast<TaskBase*>(task);
122         self->dispatcher_ = nullptr;
123         return static_cast<T*>(self);
124     }
125 
126 private:
127     async_task_t task_;
128     async_dispatcher_t* dispatcher_ = nullptr;
129 };
130 
131 // A task whose handler is bound to a |async::Task::Handler| function.
132 //
133 // Prefer using |async::TaskMethod| instead for binding to a fixed class member
134 // function since it is more efficient to dispatch.
135 class Task final : public TaskBase {
136 public:
137     // Handles execution of a posted task.
138     //
139     // The |status| is |ZX_OK| if the task's deadline elapsed and the task should run.
140     // The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before
141     // the task's handler ran or the task was canceled.
142     using Handler = fbl::Function<void(async_dispatcher_t* dispatcher, async::Task* task, zx_status_t status)>;
143 
144     explicit Task(Handler handler = nullptr);
145     ~Task();
146 
set_handler(Handler handler)147     void set_handler(Handler handler) { handler_ = std::move(handler); }
has_handler()148     bool has_handler() const { return !!handler_; }
149 
150 private:
151     static void CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status);
152 
153     Handler handler_;
154 };
155 
156 // A task whose handler is bound to a fixed class member function.
157 //
158 // Usage:
159 //
160 // class Foo {
161 //     void Handle(async_dispatcher_t* dispatcher, async::TaskBase* task, zx_status_t status) { ... }
162 //     async::TaskMethod<Foo, &Foo::Handle> task_{this};
163 // };
164 template <class Class,
165           void (Class::*method)(async_dispatcher_t* dispatcher, async::TaskBase* task, zx_status_t status)>
166 class TaskMethod final : public TaskBase {
167 public:
TaskMethod(Class * instance)168     explicit TaskMethod(Class* instance)
169         : TaskBase(&TaskMethod::CallHandler), instance_(instance) {}
170     ~TaskMethod() = default;
171 
172 private:
CallHandler(async_dispatcher_t * dispatcher,async_task_t * task,zx_status_t status)173     static void CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
174         auto self = Dispatch<TaskMethod>(task);
175         (self->instance_->*method)(dispatcher, self, status);
176     }
177 
178     Class* const instance_;
179 };
180 
181 // A task whose handler is bound to a |fbl::Closure| function with no arguments.
182 // The closure is not invoked when errors occur since it doesn't have a |zx_status_t|
183 // argument.
184 //
185 // Prefer using |async::TaskClosureMethod| instead for binding to a fixed class member
186 // function since it is more efficient to dispatch.
187 class TaskClosure final : public TaskBase {
188 public:
189     explicit TaskClosure(fbl::Closure handler = nullptr);
190     ~TaskClosure();
191 
set_handler(fbl::Closure handler)192     void set_handler(fbl::Closure handler) { handler_ = std::move(handler); }
has_handler()193     bool has_handler() const { return !!handler_; }
194 
195 private:
196     static void CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status);
197 
198     fbl::Closure handler_;
199 };
200 
201 // A task whose handler is bound to a fixed class member function with no arguments.
202 // The closure is not invoked when errors occur since it doesn't have a |zx_status_t|
203 // argument.
204 //
205 // Usage:
206 //
207 // class Foo {
208 //     void Handle() { ... }
209 //     async::TaskClosureMethod<Foo, &Foo::Handle> trap_{this};
210 // };
211 template <class Class,
212           void (Class::*method)()>
213 class TaskClosureMethod final : public TaskBase {
214 public:
TaskClosureMethod(Class * instance)215     explicit TaskClosureMethod(Class* instance)
216         : TaskBase(&TaskClosureMethod::CallHandler), instance_(instance) {}
217     ~TaskClosureMethod() = default;
218 
219 private:
CallHandler(async_dispatcher_t * dispatcher,async_task_t * task,zx_status_t status)220     static void CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
221         auto self = Dispatch<TaskClosureMethod>(task); // must do this if status is not ok
222         if (status == ZX_OK) {
223             (self->instance_->*method)();
224         }
225     }
226 
227     Class* const instance_;
228 };
229 
230 } // namespace async
231