blob: 5f6d2b7ee63a479b2e1867996799f6ea68ff3fed [file] [log] [blame]
// Copyright 2018 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.
#ifndef LIB_ASYNC_CPP_EXCEPTION_H_
#define LIB_ASYNC_CPP_EXCEPTION_H_
#include <lib/async/exception.h>
#include <lib/fit/function.h>
namespace async {
// Holds content for an exception packet receiver and its handler.
//
// After successfully binding the port, the client is responsible for
// retaining the structure in memory (and unmodified) until all packets have
// been received by the handler or the dispatcher shuts down.
//
// Multiple packets may be delivered to the same receiver concurrently.
//
// Concrete implementations: |async::Exception|, |async::ExceptionMethod|.
// Please do not create subclasses of ExceptionBase outside of this library.
class ExceptionBase {
protected:
ExceptionBase(zx_handle_t task, uint32_t options,
async_exception_handler_t* handler);
~ExceptionBase();
ExceptionBase(const ExceptionBase&) = delete;
ExceptionBase(ExceptionBase&&) = delete;
ExceptionBase& operator=(const ExceptionBase&) = delete;
ExceptionBase& operator=(ExceptionBase&&) = delete;
template <typename T>
static T* Dispatch(async_exception_t* exception) {
static_assert(offsetof(ExceptionBase, exception_) == 0, "");
auto self = reinterpret_cast<ExceptionBase*>(exception);
return static_cast<T*>(self);
}
public:
// Return true if task's exception port has been bound to.
bool is_bound() const { return !!dispatcher_; }
// Bind the async port to the task's exception port.
//
// Returns |ZX_OK| if the task's exception port is successfully bound to.
// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
// Returns |ZX_ERR_ALREADY_EXISTS| if port is already bound.
// See |zx_task_bind_exception_port()| for other possible errors.
zx_status_t Bind(async_dispatcher_t* dispatcher);
// Unbind the async port from the task's exception port.
//
// Returns |ZX_OK| if the task's exception port is successfully unbound.
// Returns ZX_ERR_NOT_FOUND if the port is not bound.
// See |zx_task_bind_exception_port()| for other possible errors.
zx_status_t Unbind();
// Resume |task| after having processed an exception for it.
// |options| is passed to |zx_task_resume_from_exception()|.
//
// Returns |ZX_OK| if the task was resumed.
// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
// Returns ZX_ERR_NOT_FOUND if the port is not bound.
// Other error values are possible. See the documentation for
// |zx_task_resume_from_exception()|.
zx_status_t Resume(zx_handle_t task, uint32_t options);
private:
async_exception_t exception_;
async_dispatcher_t* dispatcher_ = nullptr;
};
// A receiver whose handler is bound to a |async::Task::Handler| function.
//
// Prefer using |async::ExceptioniReceiverMethod| instead for binding to a fixed class member
// function since it is more efficient to dispatch.
class Exception final : public ExceptionBase {
public:
// Handles receipt of packets containing exception reports.
//
// The |status| is |ZX_OK| if the packet was successfully delivered and |data|
// contains the information from the packet, otherwise |data| is null.
using Handler = fit::function<void(async_dispatcher_t* dispatcher,
async::Exception* exception,
zx_status_t status,
const zx_port_packet_t* report)>;
Exception(zx_handle_t task, uint32_t options, Handler handler);
~Exception();
private:
static void CallHandler(async_dispatcher_t* dispatcher,
async_exception_t* exception,
zx_status_t status, const zx_port_packet_t* report);
Handler handler_;
};
// A receiver whose handler is bound to a fixed class member function.
//
// Usage:
//
// class Foo {
// void Handle(async_dispatcher_t* dispatcher, async::ExceptionBase* receiver, zx_status_t status,
// const zx_port_packet_t* report) { ... }
// async::ExceptionReceiverMethod<Foo, &Foo::Handle> receiver_{this};
// };
template <class Class,
void (Class::*method)(async_dispatcher_t* dispatcher, async::ExceptionBase* receiver,
zx_status_t status, const zx_port_packet_t* report)>
class ExceptionMethod final : public ExceptionBase {
public:
ExceptionMethod(Class* instance,
zx_handle_t task, uint32_t options)
: ExceptionBase(task, options, &ExceptionMethod::CallHandler),
instance_(instance) {}
private:
static void CallHandler(async_dispatcher_t* dispatcher,
async_exception_t* exception,
zx_status_t status, const zx_port_packet_t* report) {
auto self = Dispatch<ExceptionMethod>(exception);
(self->instance_->*method)(dispatcher, self, status, report);
}
Class* const instance_;
};
} // namespace async
#endif // LIB_ASYNC_CPP_EXCEPTION_H_