201 lines
6.2 KiB
C++
201 lines
6.2 KiB
C++
// 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/trap.h"
|
|
#include "mojo/public/cpp/system/trap.h"
|
|
|
|
namespace mojo {
|
|
namespace {
|
|
|
|
class TriggerContext : public base::RefCountedThreadSafe<TriggerContext> {
|
|
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<uintptr_t>(this); }
|
|
|
|
static void OnNotification(const MojoTrapEvent* event) {
|
|
auto* context = reinterpret_cast<TriggerContext*>(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>;
|
|
|
|
~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<TriggerContext> 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<scoped_refptr<TriggerContext>> contexts(num_handles);
|
|
std::vector<base::WaitableEvent*> 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
|