blob: 829db44b18b396fef05bcd93ab0b94d8c7584683 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async/task.h>
#include <lib/async/time.h>
#include <lib/async_patterns/cpp/internal/dispatcher_bound_storage.h>
#include <lib/fit/defer.h>
namespace async_patterns::internal {
namespace {
// |SelfDeletingTask| will run the wrapped closure even if the dispatcher
// shuts down after the task has been posted.
class SelfDeletingTask : public async_task_t {
public:
static void Post(async_dispatcher_t* dispatcher, fit::callback<void()> task) {
auto* t = new SelfDeletingTask(dispatcher, std::move(task));
zx_status_t status = async_post_task(dispatcher, t);
if (status != ZX_OK) {
if (status == ZX_ERR_BAD_STATE) {
SelfDeletingTask::Invoke(dispatcher, t, ZX_ERR_BAD_STATE);
return;
}
delete t;
if (status == ZX_ERR_NOT_SUPPORTED) {
ZX_PANIC("The |async_dispatcher_t| must support task posting.");
}
}
ZX_ASSERT(status == ZX_OK);
}
private:
SelfDeletingTask(async_dispatcher_t* dispatcher, fit::callback<void()> task)
: async_task_t({{ASYNC_STATE_INIT}, &SelfDeletingTask::Invoke, async_now(dispatcher)}),
task_(std::move(task)) {}
static void Invoke(async_dispatcher_t* /*unused*/, async_task_t* task, zx_status_t status) {
auto* self = static_cast<SelfDeletingTask*>(task);
self->task_();
delete self;
}
fit::callback<void()> task_;
};
constexpr char kDispatcherBoundThreadSafetyDescription[] =
"|async_patterns::DispatcherBound| is meant to manage thread-unsafe asynchronous objects. "
"Those objects should only be used from synchronized dispatchers. "
"However, the |async_dispatcher_t*| provided is not a synchronized dispatcher.";
} // namespace
SynchronizedBase::SynchronizedBase(async_dispatcher_t* dispatcher)
: checker_(dispatcher, kDispatcherBoundThreadSafetyDescription) {}
DispatcherBoundStorage::~DispatcherBoundStorage() {
ZX_ASSERT_MSG(!op_fn_, "Must call |Destruct| to destroy the dispatcher-bound object.");
}
void DispatcherBoundStorage::ConstructInternal(async_dispatcher_t* dispatcher,
fit::callback<void()> task) {
SelfDeletingTask::Post(dispatcher, std::move(task));
}
void DispatcherBoundStorage::CallInternal(async_dispatcher_t* dispatcher,
fit::callback<void()> member) {
ZX_ASSERT_MSG(op_fn_,
"|async_patterns::DispatcherBound| must first hold an object before "
"making calls on it.");
SelfDeletingTask::Post(dispatcher, std::move(member));
}
void DispatcherBoundStorage::Destruct(async_dispatcher_t* dispatcher) {
ZX_ASSERT(op_fn_);
SelfDeletingTask::Post(dispatcher, [op_fn = std::move(op_fn_)] { op_fn(Operation::kDestruct); });
}
} // namespace async_patterns::internal