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