aboutsummaryrefslogtreecommitdiff
path: root/mojo/public/cpp/system/wait_set.cc
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/cpp/system/wait_set.cc')
-rw-r--r--mojo/public/cpp/system/wait_set.cc371
1 files changed, 0 insertions, 371 deletions
diff --git a/mojo/public/cpp/system/wait_set.cc b/mojo/public/cpp/system/wait_set.cc
deleted file mode 100644
index 1728f81..0000000
--- a/mojo/public/cpp/system/wait_set.cc
+++ /dev/null
@@ -1,371 +0,0 @@
-// 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_set.h"
-
-#include <algorithm>
-#include <limits>
-#include <map>
-#include <set>
-#include <vector>
-
-#include "base/containers/stack_container.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "mojo/public/cpp/system/watcher.h"
-
-namespace mojo {
-
-class WaitSet::State : public base::RefCountedThreadSafe<State> {
- public:
- State()
- : handle_event_(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED) {
- MojoResult rv = CreateWatcher(&Context::OnNotification, &watcher_handle_);
- DCHECK_EQ(MOJO_RESULT_OK, rv);
- }
-
- void ShutDown() {
- // NOTE: This may immediately invoke Notify for every context.
- watcher_handle_.reset();
- }
-
- MojoResult AddEvent(base::WaitableEvent* event) {
- auto result = user_events_.insert(event);
- if (result.second)
- return MOJO_RESULT_OK;
- return MOJO_RESULT_ALREADY_EXISTS;
- }
-
- MojoResult RemoveEvent(base::WaitableEvent* event) {
- auto it = user_events_.find(event);
- if (it == user_events_.end())
- return MOJO_RESULT_NOT_FOUND;
- user_events_.erase(it);
- return MOJO_RESULT_OK;
- }
-
- MojoResult AddHandle(Handle handle, MojoHandleSignals signals) {
- DCHECK(watcher_handle_.is_valid());
-
- scoped_refptr<Context> context = new Context(this, handle);
-
- {
- base::AutoLock lock(lock_);
-
- if (handle_to_context_.count(handle))
- return MOJO_RESULT_ALREADY_EXISTS;
- DCHECK(!contexts_.count(context->context_value()));
-
- handle_to_context_[handle] = context;
- contexts_[context->context_value()] = context;
- }
-
- // Balanced in State::Notify() with MOJO_RESULT_CANCELLED if
- // MojoWatch() succeeds. Otherwise balanced immediately below.
- context->AddRef();
-
- // This can notify immediately if the watcher is already armed. Don't hold
- // |lock_| while calling it.
- MojoResult rv = MojoWatch(watcher_handle_.get().value(), handle.value(),
- signals, context->context_value());
- if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
- base::AutoLock lock(lock_);
- handle_to_context_.erase(handle);
- contexts_.erase(context->context_value());
-
- // Balanced above.
- context->Release();
- return rv;
- }
- DCHECK_EQ(MOJO_RESULT_OK, rv);
-
- return rv;
- }
-
- MojoResult RemoveHandle(Handle handle) {
- DCHECK(watcher_handle_.is_valid());
-
- scoped_refptr<Context> context;
- {
- base::AutoLock lock(lock_);
- auto it = handle_to_context_.find(handle);
- if (it == handle_to_context_.end())
- return MOJO_RESULT_NOT_FOUND;
-
- context = std::move(it->second);
- handle_to_context_.erase(it);
-
- // Ensure that we never return this handle as a ready result again. Note
- // that it's removal from |handle_to_context_| above ensures it will never
- // be added back to this map.
- ready_handles_.erase(handle);
- }
-
- // NOTE: This may enter the notification callback immediately, so don't hold
- // |lock_| while calling it.
- MojoResult rv = MojoCancelWatch(watcher_handle_.get().value(),
- context->context_value());
-
- // We don't really care whether or not this succeeds. In either case, the
- // context was or will imminently be cancelled and moved from |contexts_|
- // to |cancelled_contexts_|.
- DCHECK(rv == MOJO_RESULT_OK || rv == MOJO_RESULT_NOT_FOUND);
-
- {
- // Always clear |cancelled_contexts_| in case it's accumulated any more
- // entries since the last time we ran.
- base::AutoLock lock(lock_);
- cancelled_contexts_.clear();
- }
-
- return rv;
- }
-
- void Wait(base::WaitableEvent** ready_event,
- size_t* num_ready_handles,
- Handle* ready_handles,
- MojoResult* ready_results,
- MojoHandleSignalsState* signals_states) {
- DCHECK(watcher_handle_.is_valid());
- DCHECK(num_ready_handles);
- DCHECK(ready_handles);
- DCHECK(ready_results);
- {
- base::AutoLock lock(lock_);
- if (ready_handles_.empty()) {
- // No handles are currently in the ready set. Make sure the event is
- // reset and try to arm the watcher.
- handle_event_.Reset();
-
- DCHECK_LE(*num_ready_handles, std::numeric_limits<uint32_t>::max());
- uint32_t num_ready_contexts = static_cast<uint32_t>(*num_ready_handles);
-
- base::StackVector<uintptr_t, 4> ready_contexts;
- ready_contexts.container().resize(num_ready_contexts);
- base::StackVector<MojoHandleSignalsState, 4> ready_states;
- MojoHandleSignalsState* out_states = signals_states;
- if (!out_states) {
- // If the caller didn't provide a buffer for signal states, we provide
- // our own locally. MojoArmWatcher() requires one if we want to handle
- // arming failure properly.
- ready_states.container().resize(num_ready_contexts);
- out_states = ready_states.container().data();
- }
- MojoResult rv = MojoArmWatcher(
- watcher_handle_.get().value(), &num_ready_contexts,
- ready_contexts.container().data(), ready_results, out_states);
-
- if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
- // Simulate the handles becoming ready. We do this in lieu of
- // returning the results immediately so as to avoid potentially
- // starving user events. i.e., we always want to call WaitMany()
- // below.
- handle_event_.Signal();
- for (size_t i = 0; i < num_ready_contexts; ++i) {
- auto it = contexts_.find(ready_contexts.container()[i]);
- DCHECK(it != contexts_.end());
- ready_handles_[it->second->handle()] = {ready_results[i],
- out_states[i]};
- }
- } else if (rv == MOJO_RESULT_NOT_FOUND) {
- // Nothing to watch. If there are no user events, always signal to
- // avoid deadlock.
- if (user_events_.empty())
- handle_event_.Signal();
- } else {
- // Watcher must be armed now. No need to manually signal.
- DCHECK_EQ(MOJO_RESULT_OK, rv);
- }
- }
- }
-
- // Build a local contiguous array of events to wait on. These are rotated
- // across Wait() calls to avoid starvation, by virtue of the fact that
- // WaitMany guarantees left-to-right priority when multiple events are
- // signaled.
-
- base::StackVector<base::WaitableEvent*, 4> events;
- events.container().resize(user_events_.size() + 1);
- if (waitable_index_shift_ > user_events_.size())
- waitable_index_shift_ = 0;
-
- size_t dest_index = waitable_index_shift_++;
- events.container()[dest_index] = &handle_event_;
- for (auto* e : user_events_) {
- dest_index = (dest_index + 1) % events.container().size();
- events.container()[dest_index] = e;
- }
-
- size_t index = base::WaitableEvent::WaitMany(events.container().data(),
- events.container().size());
- base::AutoLock lock(lock_);
-
- // Pop as many handles as we can out of the ready set and return them. Note
- // that we do this regardless of which event signaled, as there may be
- // ready handles in any case and they may be interesting to the caller.
- *num_ready_handles = std::min(*num_ready_handles, ready_handles_.size());
- for (size_t i = 0; i < *num_ready_handles; ++i) {
- auto it = ready_handles_.begin();
- ready_handles[i] = it->first;
- ready_results[i] = it->second.result;
- if (signals_states)
- signals_states[i] = it->second.signals_state;
- ready_handles_.erase(it);
- }
-
- // If the caller cares, let them know which user event unblocked us, if any.
- if (ready_event) {
- if (events.container()[index] == &handle_event_)
- *ready_event = nullptr;
- else
- *ready_event = events.container()[index];
- }
- }
-
- private:
- friend class base::RefCountedThreadSafe<State>;
-
- class Context : public base::RefCountedThreadSafe<Context> {
- public:
- Context(scoped_refptr<State> state, Handle handle)
- : state_(state), handle_(handle) {}
-
- Handle handle() const { return handle_; }
-
- uintptr_t context_value() const {
- return reinterpret_cast<uintptr_t>(this);
- }
-
- static void OnNotification(uintptr_t context,
- MojoResult result,
- MojoHandleSignalsState signals_state,
- MojoWatcherNotificationFlags flags) {
- reinterpret_cast<Context*>(context)->Notify(result, signals_state);
- }
-
- private:
- friend class base::RefCountedThreadSafe<Context>;
-
- ~Context() {}
-
- void Notify(MojoResult result, MojoHandleSignalsState signals_state) {
- state_->Notify(handle_, result, signals_state, this);
- }
-
- const scoped_refptr<State> state_;
- const Handle handle_;
-
- DISALLOW_COPY_AND_ASSIGN(Context);
- };
-
- ~State() {}
-
- void Notify(Handle handle,
- MojoResult result,
- MojoHandleSignalsState signals_state,
- Context* context) {
- base::AutoLock lock(lock_);
-
- // This could be a cancellation notification following an explicit
- // RemoveHandle(), in which case we really don't care and don't want to
- // add it to the ready set. Only update and signal if that's not the case.
- if (!handle_to_context_.count(handle)) {
- DCHECK_EQ(MOJO_RESULT_CANCELLED, result);
- } else {
- ready_handles_[handle] = {result, signals_state};
- handle_event_.Signal();
- }
-
- // Whether it's an implicit or explicit cancellation, erase from |contexts_|
- // and append to |cancelled_contexts_|.
- if (result == MOJO_RESULT_CANCELLED) {
- contexts_.erase(context->context_value());
- handle_to_context_.erase(handle);
-
- // NOTE: We retain a context ref in |cancelled_contexts_| to ensure that
- // this Context's heap address is not reused too soon. For example, it
- // would otherwise be possible for the user to call AddHandle() from the
- // WaitSet's thread immediately after this notification has fired on
- // another thread, potentially reusing the same heap address for the newly
- // added Context; and then they may call RemoveHandle() for this handle
- // (not knowing its context has just been implicitly cancelled) and
- // cause the new Context to be incorrectly removed from |contexts_|.
- //
- // This vector is cleared on the WaitSet's own thread every time
- // RemoveHandle is called.
- cancelled_contexts_.emplace_back(make_scoped_refptr(context));
-
- // Balanced in State::AddHandle().
- context->Release();
- }
- }
-
- struct ReadyState {
- ReadyState() = default;
- ReadyState(MojoResult result, MojoHandleSignalsState signals_state)
- : result(result), signals_state(signals_state) {}
- ~ReadyState() = default;
-
- MojoResult result = MOJO_RESULT_UNKNOWN;
- MojoHandleSignalsState signals_state = {0, 0};
- };
-
- // Not guarded by lock. Must only be accessed from the WaitSet's owning
- // thread.
- ScopedWatcherHandle watcher_handle_;
-
- base::Lock lock_;
- std::map<uintptr_t, scoped_refptr<Context>> contexts_;
- std::map<Handle, scoped_refptr<Context>> handle_to_context_;
- std::map<Handle, ReadyState> ready_handles_;
- std::vector<scoped_refptr<Context>> cancelled_contexts_;
- std::set<base::WaitableEvent*> user_events_;
-
- // Event signaled any time a handle notification is received.
- base::WaitableEvent handle_event_;
-
- // Offset by which to rotate the current set of waitable objects. This is used
- // to guard against event starvation, as base::WaitableEvent::WaitMany gives
- // preference to events in left-to-right order.
- size_t waitable_index_shift_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(State);
-};
-
-WaitSet::WaitSet() : state_(new State) {}
-
-WaitSet::~WaitSet() {
- state_->ShutDown();
-}
-
-MojoResult WaitSet::AddEvent(base::WaitableEvent* event) {
- return state_->AddEvent(event);
-}
-
-MojoResult WaitSet::RemoveEvent(base::WaitableEvent* event) {
- return state_->RemoveEvent(event);
-}
-
-MojoResult WaitSet::AddHandle(Handle handle, MojoHandleSignals signals) {
- return state_->AddHandle(handle, signals);
-}
-
-MojoResult WaitSet::RemoveHandle(Handle handle) {
- return state_->RemoveHandle(handle);
-}
-
-void WaitSet::Wait(base::WaitableEvent** ready_event,
- size_t* num_ready_handles,
- Handle* ready_handles,
- MojoResult* ready_results,
- MojoHandleSignalsState* signals_states) {
- state_->Wait(ready_event, num_ready_handles, ready_handles, ready_results,
- signals_states);
-}
-
-} // namespace mojo