Mojo: Move the handle table details out of CoreImpl into its own class.

(This will make it much easier to improve the handle table, e.g., by caching
handle look-ups.)

R=darin@chromium.org

Review URL: https://codereview.chromium.org/216893005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260482 0039d316-1c4b-4281-b951-d872f2087c98
parent 58ba47e1
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "mojo/system/core_impl.h" #include "mojo/system/core_impl.h"
#include "mojo/system/handle_table.h"
namespace mojo { namespace mojo {
...@@ -15,12 +16,15 @@ namespace internal { ...@@ -15,12 +16,15 @@ namespace internal {
bool ShutdownCheckNoLeaks(CoreImpl* core_impl) { bool ShutdownCheckNoLeaks(CoreImpl* core_impl) {
// No point in taking the lock. // No point in taking the lock.
if (core_impl->handle_table_.empty()) const HandleTable::HandleToEntryMap& handle_to_entry_map =
core_impl->handle_table_.handle_to_entry_map_;
if (handle_to_entry_map.empty())
return true; return true;
for (CoreImpl::HandleTableMap::const_iterator it = for (HandleTable::HandleToEntryMap::const_iterator it =
core_impl->handle_table_.begin(); handle_to_entry_map.begin();
it != core_impl->handle_table_.end(); it != handle_to_entry_map.end();
++it) { ++it) {
LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << (*it).first; LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << (*it).first;
} }
......
...@@ -126,6 +126,8 @@ ...@@ -126,6 +126,8 @@
'system/data_pipe_producer_dispatcher.h', 'system/data_pipe_producer_dispatcher.h',
'system/dispatcher.cc', 'system/dispatcher.cc',
'system/dispatcher.h', 'system/dispatcher.h',
'system/handle_table.cc',
'system/handle_table.h',
'system/local_data_pipe.cc', 'system/local_data_pipe.cc',
'system/local_data_pipe.h', 'system/local_data_pipe.h',
'system/local_message_pipe_endpoint.cc', 'system/local_message_pipe_endpoint.cc',
......
This diff is collapsed.
...@@ -7,24 +7,17 @@ ...@@ -7,24 +7,17 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "mojo/public/system/core_private.h" #include "mojo/public/system/core_private.h"
#include "mojo/system/handle_table.h"
#include "mojo/system/system_impl_export.h" #include "mojo/system/system_impl_export.h"
namespace mojo { namespace mojo {
namespace system { namespace system {
class CoreImpl;
class Dispatcher; class Dispatcher;
// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
// so it can be friended.
namespace internal {
bool ShutdownCheckNoLeaks(CoreImpl*);
}
// |CoreImpl| is a singleton object that implements the Mojo system calls. All // |CoreImpl| is a singleton object that implements the Mojo system calls. All
// public methods are thread-safe. // public methods are thread-safe.
class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core { class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core {
...@@ -131,11 +124,6 @@ class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core { ...@@ -131,11 +124,6 @@ class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core {
// invalid. // invalid.
scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle); scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
// Assigns a new handle for the given dispatcher; returns
// |MOJO_HANDLE_INVALID| on failure (due to hitting resource limits) or if
// |dispatcher| is null. Must be called under |handle_table_lock_|.
MojoHandle AddDispatcherNoLock(const scoped_refptr<Dispatcher>& dispatcher);
// Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic
// validation of arguments. // validation of arguments.
MojoResult WaitManyInternal(const MojoHandle* handles, MojoResult WaitManyInternal(const MojoHandle* handles,
...@@ -147,9 +135,8 @@ class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core { ...@@ -147,9 +135,8 @@ class MOJO_SYSTEM_IMPL_EXPORT CoreImpl : public Core {
// TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we // TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we
// had them). // had them).
base::Lock handle_table_lock_; // Protects the immediately-following members. base::Lock handle_table_lock_; // Protects |handle_table_|.
HandleTableMap handle_table_; HandleTable handle_table_;
MojoHandle next_handle_; // Invariant: never |MOJO_HANDLE_INVALID|.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
......
...@@ -16,7 +16,7 @@ namespace test { ...@@ -16,7 +16,7 @@ namespace test {
// TODO(vtl): Maybe this should be defined in a test-only file instead. // TODO(vtl): Maybe this should be defined in a test-only file instead.
DispatcherTransport DispatcherTryStartTransport( DispatcherTransport DispatcherTryStartTransport(
Dispatcher* dispatcher) { Dispatcher* dispatcher) {
return Dispatcher::CoreImplAccess::TryStartTransport(dispatcher); return Dispatcher::HandleTableAccess::TryStartTransport(dispatcher);
} }
} // namespace test } // namespace test
...@@ -24,7 +24,7 @@ DispatcherTransport DispatcherTryStartTransport( ...@@ -24,7 +24,7 @@ DispatcherTransport DispatcherTryStartTransport(
// Dispatcher ------------------------------------------------------------------ // Dispatcher ------------------------------------------------------------------
// static // static
DispatcherTransport Dispatcher::CoreImplAccess::TryStartTransport( DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport(
Dispatcher* dispatcher) { Dispatcher* dispatcher) {
DCHECK(dispatcher); DCHECK(dispatcher);
......
...@@ -27,6 +27,7 @@ class Channel; ...@@ -27,6 +27,7 @@ class Channel;
class CoreImpl; class CoreImpl;
class Dispatcher; class Dispatcher;
class DispatcherTransport; class DispatcherTransport;
class HandleTable;
class LocalMessagePipeEndpoint; class LocalMessagePipeEndpoint;
class MessageInTransit; class MessageInTransit;
class ProxyMessagePipeEndpoint; class ProxyMessagePipeEndpoint;
...@@ -126,16 +127,17 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : ...@@ -126,16 +127,17 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
void RemoveWaiter(Waiter* waiter); void RemoveWaiter(Waiter* waiter);
// A dispatcher must be put into a special state in order to be sent across a // A dispatcher must be put into a special state in order to be sent across a
// message pipe. Outside of tests, only |CoreImplAccess| is allowed to do // message pipe. Outside of tests, only |HandleTableAccess| is allowed to do
// this, since there are requirements on the handle table (see below). // this, since there are requirements on the handle table (see below).
// //
// In this special state, only a restricted set of operations is allowed. // In this special state, only a restricted set of operations is allowed.
// These are the ones available as |DispatcherTransport| methods. Other // These are the ones available as |DispatcherTransport| methods. Other
// |Dispatcher| methods must not be called until |DispatcherTransport::End()| // |Dispatcher| methods must not be called until |DispatcherTransport::End()|
// has been called. // has been called.
class CoreImplAccess { class HandleTableAccess {
private: private:
friend class CoreImpl; friend class CoreImpl;
friend class HandleTable;
// Tests also need this, to avoid needing |CoreImpl|. // Tests also need this, to avoid needing |CoreImpl|.
friend DispatcherTransport test::DispatcherTryStartTransport(Dispatcher*); friend DispatcherTransport test::DispatcherTryStartTransport(Dispatcher*);
...@@ -300,8 +302,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : ...@@ -300,8 +302,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
}; };
// Wrapper around a |Dispatcher| pointer, while it's being processed to be // Wrapper around a |Dispatcher| pointer, while it's being processed to be
// passed in a message pipe. See the comment about |Dispatcher::CoreImplAccess| // passed in a message pipe. See the comment about
// for more details. // |Dispatcher::HandleTableAccess| for more details.
// //
// Note: This class is deliberately "thin" -- no more expensive than a // Note: This class is deliberately "thin" -- no more expensive than a
// |Dispatcher*|. // |Dispatcher*|.
...@@ -324,7 +326,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport { ...@@ -324,7 +326,7 @@ class MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport {
Dispatcher* dispatcher() { return dispatcher_; } Dispatcher* dispatcher() { return dispatcher_; }
private: private:
friend class Dispatcher::CoreImplAccess; friend class Dispatcher::HandleTableAccess;
explicit DispatcherTransport(Dispatcher* dispatcher) explicit DispatcherTransport(Dispatcher* dispatcher)
: dispatcher_(dispatcher) {} : dispatcher_(dispatcher) {}
......
// Copyright 2014 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/system/handle_table.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "mojo/system/constants.h"
#include "mojo/system/dispatcher.h"
namespace mojo {
namespace system {
HandleTable::Entry::Entry()
: busy(false) {
}
HandleTable::Entry::Entry(const scoped_refptr<Dispatcher>& dispatcher)
: dispatcher(dispatcher),
busy(false) {
}
HandleTable::Entry::~Entry() {
DCHECK(!busy);
}
HandleTable::HandleTable()
: next_handle_(MOJO_HANDLE_INVALID + 1) {
}
HandleTable::~HandleTable() {
// This should usually not be reached (the only instance should be owned by
// the singleton |CoreImpl|, which lives forever), except in tests.
}
Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) {
DCHECK_NE(handle, MOJO_HANDLE_INVALID);
HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
if (it == handle_to_entry_map_.end())
return NULL;
return it->second.dispatcher;
}
MojoResult HandleTable::GetAndRemoveDispatcher(
MojoHandle handle,
scoped_refptr<Dispatcher>* dispatcher) {
DCHECK_NE(handle, MOJO_HANDLE_INVALID);
DCHECK(dispatcher);
HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
if (it == handle_to_entry_map_.end())
return MOJO_RESULT_INVALID_ARGUMENT;
if (it->second.busy)
return MOJO_RESULT_BUSY;
*dispatcher = it->second.dispatcher;
handle_to_entry_map_.erase(it);
return MOJO_RESULT_OK;
}
MojoHandle HandleTable::AddDispatcher(
const scoped_refptr<Dispatcher>& dispatcher) {
if (handle_to_entry_map_.size() >= kMaxHandleTableSize)
return MOJO_HANDLE_INVALID;
return AddDispatcherNoSizeCheck(dispatcher);
}
std::pair<MojoHandle, MojoHandle> HandleTable::AddDispatcherPair(
const scoped_refptr<Dispatcher>& dispatcher0,
const scoped_refptr<Dispatcher>& dispatcher1) {
if (handle_to_entry_map_.size() + 1 >= kMaxHandleTableSize)
return std::make_pair(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID);
return std::make_pair(AddDispatcherNoSizeCheck(dispatcher0),
AddDispatcherNoSizeCheck(dispatcher1));
}
bool HandleTable::AddDispatcherVector(
const std::vector<scoped_refptr<Dispatcher> >& dispatchers,
MojoHandle* handles) {
DCHECK_LE(dispatchers.size(), kMaxMessageNumHandles);
DCHECK(handles);
// TODO(vtl): |std::numeric_limits<size_t>::max()| isn't a compile-time
// expression in C++03.
COMPILE_ASSERT(
static_cast<uint64_t>(kMaxHandleTableSize) + kMaxMessageNumHandles <
(sizeof(size_t) == 8 ? kuint64max :
static_cast<uint64_t>(kuint32max)),
addition_may_overflow);
if (handle_to_entry_map_.size() + dispatchers.size() > kMaxHandleTableSize)
return false;
for (size_t i = 0; i < dispatchers.size(); i++) {
if (dispatchers[i]) {
handles[i] = AddDispatcherNoSizeCheck(dispatchers[i]);
} else {
LOG(WARNING) << "Invalid dispatcher at index " << i;
handles[i] = MOJO_HANDLE_INVALID;
}
}
return true;
}
MojoResult HandleTable::MarkBusyAndStartTransport(
MojoHandle disallowed_handle,
const MojoHandle* handles,
uint32_t num_handles,
std::vector<DispatcherTransport>* transports) {
DCHECK_NE(disallowed_handle, MOJO_HANDLE_INVALID);
DCHECK(handles);
DCHECK_LE(num_handles, kMaxMessageNumHandles);
DCHECK(transports);
std::vector<Entry*> entries(num_handles);
// First verify all the handles and get their dispatchers.
uint32_t i;
MojoResult error_result = MOJO_RESULT_INTERNAL;
for (i = 0; i < num_handles; i++) {
// Sending your own handle is not allowed (and, for consistency, returns
// "busy").
if (handles[i] == disallowed_handle) {
error_result = MOJO_RESULT_BUSY;
break;
}
HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
if (it == handle_to_entry_map_.end()) {
error_result = MOJO_RESULT_INVALID_ARGUMENT;
break;
}
entries[i] = &it->second;
if (entries[i]->busy) {
error_result = MOJO_RESULT_BUSY;
break;
}
// Note: By marking the handle as busy here, we're also preventing the
// same handle from being sent multiple times in the same message.
entries[i]->busy = true;
// Try to start the transport.
DispatcherTransport transport =
Dispatcher::HandleTableAccess::TryStartTransport(
entries[i]->dispatcher.get());
if (!transport.is_valid()) {
// Unset the busy flag (since it won't be unset below).
entries[i]->busy = false;
error_result = MOJO_RESULT_BUSY;
break;
}
// Check if the dispatcher is busy (e.g., in a two-phase read/write).
// (Note that this must be done after the dispatcher's lock is acquired.)
if (transport.IsBusy()) {
// Unset the busy flag and end the transport (since it won't be done
// below).
entries[i]->busy = false;
transport.End();
error_result = MOJO_RESULT_BUSY;
break;
}
// Hang on to the transport (which we'll need to end the transport).
(*transports)[i] = transport;
}
if (i < num_handles) {
DCHECK_NE(error_result, MOJO_RESULT_INTERNAL);
// Unset the busy flags and release the locks.
for (uint32_t j = 0; j < i; j++) {
DCHECK(entries[j]->busy);
entries[j]->busy = false;
(*transports)[j].End();
}
return error_result;
}
return MOJO_RESULT_OK;
}
//////////////
MojoHandle HandleTable::AddDispatcherNoSizeCheck(
const scoped_refptr<Dispatcher>& dispatcher) {
DCHECK(dispatcher);
DCHECK_LT(handle_to_entry_map_.size(), kMaxHandleTableSize);
DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID);
// TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try
// assigning randomly?)
while (handle_to_entry_map_.find(next_handle_) !=
handle_to_entry_map_.end()) {
next_handle_++;
if (next_handle_ == MOJO_HANDLE_INVALID)
next_handle_++;
}
MojoHandle new_handle = next_handle_;
handle_to_entry_map_[new_handle] = Entry(dispatcher);
next_handle_++;
if (next_handle_ == MOJO_HANDLE_INVALID)
next_handle_++;
return new_handle;
}
void HandleTable::RemoveBusyHandles(const MojoHandle* handles,
uint32_t num_handles) {
DCHECK(handles);
DCHECK_LE(num_handles, kMaxMessageNumHandles);
for (uint32_t i = 0; i < num_handles; i++) {
HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
DCHECK(it != handle_to_entry_map_.end());
DCHECK(it->second.busy);
it->second.busy = false; // For the sake of a |DCHECK()|.
handle_to_entry_map_.erase(it);
}
}
void HandleTable::RestoreBusyHandles(const MojoHandle* handles,
uint32_t num_handles) {
DCHECK(handles);
DCHECK_LE(num_handles, kMaxMessageNumHandles);
for (uint32_t i = 0; i < num_handles; i++) {
HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
DCHECK(it != handle_to_entry_map_.end());
DCHECK(it->second.busy);
it->second.busy = false;
}
}
} // namespace system
} // namespace mojo
// Copyright 2014 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.
#ifndef MOJO_SYSTEM_HANDLE_TABLE_H_
#define MOJO_SYSTEM_HANDLE_TABLE_H_
#include <utility>
#include <vector>
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "mojo/public/c/system/core.h"
#include "mojo/system/system_impl_export.h"
namespace mojo {
namespace system {
class CoreImpl;
class Dispatcher;
class DispatcherTransport;
// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
// so it can be friended.
namespace internal {
bool ShutdownCheckNoLeaks(CoreImpl*);
}
// This class provides the (global) handle table (owned by |CoreImpl|), which
// maps (valid) |MojoHandle|s to |Dispatcher|s. This is abstracted so that,
// e.g., caching may be added.
//
// This class is NOT thread-safe; locking is left to |CoreImpl| (since it may
// need to make several changes -- "atomically" or in rapid successsion, in
// which case the extra locking/unlocking would be unnecessary overhead).
class MOJO_SYSTEM_IMPL_EXPORT HandleTable {
public:
HandleTable();
~HandleTable();
// Gets the dispatcher for a given handle (which should not be
// |MOJO_HANDLE_INVALID|). Returns null if there's no dispatcher for the given
// handle.
// WARNING: For efficiency, this returns a dumb pointer. If you're going to
// use the result outside |CoreImpl|'s lock, you MUST take a reference (e.g.,
// by storing the result inside a |scoped_refptr|).
Dispatcher* GetDispatcher(MojoHandle handle);
// On success, gets the dispatcher for a given handle (which should not be
// |MOJO_HANDLE_INVALID|) and removes it. (On failure, returns an appropriate
// result (and leaves |dispatcher| alone), namely
// |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
// handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
MojoResult GetAndRemoveDispatcher(MojoHandle handle,
scoped_refptr<Dispatcher>* dispatcher);
// Adds a dispatcher (which must be valid), returning the handle for it.
// Returns |MOJO_HANDLE_INVALID| on failure (if the handle table is full).
MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher);
// Adds a pair of dispatchers (which must be valid), return a pair of handles
// for them. On failure (if the handle table is full), the first (and second)
// handles will be |MOJO_HANDLE_INVALID|, and neither dispatcher will be
// added.
std::pair<MojoHandle, MojoHandle> AddDispatcherPair(
const scoped_refptr<Dispatcher>& dispatcher0,
const scoped_refptr<Dispatcher>& dispatcher1);
// Adds the given vector of dispatchers (of size at most
// |kMaxMessageNumHandles|). |handles| must point to an array of size at least
// |dispatchers.size()|. Unlike the other |AddDispatcher...()| functions, some
// of the dispatchers may be invalid (null). Returns true on success and false
// on failure (if the handle table is full), in which case it leaves
// |handles[...]| untouched (and all dispatchers unadded).
bool AddDispatcherVector(
const std::vector<scoped_refptr<Dispatcher> >& dispatchers,
MojoHandle* handles);
// Tries to mark the given handles as busy and start transport on them (i.e.,
// take their dispatcher locks); |transports| must be sized to contain
// |num_handles| elements. On failure, returns them to their original
// (non-busy, unlocked state).
MojoResult MarkBusyAndStartTransport(
MojoHandle disallowed_handle,
const MojoHandle* handles,
uint32_t num_handles,
std::vector<DispatcherTransport>* transports);
// Remove the given handles, which must all be present and which should have
// previously been marked busy by |MarkBusyAndStartTransport()|.
void RemoveBusyHandles(const MojoHandle* handles, uint32_t num_handles);
// Restores the given handles, which must all be present and which should have
// previously been marked busy by |MarkBusyAndStartTransport()|, to a non-busy
// state.
void RestoreBusyHandles(const MojoHandle* handles, uint32_t num_handles);
private:
friend bool internal::ShutdownCheckNoLeaks(CoreImpl*);
struct Entry {
Entry();
explicit Entry(const scoped_refptr<Dispatcher>& dispatcher);
~Entry();
scoped_refptr<Dispatcher> dispatcher;
bool busy;
};
typedef base::hash_map<MojoHandle, Entry> HandleToEntryMap;
// Adds the given dispatcher to the handle table, not doing any size checks.
MojoHandle AddDispatcherNoSizeCheck(
const scoped_refptr<Dispatcher>& dispatcher);
HandleToEntryMap handle_to_entry_map_;
MojoHandle next_handle_; // Invariant: never |MOJO_HANDLE_INVALID|.
DISALLOW_COPY_AND_ASSIGN(HandleTable);
};
} // namespace system
} // namespace mojo
#endif // MOJO_SYSTEM_HANDLE_TABLE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment