summaryrefslogtreecommitdiffstats
path: root/mojo/system
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 03:40:21 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 03:40:21 +0000
commit68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b (patch)
treec7dae7d4373144356c410354ff3d2dfa93bff915 /mojo/system
parent7e6d4055cddfa4a06800cffcbf978c342a608639 (diff)
downloadchromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.zip
chromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.tar.gz
chromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.tar.bz2
Mojo: Factor MessageInTransit secondary buffer stuff out into a separate class.
Also write a big comment explaining what I'm going to do. R=darin@chromium.org Review URL: https://codereview.chromium.org/265753006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268032 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/system')
-rw-r--r--mojo/system/channel.cc16
-rw-r--r--mojo/system/dispatcher.cc6
-rw-r--r--mojo/system/dispatcher.h21
-rw-r--r--mojo/system/message_in_transit.cc268
-rw-r--r--mojo/system/message_in_transit.h92
-rw-r--r--mojo/system/message_pipe.cc9
-rw-r--r--mojo/system/raw_channel.cc29
-rw-r--r--mojo/system/raw_channel_unittest.cc2
-rw-r--r--mojo/system/transport_data.cc255
-rw-r--r--mojo/system/transport_data.h163
10 files changed, 500 insertions, 361 deletions
diff --git a/mojo/system/channel.cc b/mojo/system/channel.cc
index ca65284..7446cb9 100644
--- a/mojo/system/channel.cc
+++ b/mojo/system/channel.cc
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/transport_data.h"
namespace mojo {
namespace system {
@@ -347,10 +348,17 @@ void Channel::OnReadMessageForDownstream(
return;
}
- // We need to duplicate the message, because |EnqueueMessage()| will take
- // ownership of it.
+ // We need to duplicate the message (data), because |EnqueueMessage()| will
+ // take ownership of it.
scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view));
- message->DeserializeDispatchers(this);
+ if (message_view.transport_data_buffer_size() > 0) {
+ DCHECK(message_view.transport_data_buffer());
+ message->SetDispatchers(
+ TransportData::DeserializeDispatchersFromBuffer(
+ message_view.transport_data_buffer(),
+ message_view.transport_data_buffer_size(),
+ this));
+ }
MojoResult result = endpoint_info.message_pipe->EnqueueMessage(
MessagePipe::GetPeerPort(endpoint_info.port), message.Pass());
if (result != MOJO_RESULT_OK) {
@@ -458,7 +466,7 @@ bool Channel::SendControlMessage(MessageInTransit::Subtype subtype,
DVLOG(2) << "Sending channel control message: subtype " << subtype
<< ", local ID " << local_id << ", remote ID " << remote_id;
scoped_ptr<MessageInTransit> message(new MessageInTransit(
- MessageInTransit::kTypeChannel, subtype, 0, 0, NULL));
+ MessageInTransit::kTypeChannel, subtype, 0, NULL));
message->set_source_id(local_id);
message->set_destination_id(remote_id);
return WriteMessage(message.Pass());
diff --git a/mojo/system/dispatcher.cc b/mojo/system/dispatcher.cc
index 8b0d71f..1107cf3 100644
--- a/mojo/system/dispatcher.cc
+++ b/mojo/system/dispatcher.cc
@@ -40,7 +40,7 @@ DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport(
}
// static
-void Dispatcher::MessageInTransitAccess::StartSerialize(
+void Dispatcher::TransportDataAccess::StartSerialize(
Dispatcher* dispatcher,
Channel* channel,
size_t* max_size,
@@ -50,7 +50,7 @@ void Dispatcher::MessageInTransitAccess::StartSerialize(
}
// static
-bool Dispatcher::MessageInTransitAccess::EndSerializeAndClose(
+bool Dispatcher::TransportDataAccess::EndSerializeAndClose(
Dispatcher* dispatcher,
Channel* channel,
void* destination,
@@ -62,7 +62,7 @@ bool Dispatcher::MessageInTransitAccess::EndSerializeAndClose(
}
// static
-scoped_refptr<Dispatcher> Dispatcher::MessageInTransitAccess::Deserialize(
+scoped_refptr<Dispatcher> Dispatcher::TransportDataAccess::Deserialize(
Channel* channel,
int32_t type,
const void* source,
diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h
index 504e33a..25c02e8 100644
--- a/mojo/system/dispatcher.h
+++ b/mojo/system/dispatcher.h
@@ -27,9 +27,9 @@ class Dispatcher;
class DispatcherTransport;
class HandleTable;
class LocalMessagePipeEndpoint;
-class MessageInTransit;
class ProxyMessagePipeEndpoint;
class RawSharedBufferMapping;
+class TransportData;
class Waiter;
namespace test {
@@ -146,18 +146,19 @@ 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| and then (probably in a different process) deserialize.
+ // A |TransportData| may serialize dispatchers that are given to it (and which
+ // were previously attached to the |MessageInTransit| that is creating it) to
+ // a given |Channel| and then (probably in a different process) deserialize.
// Note that the |MessageInTransit| "owns" (i.e., has the only ref to) these
// dispatchers, so there are no locking issues. (There's no lock ordering
// issue, and in fact no need to take dispatcher locks at all.)
// 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 {
+ // |MessageInTransit|, etc. only holding on to such wrappers).
+ class TransportDataAccess {
private:
- friend class MessageInTransit;
+ friend class TransportData;
// Serialization API. These functions may only be called on such
// dispatchers. (|channel| is the |Channel| to which the dispatcher is to be
@@ -286,16 +287,16 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
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
+ // |TransportData| (via |TransportData|). They may only be called on a
+ // dispatcher attached to a |MessageInTransit| (and in particular not in
// |CoreImpl|'s handle table).
//
// Starts the serialization. Returns (via the two "out" parameters) the
// maximum amount of space that may be needed to serialize this dispatcher to
// the given |Channel| (no more than
- // |MessageInTransit::kMaxSerializedDispatcherSize|) and the maximum number of
+ // |TransportData::kMaxSerializedDispatcherSize|) and the maximum number of
// |PlatformHandle|s that may need to be attached (no more than
- // |MessageInTransit::kMaxSerializedDispatcherPlatformHandles|). If this
+ // |TransportData::kMaxSerializedDispatcherPlatformHandles|). If this
// dispatcher cannot be serialized to the given |Channel|, |*max_size| and
// |*max_platform_handles| should be set to zero. A call to this method will
// ALWAYS be followed by a call to |EndSerializeAndClose()| (even if this
diff --git a/mojo/system/message_in_transit.cc b/mojo/system/message_in_transit.cc
index a38ac1c..79f1184 100644
--- a/mojo/system/message_in_transit.cc
+++ b/mojo/system/message_in_transit.cc
@@ -6,11 +6,10 @@
#include <string.h>
-#include <new>
-
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "mojo/system/constants.h"
+#include "mojo/system/transport_data.h"
namespace mojo {
namespace system {
@@ -32,10 +31,6 @@ STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
MessageInTransit::kInvalidEndpointId;
STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment;
-STATIC_CONST_MEMBER_DEFINITION const size_t
- MessageInTransit::kMaxSerializedDispatcherSize;
-STATIC_CONST_MEMBER_DEFINITION const size_t
- MessageInTransit::kMaxSerializedDispatcherPlatformHandles;
struct MessageInTransit::PrivateStructForCompileAsserts {
// The size of |Header| must be a multiple of the alignment.
@@ -51,26 +46,8 @@ struct MessageInTransit::PrivateStructForCompileAsserts {
// size is a multiple of the alignment.
COMPILE_ASSERT(kMaxMessageNumBytes % kMessageAlignment == 0,
kMessageAlignment_not_a_multiple_of_alignment);
-
- // The maximum serialized dispatcher size must be a multiple of the alignment.
- COMPILE_ASSERT(kMaxSerializedDispatcherSize % kMessageAlignment == 0,
- kMaxSerializedDispatcherSize_not_a_multiple_of_alignment);
-
- // The size of |HandleTableEntry| must be a multiple of the alignment.
- COMPILE_ASSERT(sizeof(HandleTableEntry) % kMessageAlignment == 0,
- sizeof_MessageInTransit_HandleTableEntry_invalid);
};
-// For each attached (Mojo) handle, there'll be a handle table entry and
-// serialized dispatcher data.
-// static
-const size_t MessageInTransit::kMaxSecondaryBufferSize = kMaxMessageNumHandles *
- (sizeof(HandleTableEntry) + kMaxSerializedDispatcherSize);
-
-// static
-const size_t MessageInTransit::kMaxPlatformHandles =
- kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles;
-
MessageInTransit::View::View(size_t message_size, const void* buffer)
: buffer_(buffer) {
size_t next_message_size = 0;
@@ -89,11 +66,13 @@ bool MessageInTransit::View::IsValid(const char** error_message) const {
return false;
}
- if (const char* secondary_buffer_error_message =
- ValidateSecondaryBuffer(num_handles(), secondary_buffer(),
- secondary_buffer_size())) {
- *error_message = secondary_buffer_error_message;
- return false;
+ if (transport_data_buffer_size() > 0) {
+ const char* e = TransportData::ValidateBuffer(transport_data_buffer(),
+ transport_data_buffer_size());
+ if (e) {
+ *error_message = e;
+ return false;
+ }
}
return true;
@@ -102,14 +81,11 @@ bool MessageInTransit::View::IsValid(const char** error_message) const {
MessageInTransit::MessageInTransit(Type type,
Subtype subtype,
uint32_t num_bytes,
- uint32_t num_handles,
const void* bytes)
: main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)),
main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
- kMessageAlignment))),
- secondary_buffer_size_(0) {
+ kMessageAlignment))) {
DCHECK_LE(num_bytes, kMaxMessageNumBytes);
- DCHECK_LE(num_handles, kMaxMessageNumHandles);
// |total_size| is updated below, from the other values.
header()->type = type;
@@ -117,9 +93,8 @@ MessageInTransit::MessageInTransit(Type type,
header()->source_id = kInvalidEndpointId;
header()->destination_id = kInvalidEndpointId;
header()->num_bytes = num_bytes;
- header()->num_handles = num_handles;
- // Note: If dispatchers are subsequently attached (in particular, if
- // |num_handles| is nonzero), then |total_size| will have to be adjusted.
+ // Note: If dispatchers are subsequently attached, then |total_size| will have
+ // to be adjusted.
UpdateTotalSize();
if (bytes) {
@@ -131,24 +106,14 @@ MessageInTransit::MessageInTransit(Type type,
}
}
-// TODO(vtl): Do I really want/need to copy the secondary buffer here, or should
-// I just create (deserialize) the dispatchers right away?
MessageInTransit::MessageInTransit(const View& message_view)
: main_buffer_size_(message_view.main_buffer_size()),
main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
- kMessageAlignment))),
- secondary_buffer_size_(message_view.secondary_buffer_size()),
- secondary_buffer_(secondary_buffer_size_ ?
- static_cast<char*>(
- base::AlignedAlloc(secondary_buffer_size_,
- kMessageAlignment)) : NULL) {
+ kMessageAlignment))) {
DCHECK_GE(main_buffer_size_, sizeof(Header));
DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_);
- memcpy(secondary_buffer_.get(), message_view.secondary_buffer(),
- secondary_buffer_size_);
-
DCHECK_EQ(main_buffer_size_,
RoundUpMessageAlignment(sizeof(Header) + num_bytes()));
}
@@ -163,17 +128,6 @@ MessageInTransit::~MessageInTransit() {
(*dispatchers_)[i]->Close();
}
}
-
- if (platform_handles_) {
- for (size_t i = 0; i < platform_handles_->size(); i++)
- (*platform_handles_)[i].CloseIfNecessary();
- }
-
-#ifndef NDEBUG
- secondary_buffer_size_ = 0;
- dispatchers_.reset();
- platform_handles_.reset();
-#endif
}
// static
@@ -210,206 +164,24 @@ void MessageInTransit::SetDispatchers(
void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) {
DCHECK(channel);
- DCHECK(!secondary_buffer_);
+ DCHECK(!transport_data_);
- const size_t num_handles = dispatchers_ ? dispatchers_->size() : 0;
- if (!num_handles)
+ if (!dispatchers_ || !dispatchers_->size())
return;
- // The offset to the start of the (Mojo) handle table.
- // TODO(vtl): Add a header to the secondary buffer.
- const size_t handle_table_start_offset = 0;
- // The offset to the start of the serialized dispatcher data.
- const size_t serialized_dispatcher_start_offset =
- handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
- // The estimated size of the secondary buffer. We compute this estimate below.
- // It must be at least as big as the (eventual) actual size.
- size_t estimated_size = serialized_dispatcher_start_offset;
- size_t num_platform_handles = 0;
-#if DCHECK_IS_ON
- std::vector<size_t> all_max_sizes(num_handles);
- std::vector<size_t> all_max_platform_handles(num_handles);
-#endif
- for (size_t i = 0; i < num_handles; i++) {
- if (Dispatcher* dispatcher = (*dispatchers_)[i].get()) {
- size_t max_size = 0;
- size_t max_platform_handles = 0;
- Dispatcher::MessageInTransitAccess::StartSerialize(
- dispatcher, channel, &max_size, &max_platform_handles);
-
- DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
- estimated_size += RoundUpMessageAlignment(max_size);
- DCHECK_LE(estimated_size, kMaxSecondaryBufferSize);
-
- DCHECK_LE(max_platform_handles,
- kMaxSerializedDispatcherPlatformHandles);
- num_platform_handles += max_platform_handles;
- DCHECK_LE(num_platform_handles, kMaxPlatformHandles);
-
-#if DCHECK_IS_ON
- all_max_sizes[i] = max_size;
- all_max_platform_handles[i] = max_platform_handles;
-#endif
- }
- }
-
- secondary_buffer_.reset(static_cast<char*>(
- base::AlignedAlloc(estimated_size, kMessageAlignment)));
- // 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_.get(), 0, estimated_size);
-
- if (num_platform_handles > 0) {
- DCHECK(!platform_handles_);
- platform_handles_.reset(new std::vector<embedder::PlatformHandle>());
- }
-
- HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
- secondary_buffer_.get() + handle_table_start_offset);
- size_t current_offset = serialized_dispatcher_start_offset;
- for (size_t i = 0; i < num_handles; i++) {
- Dispatcher* dispatcher = (*dispatchers_)[i].get();
- if (!dispatcher) {
- COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0,
- value_of_Dispatcher_kTypeUnknown_must_be_zero);
- continue;
- }
-
-#if DCHECK_IS_ON
- size_t old_platform_handles_size =
- platform_handles_ ? platform_handles_->size() : 0;
-#endif
-
- void* destination = secondary_buffer_.get() + current_offset;
- size_t actual_size = 0;
- if (Dispatcher::MessageInTransitAccess::EndSerializeAndClose(
- dispatcher, channel, destination, &actual_size,
- platform_handles_.get())) {
- handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
- handle_table[i].offset = static_cast<uint32_t>(current_offset);
- handle_table[i].size = static_cast<uint32_t>(actual_size);
-
-#if DCHECK_IS_ON
- DCHECK_LE(actual_size, all_max_sizes[i]);
- DCHECK_LE(platform_handles_ ? (platform_handles_->size() -
- old_platform_handles_size) : 0,
- all_max_platform_handles[i]);
-#endif
- } else {
- // Nothing to do on failure, since |secondary_buffer_| was cleared, and
- // |kTypeUnknown| is zero. The handle was simply closed.
- LOG(ERROR) << "Failed to serialize handle to remote message pipe";
- }
-
- current_offset += RoundUpMessageAlignment(actual_size);
- DCHECK_LE(current_offset, estimated_size);
- DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0,
- num_platform_handles);
- }
-
- // There's no aligned realloc, so it's no good way to release unused space (if
- // we overshot our estimated space requirements).
- secondary_buffer_size_ = current_offset;
-
- // The dispatchers (which we "owned") were closed. We can dispose of them now.
- dispatchers_.reset();
+ transport_data_.reset(new TransportData(dispatchers_.Pass(), channel));
// Update the sizes in the message header.
UpdateTotalSize();
}
-// Note: The message's secondary buffer should have been checked by calling
-// |View::IsValid()| (on the |View|) first.
-void MessageInTransit::DeserializeDispatchers(Channel* channel) {
- DCHECK(!dispatchers_);
-
- // Already checked by |View::IsValid()|:
- DCHECK_LE(num_handles(), kMaxMessageNumHandles);
-
- if (!num_handles())
- return;
-
- dispatchers_.reset(
- new std::vector<scoped_refptr<Dispatcher> >(num_handles()));
-
- size_t handle_table_size = num_handles() * sizeof(HandleTableEntry);
- // Already checked by |View::IsValid()|:
- DCHECK_LE(handle_table_size, secondary_buffer_size_);
-
- const HandleTableEntry* handle_table =
- reinterpret_cast<const HandleTableEntry*>(secondary_buffer_.get());
- for (size_t i = 0; i < num_handles(); i++) {
- size_t offset = handle_table[i].offset;
- size_t size = handle_table[i].size;
- // Already checked by |View::IsValid()|:
- DCHECK_EQ(offset % kMessageAlignment, 0u);
- DCHECK_LE(offset, secondary_buffer_size_);
- DCHECK_LE(offset + size, secondary_buffer_size_);
-
- const void* source = secondary_buffer_.get() + offset;
- (*dispatchers_)[i] = Dispatcher::MessageInTransitAccess::Deserialize(
- channel, handle_table[i].type, source, size);
- }
-}
-
-// Validates the secondary buffer. Returns null on success, or a human-readable
-// error message on error.
-// static
-const char* MessageInTransit::ValidateSecondaryBuffer(
- size_t num_handles,
- const void* secondary_buffer,
- size_t secondary_buffer_size) {
- // Always make sure that the secondary buffer size is sane (even if we have no
- // handles); if it's not, someone's messing with us.
- if (secondary_buffer_size > kMaxSecondaryBufferSize)
- return "Message secondary buffer too large";
-
- // Fast-path for the common case (no handles => no secondary buffer).
- if (num_handles == 0) {
- // We shouldn't have a secondary buffer in this case.
- if (secondary_buffer_size > 0)
- return "Message has no handles attached, but secondary buffer present";
- return NULL;
- }
-
- // Sanity-check |num_handles| (before multiplying it against anything).
- if (num_handles > kMaxMessageNumHandles)
- return "Message handle payload too large";
-
- if (secondary_buffer_size < num_handles * sizeof(HandleTableEntry))
- return "Message secondary buffer too small";
-
- DCHECK(secondary_buffer);
- const HandleTableEntry* handle_table =
- static_cast<const HandleTableEntry*>(secondary_buffer);
-
- static const char kInvalidSerializedDispatcher[] =
- "Message contains invalid serialized dispatcher";
- for (size_t i = 0; i < num_handles; i++) {
- size_t offset = handle_table[i].offset;
- if (offset % kMessageAlignment != 0)
- return kInvalidSerializedDispatcher;
-
- size_t size = handle_table[i].size;
- if (size > kMaxSerializedDispatcherSize || size > secondary_buffer_size)
- return kInvalidSerializedDispatcher;
-
- // Note: This is an overflow-safe check for |offset + size >
- // secondary_buffer_size()| (we know that |size <= secondary_buffer_size()|
- // from the previous check).
- if (offset > secondary_buffer_size - size)
- return kInvalidSerializedDispatcher;
- }
-
- return NULL;
-}
-
void MessageInTransit::UpdateTotalSize() {
DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
- DCHECK_EQ(secondary_buffer_size_ % kMessageAlignment, 0u);
- header()->total_size =
- static_cast<uint32_t>(main_buffer_size_ + secondary_buffer_size_);
+ header()->total_size = static_cast<uint32_t>(main_buffer_size_);
+ if (transport_data_) {
+ header()->total_size +=
+ static_cast<uint32_t>(transport_data_->buffer_size());
+ }
}
} // namespace system
diff --git a/mojo/system/message_in_transit.h b/mojo/system/message_in_transit.h
index 1fc021e..103ac3e 100644
--- a/mojo/system/message_in_transit.h
+++ b/mojo/system/message_in_transit.h
@@ -5,6 +5,7 @@
#ifndef MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_
#define MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_
+#include <stddef.h>
#include <stdint.h>
#include <vector>
@@ -12,7 +13,6 @@
#include "base/macros.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/scoped_ptr.h"
-#include "mojo/embedder/platform_handle.h"
#include "mojo/system/dispatcher.h"
#include "mojo/system/system_impl_export.h"
@@ -20,13 +20,14 @@ namespace mojo {
namespace system {
class Channel;
+class TransportData;
// 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
+// if it has one, the transport data buffer. Both buffers are
// |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes
// in size.
//
@@ -36,11 +37,8 @@ class Channel;
// |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).
+// See |TransportData| for a description of the (serialized) transport data
+// buffer.
class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
public:
typedef uint16_t Type;
@@ -69,14 +67,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
// quantity (which must be a power of 2).
static const size_t kMessageAlignment = 8;
- // The maximum size of a single serialized dispatcher. This must be a multiple
- // of |kMessageAlignment|.
- static const size_t kMaxSerializedDispatcherSize = 10000;
-
- // The maximum number of platform handles to attach for a single serialized
- // dispatcher.
- static const size_t kMaxSerializedDispatcherPlatformHandles = 2;
-
// Forward-declare |Header| so that |View| can use it:
private:
struct Header;
@@ -90,12 +80,10 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
// |buffer| should be |kMessageAlignment|-byte aligned.
View(size_t message_size, const void* buffer);
- // Checks the following things versus pre-determined limits:
- // - |num_bytes()| and |main_buffer_size()|,
- // - |num_handles()| and |secondary_buffer_size()|,
- // - the entries in the handle entry table (that the sizes and offsets are
- // valid).
- // Note: It does not check the serialized dispatcher data itself.
+ // Checks that the given |View| appears to be for a valid message, within
+ // predetermined limits (e.g., |num_bytes()| and |main_buffer_size()|, that
+ // |transport_data_buffer()|/|transport_data_buffer_size()| is for valid
+ // transport data -- see |TransportData::ValidateBuffer()|).
//
// It returns true (and leaves |error_message| alone) if this object appears
// to be a valid message (according to the above) and false, pointing
@@ -108,11 +96,11 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
size_t main_buffer_size() const {
return RoundUpMessageAlignment(sizeof(Header) + header()->num_bytes);
}
- const void* secondary_buffer() const {
+ const void* transport_data_buffer() const {
return (total_size() > main_buffer_size()) ?
static_cast<const char*>(buffer_) + main_buffer_size() : NULL;
}
- size_t secondary_buffer_size() const {
+ size_t transport_data_buffer_size() const {
return total_size() - main_buffer_size();
}
size_t total_size() const { return header()->total_size; }
@@ -120,7 +108,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
const void* bytes() const {
return static_cast<const char*>(buffer_) + sizeof(Header);
}
- uint32_t num_handles() const { return header()->num_handles; }
Type type() const { return header()->type; }
Subtype subtype() const { return header()->subtype; }
EndpointId source_id() const { return header()->source_id; }
@@ -141,7 +128,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
MessageInTransit(Type type,
Subtype subtype,
uint32_t num_bytes,
- uint32_t num_handles,
const void* bytes);
// Constructs a |MessageInTransit| from a |View|.
explicit MessageInTransit(const View& message_view);
@@ -172,19 +158,12 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
// stays alive through the call.
void SerializeAndCloseDispatchers(Channel* channel);
- // Deserializes any dispatchers from the secondary buffer. This message must
- // not have any dispatchers attached.
- // TODO(vtl): Having to copy the secondary buffer (in the constructor from a
- // |View|) is suboptimal. Maybe this should just be done in the constructor?
- void DeserializeDispatchers(Channel* channel);
-
// Gets the main buffer and its size (in number of bytes), respectively.
const void* main_buffer() const { return main_buffer_.get(); }
size_t main_buffer_size() const { return main_buffer_size_; }
- // Gets the secondary buffer and its size (in number of bytes), respectively.
- const void* secondary_buffer() const { return secondary_buffer_.get(); }
- size_t secondary_buffer_size() const { return secondary_buffer_size_; }
+ // Gets the transport data buffer (if any).
+ const TransportData* transport_data() const { return transport_data_.get(); }
// Gets the total size of the message (see comment in |Header|, below).
size_t total_size() const { return header()->total_size; }
@@ -196,8 +175,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
const void* bytes() const { return main_buffer_.get() + sizeof(Header); }
void* bytes() { return main_buffer_.get() + sizeof(Header); }
- uint32_t num_handles() const { return header()->num_handles; }
-
Type type() const { return header()->type; }
Subtype subtype() const { return header()->subtype; }
EndpointId source_id() const { return header()->source_id; }
@@ -220,25 +197,13 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
return dispatchers_ && !dispatchers_->empty();
}
- // Gets the platform-specific handles attached to this message; this may
- // return null if there are none. Note that the caller may mutate the set of
- // platform-specific handles.
- std::vector<embedder::PlatformHandle>* platform_handles() {
- return platform_handles_.get();
- }
-
- // Returns true if this message has platform-specific handles attached.
- bool has_platform_handles() const {
- return platform_handles_ && !platform_handles_->empty();
- }
-
// Rounds |n| up to a multiple of |kMessageAlignment|.
static inline size_t RoundUpMessageAlignment(size_t n) {
return (n + kMessageAlignment - 1) & ~(kMessageAlignment - 1);
}
private:
- // To allow us to make assertions about |Header| in the .cc file.
+ // To allow us to make compile-assertions about |Header| in the .cc file.
struct PrivateStructForCompileAsserts;
// Header for the data (main buffer). Must be a multiple of
@@ -256,29 +221,9 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
EndpointId destination_id; // 4 bytes.
// Size of actual message data.
uint32_t num_bytes;
- // Number of handles "attached".
- uint32_t num_handles;
- };
-
- struct HandleTableEntry {
- int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid").
- uint32_t offset; // Relative to the start of the secondary buffer.
- uint32_t size; // (Not including any padding.)
uint32_t unused;
};
- // The maximum possible size of a valid secondary buffer.
- static const size_t kMaxSecondaryBufferSize;
-
- // The maximum total number of platform handles that may be attached.
- static const size_t kMaxPlatformHandles;
-
- // Validates the secondary buffer. Returns null on success, or a
- // human-readable error message (meant for logging/debugging) on error.
- static const char* ValidateSecondaryBuffer(size_t num_handles,
- const void* secondary_buffer,
- size_t secondary_buffer_size);
-
const Header* header() const {
return reinterpret_cast<const Header*>(main_buffer_.get());
}
@@ -289,8 +234,7 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
const size_t main_buffer_size_;
const scoped_ptr<char, base::AlignedFreeDeleter> main_buffer_; // Never null.
- size_t secondary_buffer_size_;
- scoped_ptr<char, base::AlignedFreeDeleter> secondary_buffer_; // May be null.
+ scoped_ptr<TransportData> transport_data_; // May be null.
// Any dispatchers that may be attached to this message. These dispatchers
// should be "owned" by this message, i.e., have a ref count of exactly 1. (We
@@ -298,12 +242,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
// some reason.)
scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers_;
- // Any platform-specific handles attached to this message (for inter-process
- // transport). The vector (if any) owns the handles that it contains (and is
- // responsible for closing them).
- // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|.
- scoped_ptr<std::vector<embedder::PlatformHandle> > platform_handles_;
-
DISALLOW_COPY_AND_ASSIGN(MessageInTransit);
};
diff --git a/mojo/system/message_pipe.cc b/mojo/system/message_pipe.cc
index e154064..e25ac1c 100644
--- a/mojo/system/message_pipe.cc
+++ b/mojo/system/message_pipe.cc
@@ -72,14 +72,13 @@ MojoResult MessagePipe::WriteMessage(
std::vector<DispatcherTransport>* transports,
MojoWriteMessageFlags flags) {
DCHECK(port == 0 || port == 1);
- uint32_t num_handles =
- transports ? static_cast<uint32_t>(transports->size()) : 0;
return EnqueueMessageInternal(
GetPeerPort(port),
make_scoped_ptr(new MessageInTransit(
MessageInTransit::kTypeMessagePipeEndpoint,
MessageInTransit::kSubtypeMessagePipeEndpointData,
- num_bytes, num_handles, bytes)),
+ num_bytes,
+ bytes)),
transports);
}
@@ -226,9 +225,6 @@ MojoResult MessagePipe::EnqueueMessageInternal(
return result;
}
- if (message->has_dispatchers())
- DCHECK_EQ(message->dispatchers()->size(), message->num_handles());
-
// The endpoint's |EnqueueMessage()| may not report failure.
endpoints_[port]->EnqueueMessage(message.Pass());
return MOJO_RESULT_OK;
@@ -239,7 +235,6 @@ MojoResult MessagePipe::AttachTransportsNoLock(
MessageInTransit* message,
std::vector<DispatcherTransport>* transports) {
DCHECK(!message->has_dispatchers());
- DCHECK_EQ(transports->size(), message->num_handles());
// You're not allowed to send either handle to a message pipe over the message
// pipe, so check for this. (The case of trying to write a handle to itself is
diff --git a/mojo/system/raw_channel.cc b/mojo/system/raw_channel.cc
index 92b9a3d..9780baf 100644
--- a/mojo/system/raw_channel.cc
+++ b/mojo/system/raw_channel.cc
@@ -14,6 +14,7 @@
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "mojo/system/message_in_transit.h"
+#include "mojo/system/transport_data.h"
namespace mojo {
namespace system {
@@ -45,7 +46,10 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const {
return;
MessageInTransit* message = message_queue_.front();
- if (!message->secondary_buffer_size()) {
+ size_t transport_data_buffer_size = message->transport_data() ?
+ message->transport_data()->buffer_size() : 0;
+
+ if (!transport_data_buffer_size) {
// Only write from the main buffer.
DCHECK_LT(offset_, message->main_buffer_size());
DCHECK_LE(bytes_to_write, message->main_buffer_size());
@@ -57,12 +61,12 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const {
}
if (offset_ >= message->main_buffer_size()) {
- // Only write from the secondary buffer.
+ // Only write from the transport data buffer.
DCHECK_LT(offset_ - message->main_buffer_size(),
- message->secondary_buffer_size());
- DCHECK_LE(bytes_to_write, message->secondary_buffer_size());
+ transport_data_buffer_size);
+ DCHECK_LE(bytes_to_write, transport_data_buffer_size);
Buffer buffer = {
- static_cast<const char*>(message->secondary_buffer()) +
+ static_cast<const char*>(message->transport_data()->buffer()) +
(offset_ - message->main_buffer_size()),
bytes_to_write};
buffers->push_back(buffer);
@@ -71,14 +75,16 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const {
// Write from both buffers.
DCHECK_EQ(bytes_to_write, message->main_buffer_size() - offset_ +
- message->secondary_buffer_size());
+ transport_data_buffer_size);
Buffer buffer1 = {
- static_cast<const char*>(message->main_buffer()) + offset_,
- message->main_buffer_size() - offset_};
+ static_cast<const char*>(message->main_buffer()) + offset_,
+ message->main_buffer_size() - offset_
+ };
buffers->push_back(buffer1);
Buffer buffer2 = {
- static_cast<const char*>(message->secondary_buffer()),
- message->secondary_buffer_size()};
+ static_cast<const char*>(message->transport_data()->buffer()),
+ transport_data_buffer_size
+ };
buffers->push_back(buffer2);
}
@@ -159,7 +165,8 @@ bool RawChannel::WriteMessage(scoped_ptr<MessageInTransit> message) {
DCHECK(message);
// TODO(vtl)
- if (message->has_platform_handles()) {
+ if (message->transport_data() &&
+ message->transport_data()->has_platform_handles()) {
NOTIMPLEMENTED();
return false;
}
diff --git a/mojo/system/raw_channel_unittest.cc b/mojo/system/raw_channel_unittest.cc
index 22c52e7..a5b8cb4 100644
--- a/mojo/system/raw_channel_unittest.cc
+++ b/mojo/system/raw_channel_unittest.cc
@@ -40,7 +40,7 @@ scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) {
return make_scoped_ptr(
new MessageInTransit(MessageInTransit::kTypeMessagePipeEndpoint,
MessageInTransit::kSubtypeMessagePipeEndpointData,
- num_bytes, 0, bytes.empty() ? NULL : &bytes[0]));
+ num_bytes, bytes.empty() ? NULL : &bytes[0]));
}
bool CheckMessageData(const void* bytes, uint32_t num_bytes) {
diff --git a/mojo/system/transport_data.cc b/mojo/system/transport_data.cc
new file mode 100644
index 0000000..b3c4845
--- /dev/null
+++ b/mojo/system/transport_data.cc
@@ -0,0 +1,255 @@
+// 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/transport_data.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/message_in_transit.h"
+
+namespace mojo {
+namespace system {
+
+STATIC_CONST_MEMBER_DEFINITION const size_t
+ TransportData::kMaxSerializedDispatcherSize;
+STATIC_CONST_MEMBER_DEFINITION const size_t
+ TransportData::kMaxSerializedDispatcherPlatformHandles;
+
+// In additional to the header, for each attached (Mojo) handle there'll be a
+// handle table entry and serialized dispatcher data.
+// static
+const size_t TransportData::kMaxBufferSize =
+ sizeof(Header) + kMaxMessageNumHandles * (sizeof(HandleTableEntry) +
+ kMaxSerializedDispatcherSize);
+
+// static
+const size_t TransportData::kMaxPlatformHandles =
+ kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles;
+
+struct TransportData::PrivateStructForCompileAsserts {
+ // The size of |Header| must be a multiple of the alignment.
+ COMPILE_ASSERT(sizeof(Header) % MessageInTransit::kMessageAlignment == 0,
+ sizeof_MessageInTransit_Header_invalid);
+
+ // The maximum serialized dispatcher size must be a multiple of the alignment.
+ COMPILE_ASSERT(kMaxSerializedDispatcherSize %
+ MessageInTransit::kMessageAlignment == 0,
+ kMaxSerializedDispatcherSize_not_a_multiple_of_alignment);
+
+ // The size of |HandleTableEntry| must be a multiple of the alignment.
+ COMPILE_ASSERT(sizeof(HandleTableEntry) %
+ MessageInTransit::kMessageAlignment == 0,
+ sizeof_MessageInTransit_HandleTableEntry_invalid);
+};
+
+TransportData::TransportData(
+ scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers,
+ Channel* channel)
+ : buffer_size_(0) {
+ DCHECK(dispatchers);
+ DCHECK(channel);
+
+ const size_t num_handles = dispatchers->size();
+ DCHECK_GT(num_handles, 0u);
+
+ // The offset to the start of the (Mojo) handle table.
+ const size_t handle_table_start_offset = sizeof(Header);
+ // The offset to the start of the serialized dispatcher data.
+ const size_t serialized_dispatcher_start_offset =
+ handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
+ // The estimated size of the secondary buffer. We compute this estimate below.
+ // It must be at least as big as the (eventual) actual size.
+ size_t estimated_size = serialized_dispatcher_start_offset;
+ size_t num_platform_handles = 0;
+#if DCHECK_IS_ON
+ std::vector<size_t> all_max_sizes(num_handles);
+ std::vector<size_t> all_max_platform_handles(num_handles);
+#endif
+ for (size_t i = 0; i < num_handles; i++) {
+ if (Dispatcher* dispatcher = (*dispatchers)[i].get()) {
+ size_t max_size = 0;
+ size_t max_platform_handles = 0;
+ Dispatcher::TransportDataAccess::StartSerialize(
+ dispatcher, channel, &max_size, &max_platform_handles);
+
+ DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
+ estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size);
+ DCHECK_LE(estimated_size, kMaxBufferSize);
+
+ DCHECK_LE(max_platform_handles,
+ kMaxSerializedDispatcherPlatformHandles);
+ num_platform_handles += max_platform_handles;
+ DCHECK_LE(num_platform_handles, kMaxPlatformHandles);
+
+#if DCHECK_IS_ON
+ all_max_sizes[i] = max_size;
+ all_max_platform_handles[i] = max_platform_handles;
+#endif
+ }
+ }
+
+ buffer_.reset(static_cast<char*>(
+ base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment)));
+ // 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(buffer_.get(), 0, estimated_size);
+
+ if (num_platform_handles > 0) {
+ DCHECK(!platform_handles_);
+ platform_handles_.reset(new std::vector<embedder::PlatformHandle>());
+ }
+
+ Header* header = reinterpret_cast<Header*>(buffer_.get());
+ header->num_handles = static_cast<uint32_t>(num_handles);
+ // TODO(vtl): platform_handle_table_offset and num_platform_handles
+
+ HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
+ buffer_.get() + handle_table_start_offset);
+ size_t current_offset = serialized_dispatcher_start_offset;
+ for (size_t i = 0; i < num_handles; i++) {
+ Dispatcher* dispatcher = (*dispatchers)[i].get();
+ if (!dispatcher) {
+ COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0,
+ value_of_Dispatcher_kTypeUnknown_must_be_zero);
+ continue;
+ }
+
+#if DCHECK_IS_ON
+ size_t old_platform_handles_size =
+ platform_handles_ ? platform_handles_->size() : 0;
+#endif
+
+ void* destination = buffer_.get() + current_offset;
+ size_t actual_size = 0;
+ if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
+ dispatcher, channel, destination, &actual_size,
+ platform_handles_.get())) {
+ handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
+ handle_table[i].offset = static_cast<uint32_t>(current_offset);
+ handle_table[i].size = static_cast<uint32_t>(actual_size);
+
+#if DCHECK_IS_ON
+ DCHECK_LE(actual_size, all_max_sizes[i]);
+ DCHECK_LE(platform_handles_ ? (platform_handles_->size() -
+ old_platform_handles_size) : 0,
+ all_max_platform_handles[i]);
+#endif
+ } else {
+ // Nothing to do on failure, since |buffer_| was cleared, and
+ // |kTypeUnknown| is zero. The handle was simply closed.
+ LOG(ERROR) << "Failed to serialize handle to remote message pipe";
+ }
+
+ current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size);
+ DCHECK_LE(current_offset, estimated_size);
+ DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0,
+ num_platform_handles);
+ }
+
+ // There's no aligned realloc, so it's no good way to release unused space (if
+ // we overshot our estimated space requirements).
+ buffer_size_ = current_offset;
+
+ // |dispatchers_| will be destroyed as it goes out of scope.
+}
+
+TransportData::~TransportData() {
+ if (platform_handles_) {
+ for (size_t i = 0; i < platform_handles_->size(); i++)
+ (*platform_handles_)[i].CloseIfNecessary();
+ }
+}
+
+// static
+const char* TransportData::ValidateBuffer(const void* buffer,
+ size_t buffer_size) {
+ DCHECK(buffer);
+ DCHECK_GT(buffer_size, 0u);
+
+ // Always make sure that the buffer size is sane; if it's not, someone's
+ // messing with us.
+ if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize ||
+ buffer_size % MessageInTransit::kMessageAlignment != 0)
+ return "Invalid message secondary buffer size";
+
+ const Header* header = static_cast<const Header*>(buffer);
+ const size_t num_handles = header->num_handles;
+ if (num_handles == 0)
+ return "Message has no handles attached, but secondary buffer present";
+
+ // Sanity-check |num_handles| (before multiplying it against anything).
+ if (num_handles > kMaxMessageNumHandles)
+ return "Message handle payload too large";
+
+ if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry))
+ return "Message secondary buffer too small";
+
+ // TODO(vtl): Check |platform_handle_table_offset| and |num_platform_handles|
+ // once they're used.
+ if (header->platform_handle_table_offset != 0 ||
+ header->num_platform_handles != 0)
+ return "Bad message secondary buffer header values";
+
+ const HandleTableEntry* handle_table =
+ reinterpret_cast<const HandleTableEntry*>(
+ static_cast<const char*>(buffer) + sizeof(Header));
+ static const char kInvalidSerializedDispatcher[] =
+ "Message contains invalid serialized dispatcher";
+ for (size_t i = 0; i < num_handles; i++) {
+ size_t offset = handle_table[i].offset;
+ if (offset % MessageInTransit::kMessageAlignment != 0)
+ return kInvalidSerializedDispatcher;
+
+ size_t size = handle_table[i].size;
+ if (size > kMaxSerializedDispatcherSize || size > buffer_size)
+ return kInvalidSerializedDispatcher;
+
+ // Note: This is an overflow-safe check for |offset + size > buffer_size|
+ // (we know that |size <= buffer_size| from the previous check).
+ if (offset > buffer_size - size)
+ return kInvalidSerializedDispatcher;
+ }
+
+ return NULL;
+}
+
+// static
+scoped_ptr<std::vector<scoped_refptr<Dispatcher> > >
+ TransportData::DeserializeDispatchersFromBuffer(const void* buffer,
+ size_t buffer_size,
+ Channel* channel) {
+ DCHECK(buffer);
+ DCHECK_GT(buffer_size, 0u);
+ DCHECK(channel);
+
+ const Header* header = static_cast<const Header*>(buffer);
+ const size_t num_handles = header->num_handles;
+ scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers(
+ new std::vector<scoped_refptr<Dispatcher> >(num_handles));
+
+ const HandleTableEntry* handle_table =
+ reinterpret_cast<const HandleTableEntry*>(
+ static_cast<const char*>(buffer) + sizeof(Header));
+ for (size_t i = 0; i < num_handles; i++) {
+ size_t offset = handle_table[i].offset;
+ size_t size = handle_table[i].size;
+ // Should already have been checked by |ValidateBuffer()|:
+ DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u);
+ DCHECK_LE(offset, buffer_size);
+ DCHECK_LE(offset + size, buffer_size);
+
+ const void* source = static_cast<const char*>(buffer) + offset;
+ (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize(
+ channel, handle_table[i].type, source, size);
+ }
+
+ return dispatchers.Pass();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/mojo/system/transport_data.h b/mojo/system/transport_data.h
new file mode 100644
index 0000000..d4d9528
--- /dev/null
+++ b/mojo/system/transport_data.h
@@ -0,0 +1,163 @@
+// 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_TRANSPORT_DATA_H_
+#define MOJO_SYSTEM_TRANSPORT_DATA_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// This class is used by |MessageInTransit| to represent handles (|Dispatcher|s)
+// in various stages of serialization.
+//
+// The stages are:
+// - Before reaching |TransportData|: Turn |DispatcherTransport|s into
+// |Dispatcher|s that are "owned" by (and attached to) a |MessageInTransit|.
+// This invalidates the handles in the space of the sending application
+// (and, e.g., if another thread is waiting on such a handle, it'll be
+// notified of this invalidation).
+// - Serialize these dispatchers into the |TransportData|: First, for each
+// attached dispatcher, there's an entry in the |TransportData|'s "handle
+// table", which points to a segment of (dispatcher-type-dependent) data.
+// - During the serialization of the dispatchers, |PlatformHandle|s may be
+// detached from the dispatchers and attached to the |TransportData|.
+// - Before sending the |MessageInTransit|, including its main buffer and the
+// |TransportData|'s buffer, the |Channel| sends any |PlatformHandle|s (in a
+// platform-, and possibly sandobx-situation-, specific way) first. In doing
+// so, it appends a "platform handle table" to the |TransportData|
+// containing information about how to deserialize these |PlatformHandle|s.
+// - Finally, at this point, to send the |MessageInTransit|, there only
+// remains "inert" data: the |MessageInTransit|'s main buffer and data from
+// the |TransportData|, consisting of the "handle table" (one entry for each
+// attached dispatcher), dispatcher-type-specific data (one segment for each
+// entry in the "handle table"), and the "platform handle table" (one entry
+// for each attached |PlatformHandle|).
+//
+// To receive a message (|MessageInTransit|), the "reverse" happens:
+// - On POSIX, receive and buffer |PlatformHandle|s (i.e., FDs), which were
+// sent before the "inert" data.
+// - Receive the "inert" data from the |MessageInTransit|. Examine its
+// "platform handle table". On POSIX, match its entries with the buffered
+// |PlatformHandle|s, which were previously received. On Windows, do what's
+// necessary to obtain |PlatformHandle|s (e.g.: i. if the sender is fully
+// trusted and able to duplicate handle into the receiver, then just pick
+// out the |HANDLE| value; ii. if the receiver is fully trusted and able to
+// duplicate handles from the receiver, do the |DuplicateHandle()|; iii.
+// otherwise, talk to a broker to get handles). Reattach all the
+// |PlatformHandle|s to the |MessageInTransit|.
+// - For each entry in the "handle table", use serialized dispatcher data to
+// reconstitute a dispatcher, taking ownership of associated
+// |PlatformHandle|s (and detaching them). Attach these dispatchers to the
+// |MessageInTransit|.
+// - At this point, the |MessageInTransit| consists of its main buffer
+// (primarily the data payload) and the attached dispatchers; the
+// |TransportData| can be discarded.
+// - When |MojoReadMessage()| is to give data to the application, attach the
+// dispatchers to the (global, "core") handle table, getting handles; give
+// the application the data payload and these handles.
+//
+// TODO(vtl): Everything above involving |PlatformHandle|s.
+class MOJO_SYSTEM_IMPL_EXPORT TransportData {
+ public:
+ // The maximum size of a single serialized dispatcher. This must be a multiple
+ // of |kMessageAlignment|.
+ static const size_t kMaxSerializedDispatcherSize = 10000;
+
+ // The maximum number of platform handles to attach for a single serialized
+ // dispatcher.
+ static const size_t kMaxSerializedDispatcherPlatformHandles = 2;
+
+ TransportData(
+ scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers,
+ Channel* channel);
+ ~TransportData();
+
+ const void* buffer() const { return buffer_.get(); }
+ size_t buffer_size() const { return buffer_size_; }
+
+ // Gets attached platform-specific handles; this may return null if there are
+ // none. Note that the caller may mutate the set of platform-specific handles.
+ std::vector<embedder::PlatformHandle>* platform_handles() {
+ return platform_handles_.get();
+ }
+
+ // Returns true if there are platform-specific handles attached.
+ bool has_platform_handles() const {
+ return platform_handles_ && !platform_handles_->empty();
+ }
+
+ // Receive-side functions:
+
+ // Checks if the given buffer (from the "wire") looks like a valid
+ // |TransportData| buffer. (Should only be called if |buffer_size| is
+ // nonzero.) Returns null if valid, and a pointer to a human-readable error
+ // message (for debug/logging purposes) on error. Note: This checks the
+ // validity of the handle table entries (i.e., does range checking), but does
+ // not check that the validity of the actual serialized dispatcher
+ // information.
+ static const char* ValidateBuffer(const void* buffer, size_t buffer_size);
+
+ // Deserializes dispatchers from the given (serialized) transport data buffer
+ // (typically from a |MessageInTransit::View|). |buffer| should be non-null
+ // and |buffer_size| should be nonzero.
+ static scoped_ptr<std::vector<scoped_refptr<Dispatcher> > >
+ DeserializeDispatchersFromBuffer(const void* buffer,
+ size_t buffer_size,
+ Channel* channel);
+
+ private:
+ // To allow us to make compile-assertions about |Header|, etc. in the .cc
+ // file.
+ struct PrivateStructForCompileAsserts;
+
+ // Header for the "secondary buffer"/"transport data". Must be a multiple of
+ // |MessageInTransit::kMessageAlignment| in size. Must be POD.
+ struct Header {
+ uint32_t num_handles;
+ // TODO(vtl): Not used yet:
+ uint32_t platform_handle_table_offset;
+ uint32_t num_platform_handles;
+ uint32_t unused;
+ };
+
+ struct HandleTableEntry {
+ int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid").
+ uint32_t offset; // Relative to the start of the "secondary buffer".
+ uint32_t size; // (Not including any padding.)
+ uint32_t unused;
+ };
+
+ // The maximum possible size of a valid transport data buffer.
+ static const size_t kMaxBufferSize;
+
+ // The maximum total number of platform handles that may be attached.
+ static const size_t kMaxPlatformHandles;
+
+ size_t buffer_size_;
+ scoped_ptr<char, base::AlignedFreeDeleter> buffer_; // Never null.
+
+ // Any platform-specific handles attached to this message (for inter-process
+ // transport). The vector (if any) owns the handles that it contains (and is
+ // responsible for closing them).
+ // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|.
+ scoped_ptr<std::vector<embedder::PlatformHandle> > platform_handles_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransportData);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_TRANSPORT_DATA_H_