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