blob: 61f3cbd15b5ab41b691a9f28bc0f36440b5144d2 [file] [log] [blame]
// 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 <zircon/syscalls.h>
#include <zircon/types.h>
#include <lib/zx/timer.h>
#include <dispatcher-pool/dispatcher-execution-domain.h>
#include <dispatcher-pool/dispatcher-wakeup-event.h>
#include <utility>
namespace dispatcher {
// static
fbl::RefPtr<WakeupEvent> WakeupEvent::Create() {
fbl::AllocChecker ac;
auto ptr = new (&ac) WakeupEvent();
if (!ac.check())
return nullptr;
return fbl::AdoptRef(ptr);
}
zx_status_t WakeupEvent::Activate(fbl::RefPtr<ExecutionDomain> domain,
ProcessHandler process_handler) {
if (process_handler == nullptr)
return ZX_ERR_INVALID_ARGS;
fbl::AutoLock obj_lock(&obj_lock_);
zx::event event;
zx_status_t res = zx::event::create(0, &event);
if (res != ZX_OK)
return res;
res = ActivateLocked(std::move(event), std::move(domain));
if (res != ZX_OK)
return res;
res = WaitOnPortLocked();
if (res != ZX_OK) {
InternalDeactivateLocked();
return res;
}
process_handler_ = std::move(process_handler);
return ZX_OK;
}
void WakeupEvent::Deactivate() {
ProcessHandler old_process_handler;
{
fbl::AutoLock obj_lock(&obj_lock_);
InternalDeactivateLocked();
// If we were previously signalled, we are not any more.
signaled_ = false;
// If we are in the process of actively dispatching, do not discard our
// handler just yet. It is currently being used by the dispatch thread.
// Instead, wait until the dispatch thread unwinds and allow it to clean
// up the handler.
//
// Otherwise, transfer the handler state into local storage and let it
// destruct after we have released the object lock.
if (dispatch_state() != DispatchState::Dispatching) {
ZX_DEBUG_ASSERT((dispatch_state() == DispatchState::Idle) ||
(dispatch_state() == DispatchState::WaitingOnPort));
old_process_handler = std::move(process_handler_);
}
}
}
zx_status_t WakeupEvent::Signal() {
fbl::AutoLock obj_lock(&obj_lock_);
// If we are no longer active, we cannot signal the event.
if (!is_active())
return ZX_ERR_BAD_HANDLE;
// If we are still active, then our handle had better be valid.
ZX_DEBUG_ASSERT(handle_.is_valid());
// Update our internal bookkeeping.
signaled_ = true;
// If we have already fired and are in the process of dispatching, don't
// bother to actually signal the event at the kernel level.
if ((dispatch_state() == DispatchState::DispatchPending) ||
(dispatch_state() == DispatchState::Dispatching)) {
return ZX_OK;
}
zx_status_t res = zx_object_signal(handle_.get(), 0u, ZX_USER_SIGNAL_0);
ZX_DEBUG_ASSERT(res == ZX_OK); // I cannot think of any reason that this should ever fail.
return res;
}
void WakeupEvent::Dispatch(ExecutionDomain* domain) {
ZX_DEBUG_ASSERT(domain != nullptr);
ZX_DEBUG_ASSERT(process_handler_ != nullptr);
{
// Clear the signalled flag. Someone might signal us again during the
// dispatch operation.
fbl::AutoLock obj_lock(&obj_lock_);
ZX_DEBUG_ASSERT(dispatch_state() == DispatchState::Dispatching);
signaled_ = false;
}
zx_status_t res = process_handler_(this);
ProcessHandler old_process_handler;
{
fbl::AutoLock obj_lock(&obj_lock_);
ZX_DEBUG_ASSERT(dispatch_state() == DispatchState::Dispatching);
dispatch_state_ = DispatchState::Idle;
// Was there a problem during processing? If so, make sure that we
// de-activate ourselves.
if (res != ZX_OK) {
InternalDeactivateLocked();
}
// Are we still active? If so, either setup the next port wait
// operation, or re-queue ourselves if we were signalled during the
// dispatch operation.
if (is_active()) {
if (signaled_) {
dispatch_state_ = DispatchState::WaitingOnPort;
res = domain->AddPendingWork(this);
} else {
res = zx_object_signal(handle_.get(), ZX_USER_SIGNAL_0, 0u);
if (res == ZX_OK)
res = WaitOnPortLocked();
}
if (res != ZX_OK) {
dispatch_state_ = DispatchState::Idle;
InternalDeactivateLocked();
}
}
// Have we become deactivated (either during dispatching or just now)?
// If so, move our process handler state outside of our lock so that it
// can safely destruct.
if (!is_active()) {
old_process_handler = std::move(process_handler_);
}
}
}
} // namespace dispatcher