diff options
Diffstat (limited to 'mojo/public/cpp/bindings/lib/sync_handle_registry.cc')
-rw-r--r-- | mojo/public/cpp/bindings/lib/sync_handle_registry.cc | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc new file mode 100644 index 0000000000..fd3df396ec --- /dev/null +++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc @@ -0,0 +1,135 @@ +// Copyright 2016 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/bindings/sync_handle_registry.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/threading/thread_local.h" +#include "mojo/public/c/system/core.h" + +namespace mojo { +namespace { + +base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>::Leaky + g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() { + scoped_refptr<SyncHandleRegistry> result( + g_current_sync_handle_watcher.Pointer()->Get()); + if (!result) { + result = new SyncHandleRegistry(); + DCHECK_EQ(result.get(), g_current_sync_handle_watcher.Pointer()->Get()); + } + return result; +} + +bool SyncHandleRegistry::RegisterHandle(const Handle& handle, + MojoHandleSignals handle_signals, + const HandleCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (base::ContainsKey(handles_, handle)) + return false; + + MojoResult result = wait_set_.AddHandle(handle, handle_signals); + if (result != MOJO_RESULT_OK) + return false; + + handles_[handle] = callback; + return true; +} + +void SyncHandleRegistry::UnregisterHandle(const Handle& handle) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!base::ContainsKey(handles_, handle)) + return; + + MojoResult result = wait_set_.RemoveHandle(handle); + DCHECK_EQ(MOJO_RESULT_OK, result); + handles_.erase(handle); +} + +bool SyncHandleRegistry::RegisterEvent(base::WaitableEvent* event, + const base::Closure& callback) { + auto result = events_.insert({event, callback}); + DCHECK(result.second); + MojoResult rv = wait_set_.AddEvent(event); + if (rv == MOJO_RESULT_OK) + return true; + DCHECK_EQ(MOJO_RESULT_ALREADY_EXISTS, rv); + return false; +} + +void SyncHandleRegistry::UnregisterEvent(base::WaitableEvent* event) { + auto it = events_.find(event); + DCHECK(it != events_.end()); + events_.erase(it); + MojoResult rv = wait_set_.RemoveEvent(event); + DCHECK_EQ(MOJO_RESULT_OK, rv); +} + +bool SyncHandleRegistry::Wait(const bool* should_stop[], size_t count) { + DCHECK(thread_checker_.CalledOnValidThread()); + + size_t num_ready_handles; + Handle ready_handle; + MojoResult ready_handle_result; + + scoped_refptr<SyncHandleRegistry> preserver(this); + while (true) { + for (size_t i = 0; i < count; ++i) + if (*should_stop[i]) + return true; + + // TODO(yzshen): Theoretically it can reduce sync call re-entrancy if we + // give priority to the handle that is waiting for sync response. + base::WaitableEvent* ready_event = nullptr; + num_ready_handles = 1; + wait_set_.Wait(&ready_event, &num_ready_handles, &ready_handle, + &ready_handle_result); + if (num_ready_handles) { + DCHECK_EQ(1u, num_ready_handles); + const auto iter = handles_.find(ready_handle); + iter->second.Run(ready_handle_result); + } + + if (ready_event) { + const auto iter = events_.find(ready_event); + DCHECK(iter != events_.end()); + iter->second.Run(); + } + }; + + return false; +} + +SyncHandleRegistry::SyncHandleRegistry() { + DCHECK(!g_current_sync_handle_watcher.Pointer()->Get()); + g_current_sync_handle_watcher.Pointer()->Set(this); +} + +SyncHandleRegistry::~SyncHandleRegistry() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // This object may be destructed after the thread local storage slot used by + // |g_current_sync_handle_watcher| is reset during thread shutdown. + // For example, another slot in the thread local storage holds a referrence to + // this object, and that slot is cleaned up after + // |g_current_sync_handle_watcher|. + if (!g_current_sync_handle_watcher.Pointer()->Get()) + return; + + // If this breaks, it is likely that the global variable is bulit into and + // accessed from multiple modules. + DCHECK_EQ(this, g_current_sync_handle_watcher.Pointer()->Get()); + + g_current_sync_handle_watcher.Pointer()->Set(nullptr); +} + +} // namespace mojo |