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