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