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