blob: ec1e2b710f94a4f9837cb1a9b6569982b10c1c2b [file] [log] [blame]
// Copyright 2019 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_irq_H_
#define LIB_ASYNC_CPP_irq_H_
#include <lib/async/irq.h>
#include <lib/fit/function.h>
#include <utility>
namespace async {
// Holds context for an irq and its handler, with RAII semantics.
// Automatically unbinds the irq when it goes out of scope.
//
// This class must only be used with single-threaded asynchronous dispatchers
// and must only be accessed on the dispatch thread since it lacks internal
// synchronization of its state.
//
// Concrete implementations: |async::Irq|, |async::IrqMethod|.
// Please do not create subclasses of IrqBase outside of this library.
class IrqBase {
protected:
explicit IrqBase(zx_handle_t object, zx_signals_t trigger, uint32_t options,
async_irq_handler_t* handler);
~IrqBase();
IrqBase(const IrqBase&) = delete;
IrqBase(IrqBase&&) = delete;
IrqBase& operator=(const IrqBase&) = delete;
IrqBase& operator=(IrqBase&&) = delete;
public:
// Gets or sets the interrupt object.
zx_handle_t object() const { return irq_.object; }
void set_object(zx_handle_t object) { irq_.object = object; }
// Begins asynchronously waiting for the object to receive one or more of
// the trigger signals. Invokes the handler when the irq is triggered.
//
// Returns |ZX_OK| if the irq was successfully begun.
// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
zx_status_t Begin(async_dispatcher_t* dispatcher);
// Cancels the irq.
//
// If successful, the irq's handler will not run.
//
// Returns |ZX_OK| if the irq was pending and it has been successfully
// canceled; its handler will not run again and can be released immediately.
// Returns |ZX_ERR_NOT_FOUND| if there was no pending irq either because it
// already completed, or had not been started.
// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
zx_status_t Cancel();
protected:
template <typename T>
static T* Dispatch(async_irq* irq) {
static_assert(offsetof(IrqBase, irq_) == 0, "");
auto self = reinterpret_cast<IrqBase*>(irq);
return static_cast<T*>(self);
}
private:
async_irq_t irq_;
async_dispatcher_t* dispatcher_ = nullptr;
};
// An asynchronous IRQ whose handler is bound to a |async::irq::Handler| function.
//
// Prefer using |async::IrqMethod| instead for binding to a fixed class member
// function since it is more efficient to dispatch.
class Irq final : public IrqBase {
public:
// Handles completion of asynchronous irq operations.
//
// The |status| is |ZX_OK| if the irq was satisfied and |signal| is non-null.
// The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before
// the task's handler ran or the task was canceled.
using Handler = fit::function<void(async_dispatcher_t* dispatcher, async::Irq* irq,
zx_status_t status, const zx_packet_interrupt_t* interrupt)>;
// Creates a irq with options == 0.
explicit Irq(zx_handle_t object = ZX_HANDLE_INVALID, zx_signals_t trigger = ZX_SIGNAL_NONE,
Handler handler = nullptr)
: Irq(object, trigger, 0, std::move(handler)) {}
// Creates a irq with the provided |options|.
explicit Irq(zx_handle_t object, zx_signals_t trigger, uint32_t options,
Handler handler = nullptr);
~Irq();
void set_handler(Handler handler) { handler_ = std::move(handler); }
bool has_handler() const { return !!handler_; }
private:
static void CallHandler(async_dispatcher_t* dispatcher, async_irq_t* irq, zx_status_t status,
const zx_packet_interrupt_t* signal);
Handler handler_;
};
// An asynchronous irq whose handler is bound to a fixed class member function.
//
// Usage:
//
// class Foo {
// void Handle(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
// const zx_packet_interrupt_t* interrupt) { ... }
// async::IrqMethod<Foo, &Foo::Handle> irq_{this};
// };
template <class Class,
void (Class::*method)(async_dispatcher_t* dispatcher, async::IrqBase* irq,
zx_status_t status, const zx_packet_interrupt_t* interrupt)>
class IrqMethod final : public IrqBase {
public:
// Creates a irqMethod with options == 0.
explicit IrqMethod(Class* instance, zx_handle_t object = ZX_HANDLE_INVALID,
zx_signals_t trigger = ZX_SIGNAL_NONE)
: IrqMethod(instance, object, trigger, 0) {}
// Creates a IrqMethod with the provided |options|.
explicit IrqMethod(Class* instance, zx_handle_t object, zx_signals_t trigger, uint32_t options)
: IrqBase(object, trigger, options, &IrqMethod::CallHandler), instance_(instance) {}
~IrqMethod() = default;
private:
static void CallHandler(async_dispatcher_t* dispatcher, async_irq_t* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt) {
auto self = Dispatch<IrqMethod>(irq);
(self->instance_->*method)(dispatcher, self, status, interrupt);
}
Class* const instance_;
};
} // namespace async
#endif // LIB_ASYNC_CPP_irq_H_