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