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: 21 TriggerContext() 22 : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, 23 base::WaitableEvent::InitialState::NOT_SIGNALED) {} 24 25 base::WaitableEvent& event() { return event_; } 26 MojoResult wait_result() const { return wait_result_; } 27 MojoHandleSignalsState wait_state() const { return wait_state_; } 28 uintptr_t context_value() const { return reinterpret_cast<uintptr_t>(this); } 29 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 42 ~TriggerContext() {} 43 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 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 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 MojoResult rv = 135 MojoAddTrigger(trap.get().value(), handles[i].value(), signals[i], 136 MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 137 contexts[i]->context_value(), nullptr); 138 if (rv == MOJO_RESULT_INVALID_ARGUMENT) { 139 if (result_index) 140 *result_index = i; 141 142 // Balanced above. 143 contexts[i]->Release(); 144 145 return MOJO_RESULT_INVALID_ARGUMENT; 146 } 147 148 events[i] = &contexts[i]->event(); 149 } 150 151 uint32_t num_blocking_events = 1; 152 MojoTrapEvent blocking_event = {sizeof(blocking_event)}; 153 rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events, 154 &blocking_event); 155 156 size_t index = num_handles; 157 MojoResult ready_result = MOJO_RESULT_UNKNOWN; 158 MojoHandleSignalsState ready_state = {}; 159 if (rv == MOJO_RESULT_FAILED_PRECONDITION) { 160 DCHECK_EQ(1u, num_blocking_events); 161 162 // Most commonly we only watch a small number of handles. Just scan for 163 // the right index. 164 for (size_t i = 0; i < num_handles; ++i) { 165 if (contexts[i]->context_value() == blocking_event.trigger_context) { 166 index = i; 167 ready_result = blocking_event.result; 168 ready_state = blocking_event.signals_state; 169 break; 170 } 171 } 172 } else { 173 DCHECK_EQ(MOJO_RESULT_OK, rv); 174 175 // Wait for one of the contexts to signal. First one wins. 176 index = base::WaitableEvent::WaitMany(events.data(), events.size()); 177 ready_result = contexts[index]->wait_result(); 178 ready_state = contexts[index]->wait_state(); 179 } 180 181 DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result); 182 DCHECK_LT(index, num_handles); 183 184 if (result_index) 185 *result_index = index; 186 187 if (signals_states) { 188 for (size_t i = 0; i < num_handles; ++i) { 189 if (i == index) { 190 signals_states[i] = ready_state; 191 } else { 192 signals_states[i] = handles[i].QuerySignalsState(); 193 } 194 } 195 } 196 197 return ready_result; 198 } 199 200 } // namespace mojo 201