Skip to content

Commit

Permalink
[async] Add RAII helpers for waits and tasks.
Browse files Browse the repository at this point in the history
This makes the API a bit more convenient to use.

Fixed an inconsistency in the handling of cancelation errors.
We were previously checking for MX_ERR_BAD_STATE which can't happen
during cancelation.

Fixed some minor formatting inconsistencies introduced by the
mxtl->fbl rename.

Added some clarification around thread-safety.

Change-Id: If273a42eea2b869d63c79e47782c28fade339c92
  • Loading branch information
Jeff Brown committed Sep 8, 2017
1 parent f9bf251 commit 56fa14e
Show file tree
Hide file tree
Showing 15 changed files with 544 additions and 35 deletions.
4 changes: 4 additions & 0 deletions system/ulib/async/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ config("async_config") {
source_set("async") {
# Don't forget to update rules.mk as well for the Magenta build.
sources = [
"include/async/auto_task.h",
"include/async/auto_wait.h",
"include/async/receiver.h",
"include/async/task.h",
"include/async/wait.h",
"include/async/wait_with_timeout.h",
"auto_task.cpp",
"auto_wait.cpp",
"receiver.cpp",
"task.cpp",
"wait.cpp",
Expand Down
6 changes: 6 additions & 0 deletions system/ulib/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ See [async/default.h](include/async/default.h) for details.
the C API with a more convenient fbl::Function<> callback based interface
for use in C++.

`AutoWait` in [async/auto_wait.h](include/async/auto_wait.h) is an RAII helper
which cancels the wait when it goes out of scope.

`AutoTask` in [async/auto_task.h](include/async/auto_task.h) is an RAII helper
which cancels the task when it goes out of scope.

There is also a special `WaitWithTimeout` helper defined in
[async/wait_with_timeout.h](include/async/wait_with_timeout.h)
which combines a wait operation together with a pending task that invokes the
Expand Down
55 changes: 55 additions & 0 deletions system/ulib/async/auto_task.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2017 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 <async/auto_task.h>

#include <magenta/assert.h>

namespace async {

AutoTask::AutoTask(async_t* async, mx_time_t deadline, uint32_t flags)
: async_task_t{{ASYNC_STATE_INIT}, &AutoTask::CallHandler, deadline, flags, {}},
async_(async) {
MX_DEBUG_ASSERT(async_);
}

AutoTask::~AutoTask() {
Cancel();
}

mx_status_t AutoTask::Post() {
MX_DEBUG_ASSERT(!pending_);

mx_status_t status = async_post_task(async_, this);
if (status == MX_OK)
pending_ = true;

return status;
}

void AutoTask::Cancel() {
if (!pending_)
return;

mx_status_t status = async_cancel_task(async_, this);
MX_DEBUG_ASSERT_MSG(status == MX_OK, "status=%d", status);

pending_ = false;
}

async_task_result_t AutoTask::CallHandler(async_t* async, async_task_t* task,
mx_status_t status) {
auto self = static_cast<AutoTask*>(task);
MX_DEBUG_ASSERT(self->pending_);
self->pending_ = false;

async_task_result_t result = self->handler_(async, status);
if (result == ASYNC_TASK_REPEAT && status == MX_OK) {
MX_DEBUG_ASSERT(!self->pending_);
self->pending_ = true;
}
return result;
}

} // namespace async
55 changes: 55 additions & 0 deletions system/ulib/async/auto_wait.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2017 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 <async/auto_wait.h>

#include <magenta/assert.h>

namespace async {

AutoWait::AutoWait(async_t* async, mx_handle_t object, mx_signals_t trigger, uint32_t flags)
: async_wait_t{{ASYNC_STATE_INIT}, &AutoWait::CallHandler, object, trigger, flags, {}},
async_(async) {
MX_DEBUG_ASSERT(async_);
}

AutoWait::~AutoWait() {
Cancel();
}

mx_status_t AutoWait::Begin() {
MX_DEBUG_ASSERT(!pending_);

mx_status_t status = async_begin_wait(async_, this);
if (status == MX_OK)
pending_ = true;

return status;
}

void AutoWait::Cancel() {
if (!pending_)
return;

mx_status_t status = async_cancel_wait(async_, this);
MX_DEBUG_ASSERT_MSG(status == MX_OK, "status=%d", status);

pending_ = false;
}

async_wait_result_t AutoWait::CallHandler(async_t* async, async_wait_t* wait,
mx_status_t status, const mx_packet_signal_t* signal) {
auto self = static_cast<AutoWait*>(wait);
MX_DEBUG_ASSERT(self->pending_);
self->pending_ = false;

async_wait_result_t result = self->handler_(async, status, signal);
if (result == ASYNC_WAIT_AGAIN && status == MX_OK) {
MX_DEBUG_ASSERT(!self->pending_);
self->pending_ = true;
}
return result;
}

} // namespace async
94 changes: 94 additions & 0 deletions system/ulib/async/include/async/auto_task.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2017 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.

#pragma once

#include <async/task.h>

#ifdef __cplusplus

#include <fbl/function.h>
#include <fbl/macros.h>

namespace async {

// C++ wrapper for a pending task which is automatically canceled when
// it goes out of scope.
//
// This class is NOT thread-safe; it can only be used with single-threaded
// asynchronous dispatchers.
class AutoTask final : private async_task_t {
public:
// Handles execution of a posted task.
//
// Reports the |status| of the task. If the status is |MX_OK| then the
// task ran, otherwise the task did not run.
//
// The result indicates whether the task should be repeated; it may
// modify the task's properties (such as the deadline) before returning.
//
// The result must be |ASYNC_TASK_FINISHED| if |status| was not |MX_OK|.
//
// It is safe for the handler to destroy itself when returning |ASYNC_TASK_FINISHED|.
using Handler = fbl::Function<async_task_result_t(async_t* async,
mx_status_t status)>;

// Initializes the properties of the task and binds it to an asynchronous
// dispatcher.
explicit AutoTask(async_t* async,
mx_time_t deadline = MX_TIME_INFINITE,
uint32_t flags = 0u);

// Destroys the task.
//
// The task is canceled automatically if it is still pending.
~AutoTask();

// Gets the asynchronous dispatcher to which this task has been bound.
async_t* async() const { return async_; }

// Returns true if the |Post()| was called successfully but the task has
// not started execution or been canceled.
bool is_pending() const { return pending_; }

// Gets or sets the handler to invoke when the task becomes due.
// Must be set before posting the task.
const Handler& handler() const { return handler_; }
void set_handler(Handler handler) { handler_ = fbl::move(handler); }

// The time when the task should run.
mx_time_t deadline() const { return async_task_t::deadline; }
void set_deadline(mx_time_t deadline) { async_task_t::deadline = deadline; }

// Valid flags: |ASYNC_FLAG_HANDLE_SHUTDOWN|.
uint32_t flags() const { return async_task_t::flags; }
void set_flags(uint32_t flags) { async_task_t::flags = flags; }

// Posts a task to run on or after its deadline following all posted
// tasks with lesser or equal deadlines.
//
// See |async_post_task()| for details.
mx_status_t Post();

// Cancels the task.
//
// This method does nothing if the wait is not pending.
//
// See |async_cancel_task()| for details.
void Cancel();

private:
static async_task_result_t CallHandler(async_t* async, async_task_t* task,
mx_status_t status);

async_t* const async_;
Handler handler_;
bool pending_ = false;

DISALLOW_COPY_ASSIGN_AND_MOVE(AutoTask);
};

} // namespace async

#endif // __cplusplus
103 changes: 103 additions & 0 deletions system/ulib/async/include/async/auto_wait.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2017 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.

#pragma once

#include <async/wait.h>

#ifdef __cplusplus

#include <fbl/function.h>
#include <fbl/macros.h>

namespace async {

// C++ wrapper for a pending wait operation which is automatically canceled
// when it goes out of scope.
//
// This class is NOT thread-safe; it can only be used with single-threaded
// asynchronous dispatchers.
class AutoWait final : private async_wait_t {
public:
// Handles completion of asynchronous wait operations.
//
// Reports the |status| of the wait. If the status is |MX_OK| then |signal|
// describes the signal which was received, otherwise |signal| is null.
//
// The result indicates whether the wait should be repeated; it may
// modify the wait's properties (such as the trigger) before returning.
//
// The result must be |ASYNC_WAIT_FINISHED| if |status| was not |MX_OK|.
//
// It is safe for the handler to destroy itself when returning |ASYNC_WAIT_FINISHED|.
using Handler = fbl::Function<async_wait_result_t(async_t* async,
mx_status_t status,
const mx_packet_signal_t* signal)>;

// Initializes the properties of the wait operation and binds it to an
// asynchronous dispatcher.
explicit AutoWait(async_t* async,
mx_handle_t object = MX_HANDLE_INVALID,
mx_signals_t trigger = MX_SIGNAL_NONE,
uint32_t flags = 0u);

// Destroys the wait operation.
//
// The wait is canceled automatically if it is still pending.
~AutoWait();

// Gets the asynchronous dispatcher to which this wait has been bound.
async_t* async() const { return async_; }

// Returns true if |Begin()| was called successfully but the wait has not
// completed or been canceled.
bool is_pending() const { return pending_; }

// Gets or sets the handler to invoke when the wait completes.
// Must be set before beginning the wait.
const Handler& handler() const { return handler_; }
void set_handler(Handler handler) { handler_ = fbl::move(handler); }

// The object to wait for signals on.
mx_handle_t object() const { return async_wait_t::object; }
void set_object(mx_handle_t object) { async_wait_t::object = object; }

// The set of signals to wait for.
mx_signals_t trigger() const { return async_wait_t::trigger; }
void set_trigger(mx_signals_t trigger) { async_wait_t::trigger = trigger; }

// Valid flags: |ASYNC_FLAG_HANDLE_SHUTDOWN|.
uint32_t flags() const { return async_wait_t::flags; }
void set_flags(uint32_t flags) { async_wait_t::flags = flags; }

// Begins asynchronously waiting for the object to receive one or more of
// the trigger signals.
//
// This method must not be called when the wait is already pending.
//
// See |async_begin_wait()| for details.
mx_status_t Begin();

// Cancels the wait.
//
// This method does nothing if the wait is not pending.
//
// See |async_cancel_wait()| for details.
void Cancel();

private:
static async_wait_result_t CallHandler(async_t* async, async_wait_t* wait,
mx_status_t status,
const mx_packet_signal_t* signal);

async_t* const async_;
Handler handler_;
bool pending_ = false;

DISALLOW_COPY_ASSIGN_AND_MOVE(AutoWait);
};

} // namespace async

#endif // __cplusplus
2 changes: 2 additions & 0 deletions system/ulib/async/include/async/loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ __END_CDECLS
namespace async {

// C++ wrapper for an asynchronous dispatch loop.
//
// This class is thread-safe.
class Loop {
public:
// Creates a message loop.
Expand Down
8 changes: 5 additions & 3 deletions system/ulib/async/include/async/receiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ typedef void(async_receiver_handler_t)(async_t* async,
// The same instance may be used to receive arbitrarily many queued packets.
//
// It is customary to aggregate (in C) or subclass (in C++) this structure
// to allow the handler to retain additional information about the receiver.
// to allow the receiver context to retain additional state for its handler.
//
// See also |async::Receiver|.
struct async_receiver {
Expand Down Expand Up @@ -72,6 +72,8 @@ namespace async {

// C++ wrapper for a packet receiver.
// The same instance may be used to receive arbitrarily many queued packets.
//
// This class is thread-safe.
class Receiver final : private async_receiver_t {
public:
// Receives packets containing user supplied data.
Expand All @@ -83,8 +85,8 @@ class Receiver final : private async_receiver_t {
// It is safe for the handler to destroy itself when there are no remaining
// packets pending delivery to it.
using Handler = fbl::Function<void(async_t* async,
mx_status_t status,
const mx_packet_user_t* data)>;
mx_status_t status,
const mx_packet_user_t* data)>;

// Initializes the properties of the receiver.
explicit Receiver(uint32_t flags = 0u);
Expand Down
Loading

0 comments on commit 56fa14e

Please sign in to comment.