1 // Copyright 2017 The Chromium 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 "mojo/public/cpp/system/wait.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "base/memory/ptr_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "mojo/public/c/system/trap.h"
14 #include "mojo/public/cpp/system/trap.h"
15
16 namespace mojo {
17 namespace {
18
19 class TriggerContext : public base::RefCountedThreadSafe<TriggerContext> {
20 public:
TriggerContext()21 TriggerContext()
22 : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
23 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
24
event()25 base::WaitableEvent& event() { return event_; }
wait_result() const26 MojoResult wait_result() const { return wait_result_; }
wait_state() const27 MojoHandleSignalsState wait_state() const { return wait_state_; }
context_value() const28 uintptr_t context_value() const { return reinterpret_cast<uintptr_t>(this); }
29
OnNotification(const MojoTrapEvent * event)30 static void OnNotification(const MojoTrapEvent* event) {
31 auto* context = reinterpret_cast<TriggerContext*>(event->trigger_context);
32 context->Notify(event->result, event->signals_state);
33 if (event->result == MOJO_RESULT_CANCELLED) {
34 // Balanced in Wait() or WaitMany().
35 context->Release();
36 }
37 }
38
39 private:
40 friend class base::RefCountedThreadSafe<TriggerContext>;
41
~TriggerContext()42 ~TriggerContext() {}
43
Notify(MojoResult result,MojoHandleSignalsState state)44 void Notify(MojoResult result, MojoHandleSignalsState state) {
45 if (wait_result_ == MOJO_RESULT_UNKNOWN) {
46 wait_result_ = result;
47 wait_state_ = state;
48 }
49 event_.Signal();
50 }
51
52 base::WaitableEvent event_;
53
54 // NOTE: Although these are modified in Notify() which may be called from any
55 // sequence, Notify() is guaranteed to never run concurrently with itself.
56 // Furthermore, they are only modified once, before |event_| signals; so there
57 // is no need for a TriggerContext user to synchronize access to these fields
58 // apart from waiting on |event()|.
59 MojoResult wait_result_ = MOJO_RESULT_UNKNOWN;
60 MojoHandleSignalsState wait_state_ = {0, 0};
61
62 DISALLOW_COPY_AND_ASSIGN(TriggerContext);
63 };
64
65 } // namespace
66
Wait(Handle handle,MojoHandleSignals signals,MojoTriggerCondition condition,MojoHandleSignalsState * signals_state)67 MojoResult Wait(Handle handle,
68 MojoHandleSignals signals,
69 MojoTriggerCondition condition,
70 MojoHandleSignalsState* signals_state) {
71 ScopedTrapHandle trap;
72 MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap);
73 DCHECK_EQ(MOJO_RESULT_OK, rv);
74
75 scoped_refptr<TriggerContext> context = new TriggerContext;
76
77 // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is
78 // successful. Otherwise balanced immediately below.
79 context->AddRef();
80
81 rv = MojoAddTrigger(trap.get().value(), handle.value(), signals, condition,
82 context->context_value(), nullptr);
83 if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
84 // Balanced above.
85 context->Release();
86 return rv;
87 }
88 DCHECK_EQ(MOJO_RESULT_OK, rv);
89
90 uint32_t num_blocking_events = 1;
91 MojoTrapEvent blocking_event = {sizeof(blocking_event)};
92 rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events,
93 &blocking_event);
94 if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
95 DCHECK_EQ(1u, num_blocking_events);
96 if (signals_state)
97 *signals_state = blocking_event.signals_state;
98 return blocking_event.result;
99 }
100
101 // Wait for the first notification only.
102 context->event().Wait();
103
104 MojoResult ready_result = context->wait_result();
105 DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
106
107 if (signals_state)
108 *signals_state = context->wait_state();
109
110 return ready_result;
111 }
112
WaitMany(const Handle * handles,const MojoHandleSignals * signals,size_t num_handles,size_t * result_index,MojoHandleSignalsState * signals_states)113 MojoResult WaitMany(const Handle* handles,
114 const MojoHandleSignals* signals,
115 size_t num_handles,
116 size_t* result_index,
117 MojoHandleSignalsState* signals_states) {
118 if (!handles || !signals)
119 return MOJO_RESULT_INVALID_ARGUMENT;
120
121 ScopedTrapHandle trap;
122 MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap);
123 DCHECK_EQ(MOJO_RESULT_OK, rv);
124
125 std::vector<scoped_refptr<TriggerContext>> contexts(num_handles);
126 std::vector<base::WaitableEvent*> events(num_handles);
127 for (size_t i = 0; i < num_handles; ++i) {
128 contexts[i] = new TriggerContext();
129
130 // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is
131 // successful. Otherwise balanced immediately below.
132 contexts[i]->AddRef();
133
134 rv = MojoAddTrigger(trap.get().value(), handles[i].value(), signals[i],
135 MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
136 contexts[i]->context_value(), nullptr);
137 if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
138 if (result_index)
139 *result_index = i;
140
141 // Balanced above.
142 contexts[i]->Release();
143
144 return MOJO_RESULT_INVALID_ARGUMENT;
145 }
146
147 events[i] = &contexts[i]->event();
148 }
149
150 uint32_t num_blocking_events = 1;
151 MojoTrapEvent blocking_event = {sizeof(blocking_event)};
152 rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events,
153 &blocking_event);
154
155 size_t index = num_handles;
156 MojoResult ready_result = MOJO_RESULT_UNKNOWN;
157 MojoHandleSignalsState ready_state = {};
158 if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
159 DCHECK_EQ(1u, num_blocking_events);
160
161 // Most commonly we only watch a small number of handles. Just scan for
162 // the right index.
163 for (size_t i = 0; i < num_handles; ++i) {
164 if (contexts[i]->context_value() == blocking_event.trigger_context) {
165 index = i;
166 ready_result = blocking_event.result;
167 ready_state = blocking_event.signals_state;
168 break;
169 }
170 }
171 } else {
172 DCHECK_EQ(MOJO_RESULT_OK, rv);
173
174 // Wait for one of the contexts to signal. First one wins.
175 index = base::WaitableEvent::WaitMany(events.data(), events.size());
176 ready_result = contexts[index]->wait_result();
177 ready_state = contexts[index]->wait_state();
178 }
179
180 DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
181 DCHECK_LT(index, num_handles);
182
183 if (result_index)
184 *result_index = index;
185
186 if (signals_states) {
187 for (size_t i = 0; i < num_handles; ++i) {
188 if (i == index) {
189 signals_states[i] = ready_state;
190 } else {
191 signals_states[i] = handles[i].QuerySignalsState();
192 }
193 }
194 }
195
196 return ready_result;
197 }
198
199 } // namespace mojo
200