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