| // 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 |