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