OLD | NEW |
(Empty) | |
| 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/watcher.h" |
| 14 #include "mojo/public/cpp/system/watcher.h" |
| 15 |
| 16 namespace mojo { |
| 17 namespace { |
| 18 |
| 19 class WatchContext : public base::RefCountedThreadSafe<WatchContext> { |
| 20 public: |
| 21 WatchContext() |
| 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(uintptr_t context_value, |
| 31 MojoResult result, |
| 32 MojoHandleSignalsState state, |
| 33 MojoWatcherNotificationFlags flags) { |
| 34 auto* context = reinterpret_cast<WatchContext*>(context_value); |
| 35 context->Notify(result, state); |
| 36 if (result == MOJO_RESULT_CANCELLED) { |
| 37 // Balanced in Wait() or WaitMany(). |
| 38 context->Release(); |
| 39 } |
| 40 } |
| 41 |
| 42 private: |
| 43 friend class base::RefCountedThreadSafe<WatchContext>; |
| 44 |
| 45 ~WatchContext() {} |
| 46 |
| 47 void Notify(MojoResult result, MojoHandleSignalsState state) { |
| 48 if (wait_result_ == MOJO_RESULT_UNKNOWN) { |
| 49 wait_result_ = result; |
| 50 wait_state_ = state; |
| 51 } |
| 52 event_.Signal(); |
| 53 } |
| 54 |
| 55 base::WaitableEvent event_; |
| 56 |
| 57 // NOTE: Although these are modified in Notify() which may be called from any |
| 58 // thread, Notify() is guaranteed to never run concurrently with itself. |
| 59 // Furthermore, they are only modified once, before |event_| signals; so there |
| 60 // is no need for a WatchContext user to synchronize access to these fields |
| 61 // apart from waiting on |event()|. |
| 62 MojoResult wait_result_ = MOJO_RESULT_UNKNOWN; |
| 63 MojoHandleSignalsState wait_state_ = {0, 0}; |
| 64 |
| 65 DISALLOW_COPY_AND_ASSIGN(WatchContext); |
| 66 }; |
| 67 |
| 68 } // namespace |
| 69 |
| 70 MojoResult Wait(Handle handle, |
| 71 MojoHandleSignals signals, |
| 72 MojoHandleSignalsState* signals_state) { |
| 73 ScopedWatcherHandle watcher; |
| 74 MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher); |
| 75 DCHECK_EQ(MOJO_RESULT_OK, rv); |
| 76 |
| 77 scoped_refptr<WatchContext> context = new WatchContext; |
| 78 |
| 79 // Balanced in WatchContext::OnNotification if MojoWatch() is successful. |
| 80 // Otherwise balanced immediately below. |
| 81 context->AddRef(); |
| 82 |
| 83 rv = MojoWatch(watcher.get().value(), handle.value(), signals, |
| 84 context->context_value()); |
| 85 if (rv == MOJO_RESULT_INVALID_ARGUMENT) { |
| 86 // Balanced above. |
| 87 context->Release(); |
| 88 return rv; |
| 89 } |
| 90 DCHECK_EQ(MOJO_RESULT_OK, rv); |
| 91 |
| 92 uint32_t num_ready_contexts = 1; |
| 93 uintptr_t ready_context; |
| 94 MojoResult ready_result; |
| 95 MojoHandleSignalsState ready_state; |
| 96 rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts, |
| 97 &ready_context, &ready_result, &ready_state); |
| 98 if (rv == MOJO_RESULT_FAILED_PRECONDITION) { |
| 99 DCHECK_EQ(1u, num_ready_contexts); |
| 100 if (signals_state) |
| 101 *signals_state = ready_state; |
| 102 return ready_result; |
| 103 } |
| 104 |
| 105 // Wait for the first notification only. |
| 106 context->event().Wait(); |
| 107 |
| 108 ready_result = context->wait_result(); |
| 109 DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result); |
| 110 |
| 111 if (signals_state) |
| 112 *signals_state = context->wait_state(); |
| 113 |
| 114 return ready_result; |
| 115 } |
| 116 |
| 117 MojoResult WaitMany(const Handle* handles, |
| 118 const MojoHandleSignals* signals, |
| 119 size_t num_handles, |
| 120 size_t* result_index, |
| 121 MojoHandleSignalsState* signals_states) { |
| 122 if (!handles || !signals) |
| 123 return MOJO_RESULT_INVALID_ARGUMENT; |
| 124 |
| 125 ScopedWatcherHandle watcher; |
| 126 MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher); |
| 127 DCHECK_EQ(MOJO_RESULT_OK, rv); |
| 128 |
| 129 std::vector<scoped_refptr<WatchContext>> contexts(num_handles); |
| 130 std::vector<base::WaitableEvent*> events(num_handles); |
| 131 for (size_t i = 0; i < num_handles; ++i) { |
| 132 contexts[i] = new WatchContext(); |
| 133 |
| 134 // Balanced in WatchContext::OnNotification if MojoWatch() is successful. |
| 135 // Otherwise balanced immediately below. |
| 136 contexts[i]->AddRef(); |
| 137 |
| 138 MojoResult rv = MojoWatch(watcher.get().value(), handles[i].value(), |
| 139 signals[i], contexts[i]->context_value()); |
| 140 if (rv == MOJO_RESULT_INVALID_ARGUMENT) { |
| 141 if (result_index) |
| 142 *result_index = i; |
| 143 |
| 144 // Balanced above. |
| 145 contexts[i]->Release(); |
| 146 |
| 147 return MOJO_RESULT_INVALID_ARGUMENT; |
| 148 } |
| 149 |
| 150 events[i] = &contexts[i]->event(); |
| 151 } |
| 152 |
| 153 uint32_t num_ready_contexts = 1; |
| 154 uintptr_t ready_context = 0; |
| 155 MojoResult ready_result = MOJO_RESULT_UNKNOWN; |
| 156 MojoHandleSignalsState ready_state{0, 0}; |
| 157 rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts, |
| 158 &ready_context, &ready_result, &ready_state); |
| 159 |
| 160 size_t index = num_handles; |
| 161 if (rv == MOJO_RESULT_FAILED_PRECONDITION) { |
| 162 DCHECK_EQ(1u, num_ready_contexts); |
| 163 |
| 164 // Most commonly we only watch a small number of handles. Just scan for |
| 165 // the right index. |
| 166 for (size_t i = 0; i < num_handles; ++i) { |
| 167 if (contexts[i]->context_value() == ready_context) { |
| 168 index = i; |
| 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 |
OLD | NEW |