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 #include <lib/async/cpp/task.h>
6 #include <lib/async/cpp/time.h>
7 
8 #include <utility>
9 
10 namespace async {
11 namespace internal {
12 
13 struct RetainedTask : public async_task_t {
RetainedTaskasync::internal::RetainedTask14     RetainedTask(fbl::Closure handler, zx::time deadline)
15         : async_task_t{{ASYNC_STATE_INIT}, &RetainedTask::Handler, deadline.get()},
16           handler(static_cast<fbl::Closure&&>(handler)) {}
17 
18     fbl::Closure handler;
19 
Handlerasync::internal::RetainedTask20     static void Handler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
21         auto self = static_cast<RetainedTask*>(task);
22         if (status == ZX_OK)
23             self->handler();
24         delete self;
25     }
26 };
27 
28 } // namespace internal
29 
PostTask(async_dispatcher_t * dispatcher,fbl::Closure handler)30 zx_status_t PostTask(async_dispatcher_t* dispatcher, fbl::Closure handler) {
31     return PostTaskForTime(dispatcher, static_cast<fbl::Closure&&>(handler),
32                            async::Now(dispatcher));
33 }
34 
PostDelayedTask(async_dispatcher_t * dispatcher,fbl::Closure handler,zx::duration delay)35 zx_status_t PostDelayedTask(async_dispatcher_t* dispatcher, fbl::Closure handler, zx::duration delay) {
36     return PostTaskForTime(dispatcher, static_cast<fbl::Closure&&>(handler),
37                            async::Now(dispatcher) + delay);
38 }
39 
PostTaskForTime(async_dispatcher_t * dispatcher,fbl::Closure handler,zx::time deadline)40 zx_status_t PostTaskForTime(async_dispatcher_t* dispatcher, fbl::Closure handler, zx::time deadline) {
41     auto* task = new internal::RetainedTask(static_cast<fbl::Closure&&>(handler), deadline);
42     zx_status_t status = async_post_task(dispatcher, task);
43     if (status != ZX_OK)
44         delete task;
45     return status;
46 }
47 
TaskBase(async_task_handler_t * handler)48 TaskBase::TaskBase(async_task_handler_t* handler)
49     : task_{{ASYNC_STATE_INIT}, handler, ZX_TIME_INFINITE} {}
50 
~TaskBase()51 TaskBase::~TaskBase() {
52     if (dispatcher_) {
53         // Failure to cancel here may result in a dangling pointer...
54         zx_status_t status = async_cancel_task(dispatcher_, &task_);
55         ZX_ASSERT_MSG(status == ZX_OK, "status=%d", status);
56     }
57 }
58 
Post(async_dispatcher_t * dispatcher)59 zx_status_t TaskBase::Post(async_dispatcher_t* dispatcher) {
60     return PostForTime(dispatcher, async::Now(dispatcher));
61 }
62 
PostDelayed(async_dispatcher_t * dispatcher,zx::duration delay)63 zx_status_t TaskBase::PostDelayed(async_dispatcher_t* dispatcher, zx::duration delay) {
64     return PostForTime(dispatcher, async::Now(dispatcher) + delay);
65 }
66 
PostForTime(async_dispatcher_t * dispatcher,zx::time deadline)67 zx_status_t TaskBase::PostForTime(async_dispatcher_t* dispatcher, zx::time deadline) {
68     if (dispatcher_)
69         return ZX_ERR_ALREADY_EXISTS;
70 
71     dispatcher_ = dispatcher;
72     task_.deadline = deadline.get();
73     zx_status_t status = async_post_task(dispatcher, &task_);
74     if (status != ZX_OK) {
75         dispatcher_ = nullptr;
76     }
77     return status;
78 }
79 
Cancel()80 zx_status_t TaskBase::Cancel() {
81     if (!dispatcher_)
82         return ZX_ERR_NOT_FOUND;
83 
84     async_dispatcher_t* dispatcher = dispatcher_;
85     dispatcher_ = nullptr;
86 
87     zx_status_t status = async_cancel_task(dispatcher, &task_);
88     // |dispatcher| is required to be single-threaded, Cancel() is
89     // only supposed to be called on |dispatcher|'s thread, and we
90     // verified that the task was pending before calling
91     // async_cancel_task(). Assuming that |dispatcher| does not yield
92     // between removing the task and invoking the task's handler,
93     // |task_| must have been pending with |dispatcher|.
94     ZX_DEBUG_ASSERT(status != ZX_ERR_NOT_FOUND);
95     return status;
96 }
97 
Task(Handler handler)98 Task::Task(Handler handler)
99     : TaskBase(&Task::CallHandler), handler_(std::move(handler)) {}
100 
101 Task::~Task() = default;
102 
CallHandler(async_dispatcher_t * dispatcher,async_task_t * task,zx_status_t status)103 void Task::CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
104     auto self = Dispatch<Task>(task);
105     self->handler_(dispatcher, self, status);
106 }
107 
TaskClosure(fbl::Closure handler)108 TaskClosure::TaskClosure(fbl::Closure handler)
109     : TaskBase(&TaskClosure::CallHandler), handler_(std::move(handler)) {}
110 
111 TaskClosure::~TaskClosure() = default;
112 
CallHandler(async_dispatcher_t * dispatcher,async_task_t * task,zx_status_t status)113 void TaskClosure::CallHandler(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
114     auto self = Dispatch<TaskClosure>(task); // must do this if status is not ok
115     if (status == ZX_OK) {
116         self->handler_();
117     }
118 }
119 
120 } // namespace async
121