blob: fd3df396ece35544701e06a72c90b2b769a61202 [file] [log] [blame]
// 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