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