// 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 #include #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/synchronization/waitable_event.h" #include "mojo/public/c/system/trap.h" #include "mojo/public/cpp/system/trap.h" namespace mojo { namespace { class TriggerContext : public base::RefCountedThreadSafe { public: TriggerContext() : 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(this); } static void OnNotification(const MojoTrapEvent* event) { auto* context = reinterpret_cast(event->trigger_context); context->Notify(event->result, event->signals_state); if (event->result == MOJO_RESULT_CANCELLED) { // Balanced in Wait() or WaitMany(). context->Release(); } } private: friend class base::RefCountedThreadSafe; ~TriggerContext() {} 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 // sequence, 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 TriggerContext 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(TriggerContext); }; } // namespace MojoResult Wait(Handle handle, MojoHandleSignals signals, MojoTriggerCondition condition, MojoHandleSignalsState* signals_state) { ScopedTrapHandle trap; MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap); DCHECK_EQ(MOJO_RESULT_OK, rv); scoped_refptr context = new TriggerContext; // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is // successful. Otherwise balanced immediately below. context->AddRef(); rv = MojoAddTrigger(trap.get().value(), handle.value(), signals, condition, context->context_value(), nullptr); if (rv == MOJO_RESULT_INVALID_ARGUMENT) { // Balanced above. context->Release(); return rv; } DCHECK_EQ(MOJO_RESULT_OK, rv); uint32_t num_blocking_events = 1; MojoTrapEvent blocking_event = {sizeof(blocking_event)}; rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events, &blocking_event); if (rv == MOJO_RESULT_FAILED_PRECONDITION) { DCHECK_EQ(1u, num_blocking_events); if (signals_state) *signals_state = blocking_event.signals_state; return blocking_event.result; } // Wait for the first notification only. context->event().Wait(); MojoResult 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; ScopedTrapHandle trap; MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap); DCHECK_EQ(MOJO_RESULT_OK, rv); std::vector> contexts(num_handles); std::vector events(num_handles); for (size_t i = 0; i < num_handles; ++i) { contexts[i] = new TriggerContext(); // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is // successful. Otherwise balanced immediately below. contexts[i]->AddRef(); MojoResult rv = MojoAddTrigger(trap.get().value(), handles[i].value(), signals[i], MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, contexts[i]->context_value(), nullptr); 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_blocking_events = 1; MojoTrapEvent blocking_event = {sizeof(blocking_event)}; rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events, &blocking_event); size_t index = num_handles; MojoResult ready_result = MOJO_RESULT_UNKNOWN; MojoHandleSignalsState ready_state = {}; if (rv == MOJO_RESULT_FAILED_PRECONDITION) { DCHECK_EQ(1u, num_blocking_events); // 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() == blocking_event.trigger_context) { index = i; ready_result = blocking_event.result; ready_state = blocking_event.signals_state; 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