1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <zircon/syscalls.h> 6#include <zircon/types.h> 7#include <lib/zx/timer.h> 8 9#include <dispatcher-pool/dispatcher-execution-domain.h> 10#include <dispatcher-pool/dispatcher-wakeup-event.h> 11 12namespace dispatcher { 13 14// static 15fbl::RefPtr<WakeupEvent> WakeupEvent::Create() { 16 fbl::AllocChecker ac; 17 18 auto ptr = new (&ac) WakeupEvent(); 19 if (!ac.check()) 20 return nullptr; 21 22 return fbl::AdoptRef(ptr); 23} 24 25zx_status_t WakeupEvent::Activate(fbl::RefPtr<ExecutionDomain> domain, 26 ProcessHandler process_handler) { 27 if (process_handler == nullptr) 28 return ZX_ERR_INVALID_ARGS; 29 30 fbl::AutoLock obj_lock(&obj_lock_); 31 zx::event event; 32 zx_status_t res = zx::event::create(0, &event); 33 if (res != ZX_OK) 34 return res; 35 36 res = ActivateLocked(fbl::move(event), fbl::move(domain)); 37 if (res != ZX_OK) 38 return res; 39 40 res = WaitOnPortLocked(); 41 if (res != ZX_OK) { 42 InternalDeactivateLocked(); 43 return res; 44 } 45 46 process_handler_ = fbl::move(process_handler); 47 48 return ZX_OK; 49} 50 51void WakeupEvent::Deactivate() { 52 ProcessHandler old_process_handler; 53 54 { 55 fbl::AutoLock obj_lock(&obj_lock_); 56 InternalDeactivateLocked(); 57 58 // If we were previously signalled, we are not any more. 59 signaled_ = false; 60 61 // If we are in the process of actively dispatching, do not discard our 62 // handler just yet. It is currently being used by the dispatch thread. 63 // Instead, wait until the dispatch thread unwinds and allow it to clean 64 // up the handler. 65 // 66 // Otherwise, transfer the handler state into local storage and let it 67 // destruct after we have released the object lock. 68 if (dispatch_state() != DispatchState::Dispatching) { 69 ZX_DEBUG_ASSERT((dispatch_state() == DispatchState::Idle) || 70 (dispatch_state() == DispatchState::WaitingOnPort)); 71 old_process_handler = fbl::move(process_handler_); 72 } 73 } 74} 75 76zx_status_t WakeupEvent::Signal() { 77 fbl::AutoLock obj_lock(&obj_lock_); 78 79 80 // If we are no longer active, we cannot signal the event. 81 if (!is_active()) 82 return ZX_ERR_BAD_HANDLE; 83 84 // If we are still active, then our handle had better be valid. 85 ZX_DEBUG_ASSERT(handle_.is_valid()); 86 87 // Update our internal bookkeeping. 88 signaled_ = true; 89 90 // If we have already fired and are in the process of dispatching, don't 91 // bother to actually signal the event at the kernel level. 92 if ((dispatch_state() == DispatchState::DispatchPending) || 93 (dispatch_state() == DispatchState::Dispatching)) { 94 return ZX_OK; 95 } 96 97 zx_status_t res = zx_object_signal(handle_.get(), 0u, ZX_USER_SIGNAL_0); 98 ZX_DEBUG_ASSERT(res == ZX_OK); // I cannot think of any reason that this should ever fail. 99 100 return res; 101} 102 103void WakeupEvent::Dispatch(ExecutionDomain* domain) { 104 ZX_DEBUG_ASSERT(domain != nullptr); 105 ZX_DEBUG_ASSERT(process_handler_ != nullptr); 106 107 { 108 // Clear the signalled flag. Someone might signal us again during the 109 // dispatch operation. 110 fbl::AutoLock obj_lock(&obj_lock_); 111 ZX_DEBUG_ASSERT(dispatch_state() == DispatchState::Dispatching); 112 signaled_ = false; 113 } 114 115 zx_status_t res = process_handler_(this); 116 ProcessHandler old_process_handler; 117 { 118 fbl::AutoLock obj_lock(&obj_lock_); 119 ZX_DEBUG_ASSERT(dispatch_state() == DispatchState::Dispatching); 120 dispatch_state_ = DispatchState::Idle; 121 122 // Was there a problem during processing? If so, make sure that we 123 // de-activate ourselves. 124 if (res != ZX_OK) { 125 InternalDeactivateLocked(); 126 } 127 128 // Are we still active? If so, either setup the next port wait 129 // operation, or re-queue ourselves if we were signalled during the 130 // dispatch operation. 131 if (is_active()) { 132 if (signaled_) { 133 dispatch_state_ = DispatchState::WaitingOnPort; 134 res = domain->AddPendingWork(this); 135 } else { 136 res = zx_object_signal(handle_.get(), ZX_USER_SIGNAL_0, 0u); 137 if (res == ZX_OK) 138 res = WaitOnPortLocked(); 139 } 140 141 if (res != ZX_OK) { 142 dispatch_state_ = DispatchState::Idle; 143 InternalDeactivateLocked(); 144 } 145 } 146 147 // Have we become deactivated (either during dispatching or just now)? 148 // If so, move our process handler state outside of our lock so that it 149 // can safely destruct. 150 if (!is_active()) { 151 old_process_handler = fbl::move(process_handler_); 152 } 153 } 154} 155 156} // namespace dispatcher 157