Skip to content

Commit

Permalink
Factor out the shared parts of IPC channel reading.
Browse files Browse the repository at this point in the history
This adds a new class+file ChannelReader that is responsible for management
of the common parts of IPC channel reading. The existing platform-specific
ChannelImpl classes derive from this and supply platform-specific reading
features via virtual classes. This is to reduce code duplication between the
Windows and Posix implementations of Channel.


Review URL: http://codereview.chromium.org/9547009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125597 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
brettw@chromium.org committed Mar 8, 2012
1 parent aaa11b3 commit d805c6a
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 252 deletions.
4 changes: 3 additions & 1 deletion ipc/ipc.gypi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Copyright (c) 2012 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.

Expand All @@ -19,6 +19,8 @@
'ipc_channel_posix.h',
'ipc_channel_proxy.cc',
'ipc_channel_proxy.h',
'ipc_channel_reader.cc',
'ipc_channel_reader.h',
'ipc_channel_win.cc',
'ipc_channel_win.h',
'ipc_descriptors.h',
Expand Down
24 changes: 12 additions & 12 deletions ipc/ipc_channel.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 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.

Expand Down Expand Up @@ -94,6 +94,17 @@ class IPC_EXPORT Channel : public Message::Sender {
#endif
};

// The Hello message is internal to the Channel class. It is sent
// by the peer when the channel is connected. The message contains
// just the process id (pid). The message has a special routing_id
// (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE).
enum {
HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16),
// to avoid conflicting with normal
// message types, which are enumeration
// constants starting from 0.
};

// The maximum message size in bytes. Attempting to receive a message of this
// size or bigger results in a channel error.
static const size_t kMaximumMessageSize = 128 * 1024 * 1024;
Expand Down Expand Up @@ -197,17 +208,6 @@ class IPC_EXPORT Channel : public Message::Sender {
// PIMPL to which all channel calls are delegated.
class ChannelImpl;
ChannelImpl *channel_impl_;

// The Hello message is internal to the Channel class. It is sent
// by the peer when the channel is connected. The message contains
// just the process id (pid). The message has a special routing_id
// (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE).
enum {
HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16),
// to avoid conflicting with normal
// message types, which are enumeration
// constants starting from 0.
};
};

} // namespace IPC
Expand Down
95 changes: 15 additions & 80 deletions ipc/ipc_channel_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ int Channel::ChannelImpl::global_pid_ = 0;

Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle,
Mode mode, Listener* listener)
: mode_(mode),
: ChannelReader(listener),
mode_(mode),
is_blocked_on_write_(false),
waiting_connect_(true),
message_send_bytes_written_(0),
Expand All @@ -312,9 +313,7 @@ Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle,
remote_fd_pipe_(-1),
#endif // IPC_USES_READWRITE
pipe_name_(channel_handle.name),
listener_(listener),
must_unlink_(false) {
memset(input_buf_, 0, sizeof(input_buf_));
memset(input_cmsg_buf_, 0, sizeof(input_cmsg_buf_));
if (!CreatePipe(channel_handle)) {
// The pipe may have been closed already.
Expand Down Expand Up @@ -478,71 +477,6 @@ bool Channel::ChannelImpl::Connect() {
return did_connect;
}

bool Channel::ChannelImpl::ProcessIncomingMessages() {
while (true) {
int bytes_read = 0;
ReadState read_state = ReadData(input_buf_, Channel::kReadBufferSize,
&bytes_read);
if (read_state == READ_FAILED)
return false;
if (read_state == READ_PENDING)
return true;

DCHECK(bytes_read > 0);
if (!DispatchInputData(input_buf_, bytes_read))
return false;
}
}

bool Channel::ChannelImpl::DispatchInputData(const char* input_data,
int input_data_len) {
const char* p;
const char* end;

// Possibly combine with the overflow buffer to make a larger buffer.
if (input_overflow_buf_.empty()) {
p = input_data;
end = input_data + input_data_len;
} else {
if (input_overflow_buf_.size() >
kMaximumMessageSize - input_data_len) {
input_overflow_buf_.clear();
LOG(ERROR) << "IPC message is too big";
return false;
}
input_overflow_buf_.append(input_data, input_data_len);
p = input_overflow_buf_.data();
end = p + input_overflow_buf_.size();
}

// Dispatch all complete messages in the data buffer.
while (p < end) {
const char* message_tail = Message::FindNext(p, end);
if (message_tail) {
int len = static_cast<int>(message_tail - p);
Message m(p, len);
if (!WillDispatchInputMessage(&m))
return false;

if (IsHelloMessage(&m))
HandleHelloMessage(m);
else
listener_->OnMessageReceived(m);
p = message_tail;
} else {
// Last message is partial.
break;
}
}

// Save any partial data in the overflow buffer.
input_overflow_buf_.assign(p, end - p);

if (input_overflow_buf_.empty() && !DidEmptyInputBuffers())
return false;
return true;
}

bool Channel::ChannelImpl::ProcessOutgoingMessages() {
DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
// no connection?
Expand Down Expand Up @@ -604,7 +538,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
msg->header()->num_fds = static_cast<uint16>(num_fds);

#if defined(IPC_USES_READWRITE)
if (!IsHelloMessage(msg)) {
if (!IsHelloMessage(*msg)) {
// Only the Hello message sends the file descriptor with the message.
// Subsequently, we can send file descriptors on the dedicated
// fd_pipe_ which makes Seccomp sandbox operation more efficient.
Expand All @@ -624,7 +558,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
if (bytes_written == 1) {
fd_written = pipe_;
#if defined(IPC_USES_READWRITE)
if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(msg)) {
if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(*msg)) {
DCHECK_EQ(msg->file_descriptor_set()->size(), 1U);
}
if (!msgh.msg_controllen) {
Expand Down Expand Up @@ -816,7 +750,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
int new_pipe = 0;
if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) {
Close();
listener_->OnChannelListenError();
listener()->OnChannelListenError();
}

if (pipe_ != -1) {
Expand All @@ -826,7 +760,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
DPLOG(ERROR) << "shutdown " << pipe_name_;
if (HANDLE_EINTR(close(new_pipe)) < 0)
DPLOG(ERROR) << "close " << pipe_name_;
listener_->OnChannelDenied();
listener()->OnChannelDenied();
return;
}
pipe_ = new_pipe;
Expand Down Expand Up @@ -910,13 +844,13 @@ bool Channel::ChannelImpl::AcceptConnection() {
void Channel::ChannelImpl::ClosePipeOnError() {
if (HasAcceptedConnection()) {
ResetToAcceptingConnectionState();
listener_->OnChannelError();
listener()->OnChannelError();
} else {
Close();
if (AcceptsConnections()) {
listener_->OnChannelListenError();
listener()->OnChannelListenError();
} else {
listener_->OnChannelError();
listener()->OnChannelError();
}
}
}
Expand Down Expand Up @@ -953,10 +887,6 @@ void Channel::ChannelImpl::QueueHelloMessage() {
output_queue_.push(msg.release());
}

bool Channel::ChannelImpl::IsHelloMessage(const Message* m) const {
return m->routing_id() == MSG_ROUTING_NONE && m->type() == HELLO_MESSAGE_TYPE;
}

Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData(
char* buffer,
int buffer_len,
Expand Down Expand Up @@ -1035,6 +965,11 @@ bool Channel::ChannelImpl::ReadFileDescriptorsFromFDPipe() {
}
#endif

// On Posix, we need to fix up the file descriptors before the input message
// is dispatched.
//
// This will read from the input_fds_ (READWRITE mode only) and read more
// handles from the FD pipe if necessary.
bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) {
uint16 header_fds = msg->header()->num_fds;
if (!header_fds)
Expand Down Expand Up @@ -1145,7 +1080,7 @@ void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) {
CHECK(descriptor.auto_close);
}
#endif // IPC_USES_READWRITE
listener_->OnChannelConnected(pid);
listener()->OnChannelConnected(pid);
}

void Channel::ChannelImpl::Close() {
Expand Down
59 changes: 10 additions & 49 deletions ipc/ipc_channel_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "base/message_loop.h"
#include "ipc/file_descriptor_set_posix.h"
#include "ipc/ipc_channel_reader.h"

#if !defined(OS_MACOSX)
// On Linux, the seccomp sandbox makes it very expensive to call
Expand Down Expand Up @@ -47,15 +48,15 @@

namespace IPC {

class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
class Channel::ChannelImpl : public internal::ChannelReader,
public MessageLoopForIO::Watcher {
public:
// Mirror methods of Channel, see ipc_channel.h for description.
ChannelImpl(const IPC::ChannelHandle& channel_handle, Mode mode,
Listener* listener);
virtual ~ChannelImpl();
bool Connect();
void Close();
void set_listener(Listener* listener) { listener_ = listener; }
bool Send(Message* message);
int GetClientFileDescriptor();
int TakeClientFileDescriptor();
Expand All @@ -70,38 +71,22 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
#endif // OS_LINUX

private:
enum ReadState { READ_SUCCEEDED, READ_FAILED, READ_PENDING };

bool CreatePipe(const IPC::ChannelHandle& channel_handle);

bool ProcessIncomingMessages();
bool ProcessOutgoingMessages();

bool AcceptConnection();
void ClosePipeOnError();
int GetHelloMessageProcId();
void QueueHelloMessage();
bool IsHelloMessage(const Message* m) const;

// Populates the given buffer with data from the pipe.
//
// Returns the state of the read. On READ_SUCCESS, the number of bytes
// read will be placed into |*bytes_read| (which can be less than the
// buffer size). On READ_FAILED, the channel will be closed.
//
// If the return value is READ_PENDING, it means that there was no data
// ready for reading. The implementation is then responsible for either
// calling AsyncReadComplete with the number of bytes read into the
// buffer, or ProcessIncomingMessages to try the read again (depending
// on whether the platform's async I/O is "try again" or "write
// asynchronously into your buffer").
ReadState ReadData(char* buffer, int buffer_len, int* bytes_read);

// Takes the given data received from the IPC channel and dispatches any
// fully completed messages.
//
// Returns true on success. False means channel error.
bool DispatchInputData(const char* input_data, int input_data_len);
// ChannelReader implementation.
virtual ReadState ReadData(char* buffer,
int buffer_len,
int* bytes_read) OVERRIDE;
virtual bool WillDispatchInputMessage(Message* msg) OVERRIDE;
virtual bool DidEmptyInputBuffers() OVERRIDE;
virtual void HandleHelloMessage(const Message& msg) OVERRIDE;

#if defined(IPC_USES_READWRITE)
// Reads the next message from the fd_pipe_ and appends them to the
Expand All @@ -111,17 +96,6 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
bool ReadFileDescriptorsFromFDPipe();
#endif

// Loads the required file desciptors into the given message. Returns true
// on success. False means a fatal channel error.
//
// This will read from the input_fds_ and read more handles from the FD
// pipe if necessary.
bool WillDispatchInputMessage(Message* msg);

// Performs post-dispatch checks. Called when all input buffers are empty,
// though there could be more data ready to be read from the OS.
bool DidEmptyInputBuffers();

// Finds the set of file descriptors in the given message. On success,
// appends the descriptors to the input_fds_ member and returns true
//
Expand All @@ -133,9 +107,6 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// used to clean up handles in error conditions to avoid leaking the handles.
void ClearInputFDs();

// Handles the first message sent over the pipe which contains setup info.
void HandleHelloMessage(const Message& msg);

// MessageLoopForIO::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
Expand Down Expand Up @@ -178,19 +149,9 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// the pipe. On POSIX it's used as a key in a local map of file descriptors.
std::string pipe_name_;

Listener* listener_;

// Messages to be sent are queued here.
std::queue<Message*> output_queue_;

// We read from the pipe into this buffer. Managed by DispatchInputData, do
// not access directly outside that function.
char input_buf_[Channel::kReadBufferSize];

// Large messages that span multiple pipe buffers, get built-up using
// this buffer.
std::string input_overflow_buf_;

// We assume a worst case: kReadBufferSize bytes of messages, where each
// message has no payload and a full complement of descriptors.
static const size_t kMaxReadFDs =
Expand Down
Loading

0 comments on commit d805c6a

Please sign in to comment.