text: // 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.
  
  #include <lib/async/cpp/irq.h>
  #include <zircon/assert.h>
  
  #include <utility>
  
  namespace async {
  
  IrqBase::IrqBase(zx_handle_t object, zx_signals_t trigger, uint32_t options,
                   async_irq_handler_t* handler)
      : irq_{{ASYNC_STATE_INIT}, handler, object} {}
  
  IrqBase::~IrqBase() {
    if (dispatcher_) {
      // Failure to cancel here may result in a dangling pointer...
      zx_status_t status = async_unbind_irq(dispatcher_, &irq_);
      ZX_ASSERT_MSG(status == ZX_OK, "status=%d", status);
    }
  }
  
  zx_status_t IrqBase::Begin(async_dispatcher_t* dispatcher) {
    if (dispatcher_)
      return ZX_ERR_ALREADY_EXISTS;
  
    dispatcher_ = dispatcher;
    zx_status_t status = async_bind_irq(dispatcher, &irq_);
    if (status != ZX_OK) {
      dispatcher_ = nullptr;
    }
    return status;
  }
  
  zx_status_t IrqBase::Cancel() {
    if (!dispatcher_)
      return ZX_ERR_NOT_FOUND;
  
    async_dispatcher_t* dispatcher = dispatcher_;
    dispatcher_ = nullptr;
  
    zx_status_t status = async_unbind_irq(dispatcher, &irq_);
    // |dispatcher| is required to be single-threaded, Cancel() is
    // only supposed to be called on |dispatcher|'s thread, and
    // we verified that the wait was pending before calling
    // async_cancel_wait(). Assuming that |dispatcher| never queues
    // a wait, |wait_| must have been pending with |dispatcher|.
    ZX_DEBUG_ASSERT(status != ZX_ERR_NOT_FOUND);
    return status;
  }
  
  Irq::Irq(zx_handle_t object, zx_signals_t trigger, uint32_t options, Handler handler)
      : IrqBase(object, trigger, options, &Irq::CallHandler), handler_(std::move(handler)) {}
  
  Irq::~Irq() = default;
  
  void Irq::CallHandler(async_dispatcher_t* dispatcher, async_irq_t* irq, zx_status_t status,
                        const zx_packet_interrupt_t* signal) {
    auto self = Dispatch<Irq>(irq);
    self->handler_(dispatcher, self, status, signal);
  }
  
  }  // namespace async
  