summaryrefslogtreecommitdiffstats
path: root/mojo
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-27 22:37:23 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-27 22:37:23 +0000
commit7c77a0b854c5e4222601f9572f2572206eeae7ce (patch)
tree80488d3737b0ca773413f1a4fc4b3dc49d10c1b0 /mojo
parentc3c2a062f121cd6024d564fd0c959601ef8b45df (diff)
downloadchromium_src-7c77a0b854c5e4222601f9572f2572206eeae7ce.zip
chromium_src-7c77a0b854c5e4222601f9572f2572206eeae7ce.tar.gz
chromium_src-7c77a0b854c5e4222601f9572f2572206eeae7ce.tar.bz2
Mojo: Add hooks for dispatchers to serialize themselves.
Reland (committed r253869, reverted r253876; win64 breakage). R=yzshen@chromium.org Review URL: https://codereview.chromium.org/182163002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@253947 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo')
-rw-r--r--mojo/system/dispatcher.cc67
-rw-r--r--mojo/system/dispatcher.h47
-rw-r--r--mojo/system/message_in_transit.cc64
-rw-r--r--mojo/system/message_in_transit.h42
4 files changed, 178 insertions, 42 deletions
diff --git a/mojo/system/dispatcher.cc b/mojo/system/dispatcher.cc
index ac02b83..3d970d0 100644
--- a/mojo/system/dispatcher.cc
+++ b/mojo/system/dispatcher.cc
@@ -28,6 +28,23 @@ DispatcherTransport Dispatcher::CoreImplAccess::TryStartTransport(
return DispatcherTransport(dispatcher);
}
+// static
+size_t Dispatcher::MessageInTransitAccess::GetMaximumSerializedSize(
+ const Dispatcher* dispatcher,
+ const Channel* channel) {
+ DCHECK(dispatcher);
+ return dispatcher->GetMaximumSerializedSize(channel);
+}
+
+// static
+size_t Dispatcher::MessageInTransitAccess::SerializeAndClose(
+ Dispatcher* dispatcher,
+ void* destination,
+ Channel* channel) {
+ DCHECK(dispatcher);
+ return dispatcher->SerializeAndClose(destination, channel);
+}
+
MojoResult Dispatcher::Close() {
base::AutoLock locker(lock_);
if (is_closed_)
@@ -297,6 +314,23 @@ void Dispatcher::RemoveWaiterImplNoLock(Waiter* /*waiter*/) {
// will do something nontrivial.
}
+size_t Dispatcher::GetMaximumSerializedSizeImplNoLock(
+ const Channel* /*channel*/) const {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, serializing isn't supported.
+ return 0;
+}
+
+size_t Dispatcher::SerializeAndCloseImplNoLock(void* /*destination*/,
+ Channel* /*channel*/) {
+ lock_.AssertAcquired();
+ DCHECK(is_closed_);
+ // By default, serializing isn't supported, so just close.
+ CloseImplNoLock();
+ return 0;
+}
+
bool Dispatcher::IsBusyNoLock() const {
lock_.AssertAcquired();
DCHECK(!is_closed_);
@@ -324,6 +358,39 @@ Dispatcher::CreateEquivalentDispatcherAndCloseNoLock() {
return CreateEquivalentDispatcherAndCloseImplNoLock();
}
+size_t Dispatcher::GetMaximumSerializedSize(const Channel* channel) const {
+ DCHECK(channel);
+ DCHECK(HasOneRef());
+
+ base::AutoLock locker(lock_);
+ DCHECK(!is_closed_);
+ return GetMaximumSerializedSizeImplNoLock(channel);
+}
+
+size_t Dispatcher::SerializeAndClose(void* destination, Channel* channel) {
+ DCHECK(destination);
+ DCHECK(channel);
+ DCHECK(HasOneRef());
+
+ base::AutoLock locker(lock_);
+ DCHECK(!is_closed_);
+
+ // We have to call |GetMaximumSerializedSizeImplNoLock()| first, because we
+ // leave it to |SerializeAndCloseImplNoLock()| to close the thing.
+ size_t max_size = DCHECK_IS_ON() ?
+ GetMaximumSerializedSizeImplNoLock(channel) : static_cast<size_t>(-1);
+
+ // Like other |...Close()| methods, we mark ourselves as closed before calling
+ // the impl.
+ is_closed_ = true;
+ // No need to cancel waiters: we shouldn't have any (and shouldn't be in
+ // |Core|'s handle table.
+
+ size_t size = SerializeAndCloseImplNoLock(destination, channel);
+ DCHECK_LE(size, max_size);
+ return size;
+}
+
// DispatcherTransport ---------------------------------------------------------
void DispatcherTransport::End() {
diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h
index 59e536a..73bf77b 100644
--- a/mojo/system/dispatcher.h
+++ b/mojo/system/dispatcher.h
@@ -19,10 +19,12 @@
namespace mojo {
namespace system {
+class Channel;
class CoreImpl;
class Dispatcher;
class DispatcherTransport;
class LocalMessagePipeEndpoint;
+class MessageInTransit;
class ProxyMessagePipeEndpoint;
class Waiter;
@@ -139,6 +141,24 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
static DispatcherTransport TryStartTransport(Dispatcher* dispatcher);
};
+ // A |MessageInTransit| may serialize dispatchers that are attached to it to a
+ // given |Channel|. These may only be called on such dispatchers. See the
+ // |Dispatcher| methods of the same names for more details.
+ // TODO(vtl): Consider making another wrapper similar to |DispatcherTransport|
+ // (but with an owning, unique reference), and having
+ // |CreateEquivalentDispatcherAndCloseImplNoLock()| return that wrapper (and
+ // |MessageInTransit| only holding on to such wrappers).
+ class MessageInTransitAccess {
+ private:
+ friend class MessageInTransit;
+
+ static size_t GetMaximumSerializedSize(const Dispatcher* dispatcher,
+ const Channel* channel);
+ static size_t SerializeAndClose(Dispatcher* dispatcher,
+ void* destination,
+ Channel* channel);
+ };
+
protected:
Dispatcher();
@@ -194,6 +214,17 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
MojoResult wake_result);
virtual void RemoveWaiterImplNoLock(Waiter* waiter);
+ // These implement the API used to serialize dispatchers to a |Channel|
+ // (described below). They will only be called on a dispatcher that's attached
+ // to and "owned" by a |MessageInTransit|. See the non-"impl" versions for
+ // more information.
+ // TODO(vtl): Consider making these pure virtual once most things support
+ // being passed over a message pipe.
+ virtual size_t GetMaximumSerializedSizeImplNoLock(
+ const Channel* channel) const;
+ virtual size_t SerializeAndCloseImplNoLock(void* destination,
+ Channel* channel);
+
// Available to subclasses. (Note: Returns a non-const reference, just like
// |base::AutoLock|'s constructor takes a non-const reference.)
base::Lock& lock() const { return lock_; }
@@ -219,6 +250,22 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
// under the dispatcher's lock.
scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock();
+ // API to serialize dispatchers to a |Channel|, exposed to only
+ // |MessageInTransit| (via |MessageInTransitAccess|). They may only be called
+ // on a dispatcher attached to a |MessageInTransit| (and in particular not in
+ // |CoreImpl|'s handle table).
+ // Gets the maximum amount of space that'll be needed to serialize this
+ // dispatcher to the given |Channel|. Returns zero to indicate that this
+ // dispatcher cannot be serialized (to the given |Channel|).
+ size_t GetMaximumSerializedSize(const Channel* channel) const;
+ // Serializes this dispatcher to the given |Channel| by writing to
+ // |destination| and then closes this dispatcher. It may write no more than
+ // was indicated by |GetMaximumSerializedSize()|. (WARNING: Beware of races,
+ // e.g., if something can be mutated between the two calls!) This may return
+ // zero to indicate failure to serialize (but this dispatcher will still be
+ // closed).
+ size_t SerializeAndClose(void* destination, Channel* channel);
+
// This protects the following members as well as any state added by
// subclasses.
mutable base::Lock lock_;
diff --git a/mojo/system/message_in_transit.cc b/mojo/system/message_in_transit.cc
index 3a0abe2..16d3e0c 100644
--- a/mojo/system/message_in_transit.cc
+++ b/mojo/system/message_in_transit.cc
@@ -20,12 +20,16 @@ struct MessageInTransit::PrivateStructForCompileAsserts {
// The size of |Header| must be appropriate to maintain alignment of the
// following data.
COMPILE_ASSERT(sizeof(Header) % kMessageAlignment == 0,
- sizeof_MessageInTransit_Header_not_a_multiple_of_alignment);
+ sizeof_MessageInTransit_Header_invalid);
// Avoid dangerous situations, but making sure that the size of the "header" +
// the size of the data fits into a 31-bit number.
COMPILE_ASSERT(static_cast<uint64_t>(sizeof(Header)) + kMaxMessageNumBytes <=
0x7fffffffULL,
kMaxMessageNumBytes_too_big);
+
+ // The size of |HandleTableEntry| must be appropriate to maintain alignment.
+ COMPILE_ASSERT(sizeof(HandleTableEntry) % kMessageAlignment == 0,
+ sizeof_MessageInTransit_HandleTableEntry_invalid);
};
STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
@@ -192,36 +196,48 @@ void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) {
if (!num_handles())
return;
- // The size of the secondary buffer. We'll start with the size of the entry
- // size table (which will contain the size of the data for each handle), and
- // add to it as we go along.
- size_t size = RoundUpMessageAlignment(num_handles() * sizeof(uint32_t));
- // The maximum size that we'll need for the secondary buffer. We'll allocate
- // this much.
- size_t max_size = size;
- // TODO(vtl): Iterate through dispatchers and query their maximum size (and
- // add each, rounded up, to |max_size|).
-
- secondary_buffer_ = base::AlignedAlloc(max_size, kMessageAlignment);
- // TODO(vtl): I wonder if it's faster to clear everything once, or to only
- // clear padding as needed.
- memset(secondary_buffer_, 0, max_size);
-
- uint32_t* entry_size_table = static_cast<uint32_t*>(secondary_buffer_);
+ size_t handle_table_size = num_handles() * sizeof(HandleTableEntry);
+ // The size of the secondary buffer. We'll start with the size of the handle
+ // table, and add to it as we go along.
+ size_t size = handle_table_size;
for (size_t i = 0; i < dispatchers_->size(); i++) {
- // The entry size table entry is already zero by default.
if (!(*dispatchers_)[i])
continue;
- // TODO(vtl): Serialize this dispatcher (getting its actual size, and adding
- // that (rounded up) to |size|.
- entry_size_table[i] = 0;
-
- DCHECK((*dispatchers_)[i]->HasOneRef());
- (*dispatchers_)[i]->Close();
+ size += RoundUpMessageAlignment(
+ Dispatcher::MessageInTransitAccess::GetMaximumSerializedSize(
+ (*dispatchers_)[i].get(), channel));
+ // TODO(vtl): Check for overflow?
}
+ secondary_buffer_ = base::AlignedAlloc(size, kMessageAlignment);
+ // TODO(vtl): Check for overflow?
secondary_buffer_size_ = static_cast<uint32_t>(size);
+ // Entirely clear out the secondary buffer, since then we won't have to worry
+ // about clearing padding or unused space (e.g., if a dispatcher fails to
+ // serialize).
+ memset(secondary_buffer_, 0, size);
+
+ HandleTableEntry* handle_table =
+ static_cast<HandleTableEntry*>(secondary_buffer_);
+ size_t current_offset = handle_table_size;
+ for (size_t i = 0; i < dispatchers_->size(); i++) {
+ if (!(*dispatchers_)[i]) {
+ // The |handle_table[i].size| is already zero, designating this entry as
+ // invalid.
+ continue;
+ }
+
+ handle_table[i].offset = static_cast<uint32_t>(current_offset);
+ handle_table[i].size = static_cast<uint32_t>(
+ Dispatcher::MessageInTransitAccess::SerializeAndClose(
+ (*dispatchers_)[i].get(),
+ static_cast<char*>(secondary_buffer_) + current_offset,
+ channel));
+ current_offset += RoundUpMessageAlignment(handle_table[i].size);
+ DCHECK_LE(current_offset, size);
+ }
+
UpdateTotalSize();
}
diff --git a/mojo/system/message_in_transit.h b/mojo/system/message_in_transit.h
index 60d5c25..1503e44 100644
--- a/mojo/system/message_in_transit.h
+++ b/mojo/system/message_in_transit.h
@@ -20,6 +20,25 @@ namespace system {
class Channel;
// This class is used to represent data in transit. It is thread-unsafe.
+//
+// |MessageInTransit| buffers:
+//
+// A |MessageInTransit| can be serialized by writing the main buffer and then,
+// if it has one, the secondary buffer. Both buffers are
+// |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes
+// in size.
+//
+// The main buffer consists of the header (of type |Header|, which is an
+// internal detail of this class) followed immediately by the message data
+// (accessed by |bytes()| and of size |num_bytes()|, and also
+// |kMessageAlignment|-byte aligned), and then any padding needed to make the
+// main buffer a multiple of |kMessageAlignment| bytes in size.
+//
+// The secondary buffer consists first of a table of |HandleTableEntry|s (each
+// of which is already a multiple of |kMessageAlignment| in size), followed by
+// data needed for the |HandleTableEntry|s: A |HandleTableEntry| consists of an
+// offset (in bytes, relative to the start of the secondary buffer; we guarantee
+// that it's a multiple of |kMessageAlignment|), and a size (in bytes).
class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
public:
typedef uint16_t Type;
@@ -100,24 +119,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
// stays alive through the call.
void SerializeAndCloseDispatchers(Channel* channel);
- // |MessageInTransit| buffers: A |MessageInTransit| can be serialized by
- // writing the main buffer and then, if it has one, the secondary buffer. Both
- // buffers are |kMessageAlignment|-byte aligned and a multiple of
- // |kMessageAlignment| bytes in size.
- //
- // The main buffer consists of the header (of type |Header|, which is an
- // internal detail of this class) followed immediately by the message data
- // (accessed by |bytes()| and of size |num_bytes()|, and also
- // |kMessageAlignment|-byte aligned), and then any padding needed to make the
- // main buffer a multiple of |kMessageAlignment| bytes in size.
- //
- // The secondary buffer consists first of a table of |uint32_t|s with
- // |num_handles()| entries; each entry in the table is the (unpadded) size of
- // the data for a handle (a size of 0 indicates in invalid handle/null
- // dispatcher). The table is followed by padding, then the first entry and
- // padding, the second entry and padding, etc. (padding as required to
- // maintain |kMessageAlignment|-byte alignment).
-
// Gets the main buffer and its size (in number of bytes), respectively.
const void* main_buffer() const { return main_buffer_; }
size_t main_buffer_size() const { return main_buffer_size_; }
@@ -188,6 +189,11 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
uint32_t reserved1;
};
+ struct HandleTableEntry {
+ uint32_t offset;
+ uint32_t size; // (Not including any padding.) A size of 0 means "invalid".
+ };
+
const Header* header() const {
return static_cast<const Header*>(main_buffer_);
}