| Index: mojo/public/cpp/system/wait.cc
|
| diff --git a/mojo/public/cpp/system/wait.cc b/mojo/public/cpp/system/wait.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e4e124f25c247338591738b5a69a830f25e494d4
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/system/wait.cc
|
| @@ -0,0 +1,200 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "mojo/public/cpp/system/wait.h"
|
| +
|
| +#include <memory>
|
| +#include <vector>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "mojo/public/c/system/watcher.h"
|
| +#include "mojo/public/cpp/system/watcher.h"
|
| +
|
| +namespace mojo {
|
| +namespace {
|
| +
|
| +class WatchContext : public base::RefCountedThreadSafe<WatchContext> {
|
| + public:
|
| + WatchContext()
|
| + : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED) {}
|
| +
|
| + base::WaitableEvent& event() { return event_; }
|
| + MojoResult wait_result() const { return wait_result_; }
|
| + MojoHandleSignalsState wait_state() const { return wait_state_; }
|
| + uintptr_t context_value() const { return reinterpret_cast<uintptr_t>(this); }
|
| +
|
| + static void OnNotification(uintptr_t context_value,
|
| + MojoResult result,
|
| + MojoHandleSignalsState state,
|
| + MojoWatcherNotificationFlags flags) {
|
| + auto* context = reinterpret_cast<WatchContext*>(context_value);
|
| + context->Notify(result, state);
|
| + if (result == MOJO_RESULT_CANCELLED) {
|
| + // Balanced in Wait() or WaitMany().
|
| + context->Release();
|
| + }
|
| + }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<WatchContext>;
|
| +
|
| + ~WatchContext() {}
|
| +
|
| + void Notify(MojoResult result, MojoHandleSignalsState state) {
|
| + if (wait_result_ == MOJO_RESULT_UNKNOWN) {
|
| + wait_result_ = result;
|
| + wait_state_ = state;
|
| + }
|
| + event_.Signal();
|
| + }
|
| +
|
| + base::WaitableEvent event_;
|
| +
|
| + // NOTE: Although these are modified in Notify() which may be called from any
|
| + // thread, Notify() is guaranteed to never run concurrently with itself.
|
| + // Furthermore, they are only modified once, before |event_| signals; so there
|
| + // is no need for a WatchContext user to synchronize access to these fields
|
| + // apart from waiting on |event()|.
|
| + MojoResult wait_result_ = MOJO_RESULT_UNKNOWN;
|
| + MojoHandleSignalsState wait_state_ = {0, 0};
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(WatchContext);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +MojoResult Wait(Handle handle,
|
| + MojoHandleSignals signals,
|
| + MojoHandleSignalsState* signals_state) {
|
| + ScopedWatcherHandle watcher;
|
| + MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher);
|
| + DCHECK_EQ(MOJO_RESULT_OK, rv);
|
| +
|
| + scoped_refptr<WatchContext> context = new WatchContext;
|
| +
|
| + // Balanced in WatchContext::OnNotification if MojoWatch() is successful.
|
| + // Otherwise balanced immediately below.
|
| + context->AddRef();
|
| +
|
| + rv = MojoWatch(watcher.get().value(), handle.value(), signals,
|
| + context->context_value());
|
| + if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
|
| + // Balanced above.
|
| + context->Release();
|
| + return rv;
|
| + }
|
| + DCHECK_EQ(MOJO_RESULT_OK, rv);
|
| +
|
| + uint32_t num_ready_contexts = 1;
|
| + uintptr_t ready_context;
|
| + MojoResult ready_result;
|
| + MojoHandleSignalsState ready_state;
|
| + rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts,
|
| + &ready_context, &ready_result, &ready_state);
|
| + if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
|
| + DCHECK_EQ(1u, num_ready_contexts);
|
| + if (signals_state)
|
| + *signals_state = ready_state;
|
| + return ready_result;
|
| + }
|
| +
|
| + // Wait for the first notification only.
|
| + context->event().Wait();
|
| +
|
| + ready_result = context->wait_result();
|
| + DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
|
| +
|
| + if (signals_state)
|
| + *signals_state = context->wait_state();
|
| +
|
| + return ready_result;
|
| +}
|
| +
|
| +MojoResult WaitMany(const Handle* handles,
|
| + const MojoHandleSignals* signals,
|
| + size_t num_handles,
|
| + size_t* result_index,
|
| + MojoHandleSignalsState* signals_states) {
|
| + if (!handles || !signals)
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| +
|
| + ScopedWatcherHandle watcher;
|
| + MojoResult rv = CreateWatcher(&WatchContext::OnNotification, &watcher);
|
| + DCHECK_EQ(MOJO_RESULT_OK, rv);
|
| +
|
| + std::vector<scoped_refptr<WatchContext>> contexts(num_handles);
|
| + std::vector<base::WaitableEvent*> events(num_handles);
|
| + for (size_t i = 0; i < num_handles; ++i) {
|
| + contexts[i] = new WatchContext();
|
| +
|
| + // Balanced in WatchContext::OnNotification if MojoWatch() is successful.
|
| + // Otherwise balanced immediately below.
|
| + contexts[i]->AddRef();
|
| +
|
| + MojoResult rv = MojoWatch(watcher.get().value(), handles[i].value(),
|
| + signals[i], contexts[i]->context_value());
|
| + if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
|
| + if (result_index)
|
| + *result_index = i;
|
| +
|
| + // Balanced above.
|
| + contexts[i]->Release();
|
| +
|
| + return MOJO_RESULT_INVALID_ARGUMENT;
|
| + }
|
| +
|
| + events[i] = &contexts[i]->event();
|
| + }
|
| +
|
| + uint32_t num_ready_contexts = 1;
|
| + uintptr_t ready_context = 0;
|
| + MojoResult ready_result = MOJO_RESULT_UNKNOWN;
|
| + MojoHandleSignalsState ready_state{0, 0};
|
| + rv = MojoArmWatcher(watcher.get().value(), &num_ready_contexts,
|
| + &ready_context, &ready_result, &ready_state);
|
| +
|
| + size_t index = num_handles;
|
| + if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
|
| + DCHECK_EQ(1u, num_ready_contexts);
|
| +
|
| + // Most commonly we only watch a small number of handles. Just scan for
|
| + // the right index.
|
| + for (size_t i = 0; i < num_handles; ++i) {
|
| + if (contexts[i]->context_value() == ready_context) {
|
| + index = i;
|
| + break;
|
| + }
|
| + }
|
| + } else {
|
| + DCHECK_EQ(MOJO_RESULT_OK, rv);
|
| +
|
| + // Wait for one of the contexts to signal. First one wins.
|
| + index = base::WaitableEvent::WaitMany(events.data(), events.size());
|
| + ready_result = contexts[index]->wait_result();
|
| + ready_state = contexts[index]->wait_state();
|
| + }
|
| +
|
| + DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
|
| + DCHECK_LT(index, num_handles);
|
| +
|
| + if (result_index)
|
| + *result_index = index;
|
| +
|
| + if (signals_states) {
|
| + for (size_t i = 0; i < num_handles; ++i) {
|
| + if (i == index) {
|
| + signals_states[i] = ready_state;
|
| + } else {
|
| + signals_states[i] = handles[i].QuerySignalsState();
|
| + }
|
| + }
|
| + }
|
| +
|
| + return ready_result;
|
| +}
|
| +
|
| +} // namespace mojo
|
|
|