summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorbenwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-11 04:22:34 +0000
committerbenwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-11 04:22:34 +0000
commit787f5e460f6265f961f88b75391edeb6eeb8a720 (patch)
tree4bd626c508e2b76b83a2d7131c832f0cac70c52c /ipc
parent29dd580cd7a471d5760649a8546459f31382244b (diff)
downloadchromium_src-787f5e460f6265f961f88b75391edeb6eeb8a720.zip
chromium_src-787f5e460f6265f961f88b75391edeb6eeb8a720.tar.gz
chromium_src-787f5e460f6265f961f88b75391edeb6eeb8a720.tar.bz2
Revert 227999 "Alternative workaround for mac kernel bug."
> Alternative workaround for mac kernel bug. > > BUG=298276 > > Review URL: https://codereview.chromium.org/25325002 This change seemed to cause the Mac ASAN bot to timeout when running the ipc_tests. TBR=hubbe@chromium.org Review URL: https://codereview.chromium.org/26384003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228110 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ipc')
-rw-r--r--ipc/file_descriptor_set_posix.cc10
-rw-r--r--ipc/file_descriptor_set_posix.h3
-rw-r--r--ipc/ipc_channel.h23
-rw-r--r--ipc/ipc_channel_nacl.cc2
-rw-r--r--ipc/ipc_channel_nacl.h2
-rw-r--r--ipc/ipc_channel_posix.cc132
-rw-r--r--ipc/ipc_channel_posix.h12
-rw-r--r--ipc/ipc_channel_reader.cc12
-rw-r--r--ipc/ipc_channel_reader.h12
-rw-r--r--ipc/ipc_channel_win.cc3
-rw-r--r--ipc/ipc_channel_win.h2
-rw-r--r--ipc/ipc_send_fds_test.cc255
12 files changed, 75 insertions, 393 deletions
diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc
index fc15c2d..584efec 100644
--- a/ipc/file_descriptor_set_posix.cc
+++ b/ipc/file_descriptor_set_posix.cc
@@ -126,16 +126,6 @@ void FileDescriptorSet::CommitAll() {
consumed_descriptor_highwater_ = 0;
}
-void FileDescriptorSet::ReleaseFDsToClose(std::vector<int>* fds) {
- for (std::vector<base::FileDescriptor>::iterator
- i = descriptors_.begin(); i != descriptors_.end(); ++i) {
- if (i->auto_close)
- fds->push_back(i->fd);
- }
- descriptors_.clear();
- consumed_descriptor_highwater_ = 0;
-}
-
void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
DCHECK(count <= kMaxDescriptorsPerMessage);
DCHECK_EQ(descriptors_.size(), 0u);
diff --git a/ipc/file_descriptor_set_posix.h b/ipc/file_descriptor_set_posix.h
index b413b4a..f9c6033 100644
--- a/ipc/file_descriptor_set_posix.h
+++ b/ipc/file_descriptor_set_posix.h
@@ -77,9 +77,6 @@ class IPC_EXPORT FileDescriptorSet
// Returns true if any contained file descriptors appear to be handles to a
// directory.
bool ContainsDirectoryDescriptor() const;
- // Fetch all filedescriptors with the "auto close" property.
- // Used instead of CommitAll() when closing must be handled manually.
- void ReleaseFDsToClose(std::vector<int>* fds);
// ---------------------------------------------------------------------------
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
index 7e09a80..f65a62b 100644
--- a/ipc/ipc_channel.h
+++ b/ipc/ipc_channel.h
@@ -75,22 +75,15 @@ class IPC_EXPORT Channel : public Sender {
#endif
};
- // Messages internal to the IPC implementation are defined here.
- // Uses Maximum value of message type (uint16), to avoid conflicting
- // with normal message types, which are enumeration constants starting from 0.
+ // 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 {
- // The Hello message 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).
- HELLO_MESSAGE_TYPE = kuint16max,
- // The CLOSE_FD_MESSAGE_TYPE is used in the IPC class to
- // work around a bug in sendmsg() on Mac. When an FD is sent
- // over the socket, a CLOSE_FD_MESSAGE is sent with hops = 2.
- // The client will return the message with hops = 1, *after* it
- // has received the message that contains the FD. When we
- // receive it again on the sender side, we close the FD.
- CLOSE_FD_MESSAGE_TYPE = HELLO_MESSAGE_TYPE - 1
+ 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
diff --git a/ipc/ipc_channel_nacl.cc b/ipc/ipc_channel_nacl.cc
index 1ecd571..860815e 100644
--- a/ipc/ipc_channel_nacl.cc
+++ b/ipc/ipc_channel_nacl.cc
@@ -344,7 +344,7 @@ bool Channel::ChannelImpl::DidEmptyInputBuffers() {
return input_fds_.empty();
}
-void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) {
+void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) {
// The trusted side IPC::Channel should handle the "hello" handshake; we
// should not receive the "Hello" message.
NOTREACHED();
diff --git a/ipc/ipc_channel_nacl.h b/ipc/ipc_channel_nacl.h
index a21730e..7c8960f 100644
--- a/ipc/ipc_channel_nacl.h
+++ b/ipc/ipc_channel_nacl.h
@@ -60,7 +60,7 @@ class Channel::ChannelImpl : public internal::ChannelReader {
int* bytes_read) OVERRIDE;
virtual bool WillDispatchInputMessage(Message* msg) OVERRIDE;
virtual bool DidEmptyInputBuffers() OVERRIDE;
- virtual void HandleInternalMessage(const Message& msg) OVERRIDE;
+ virtual void HandleHelloMessage(const Message& msg) OVERRIDE;
Mode mode_;
bool waiting_connect_;
diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc
index 17b5695..98a7cd8 100644
--- a/ipc/ipc_channel_posix.cc
+++ b/ipc/ipc_channel_posix.cc
@@ -347,27 +347,6 @@ bool Channel::ChannelImpl::Connect() {
return did_connect;
}
-void Channel::ChannelImpl::CloseFileDescriptors(Message* msg) {
-#if defined(OS_MACOSX)
- // There is a bug on OSX which makes it dangerous to close
- // a file descriptor while it is in transit. So instead we
- // store the file descriptor in a set and send a message to
- // the recipient, which is queued AFTER the message that
- // sent the FD. The recipient will reply to the message,
- // letting us know that it is now safe to close the file
- // descriptor. For more information, see:
- // http://crbug.com/298276
- std::vector<int> to_close;
- msg->file_descriptor_set()->ReleaseFDsToClose(&to_close);
- for (size_t i = 0; i < to_close.size(); i++) {
- fds_to_close_.insert(to_close[i]);
- QueueCloseFDMessage(to_close[i], 2);
- }
-#else
- msg->file_descriptor_set()->CommitAll();
-#endif
-}
-
bool Channel::ChannelImpl::ProcessOutgoingMessages() {
DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
// no connection?
@@ -440,7 +419,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
msgh.msg_iov = &iov;
msgh.msg_controllen = 0;
if (bytes_written > 0) {
- CloseFileDescriptors(msg);
+ msg->file_descriptor_set()->CommitAll();
}
}
#endif // IPC_USES_READWRITE
@@ -461,7 +440,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
}
}
if (bytes_written > 0)
- CloseFileDescriptors(msg);
+ msg->file_descriptor_set()->CommitAll();
if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) {
#if defined(OS_MACOSX)
@@ -596,17 +575,6 @@ void Channel::ChannelImpl::ResetToAcceptingConnectionState() {
// Close any outstanding, received file descriptors.
ClearInputFDs();
-
-#if defined(OS_MACOSX)
- // Clear any outstanding, sent file descriptors.
- for (std::set<int>::iterator i = fds_to_close_.begin();
- i != fds_to_close_.end();
- ++i) {
- if (HANDLE_EINTR(close(*i)) < 0)
- PLOG(ERROR) << "close";
- }
- fds_to_close_.clear();
-#endif
}
// static
@@ -624,6 +592,7 @@ void Channel::ChannelImpl::SetGlobalPid(int pid) {
// Called by libevent when we can read from the pipe without blocking.
void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ bool send_server_hello_msg = false;
if (fd == server_listen_pipe_) {
int new_pipe = 0;
if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) ||
@@ -662,16 +631,18 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
if (!AcceptConnection()) {
NOTREACHED() << "AcceptConnection should not fail on server";
}
+ send_server_hello_msg = true;
waiting_connect_ = false;
} else if (fd == pipe_) {
if (waiting_connect_ && (mode_ & MODE_SERVER_FLAG)) {
+ send_server_hello_msg = true;
waiting_connect_ = false;
}
if (!ProcessIncomingMessages()) {
// ClosePipeOnError may delete this object, so we mustn't call
// ProcessOutgoingMessages.
+ send_server_hello_msg = false;
ClosePipeOnError();
- return;
}
} else {
NOTREACHED() << "Unknown pipe " << fd;
@@ -680,8 +651,8 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
// If we're a server and handshaking, then we want to make sure that we
// only send our handshake message after we've processed the client's.
// This gives us a chance to kill the client if the incoming handshake
- // is invalid. This also flushes any closefd messagse.
- if (!is_blocked_on_write_) {
+ // is invalid.
+ if (send_server_hello_msg) {
ProcessOutgoingMessages();
}
}
@@ -931,80 +902,29 @@ void Channel::ChannelImpl::ClearInputFDs() {
input_fds_.clear();
}
-void Channel::ChannelImpl::QueueCloseFDMessage(int fd, int hops) {
- switch (hops) {
- case 1:
- case 2: {
- // Create the message
- scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
- CLOSE_FD_MESSAGE_TYPE,
- IPC::Message::PRIORITY_NORMAL));
- if (!msg->WriteInt(hops - 1) || !msg->WriteInt(fd)) {
- NOTREACHED() << "Unable to pickle close fd.";
- }
- // Send(msg.release());
- output_queue_.push(msg.release());
- break;
- }
-
- default:
- NOTREACHED();
- break;
- }
-}
-
-void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) {
+void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) {
// The Hello message contains only the process id.
PickleIterator iter(msg);
-
- switch (msg.type()) {
- default:
- NOTREACHED();
- break;
-
- case Channel::HELLO_MESSAGE_TYPE:
- int pid;
- if (!msg.ReadInt(&iter, &pid))
- NOTREACHED();
+ int pid;
+ if (!msg.ReadInt(&iter, &pid))
+ NOTREACHED();
#if defined(IPC_USES_READWRITE)
- if (mode_ & MODE_SERVER_FLAG) {
- // With IPC_USES_READWRITE, the Hello message from the client to the
- // server also contains the fd_pipe_, which will be used for all
- // subsequent file descriptor passing.
- DCHECK_EQ(msg.file_descriptor_set()->size(), 1U);
- base::FileDescriptor descriptor;
- if (!msg.ReadFileDescriptor(&iter, &descriptor)) {
- NOTREACHED();
- }
- fd_pipe_ = descriptor.fd;
- CHECK(descriptor.auto_close);
- }
-#endif // IPC_USES_READWRITE
- peer_pid_ = pid;
- listener()->OnChannelConnected(pid);
- break;
-
-#if defined(OS_MACOSX)
- case Channel::CLOSE_FD_MESSAGE_TYPE:
- int fd, hops;
- if (!msg.ReadInt(&iter, &hops))
- NOTREACHED();
- if (!msg.ReadInt(&iter, &fd))
- NOTREACHED();
- if (hops == 0) {
- if (fds_to_close_.erase(fd) > 0) {
- if (HANDLE_EINTR(close(fd)) < 0)
- PLOG(ERROR) << "close";
- } else {
- NOTREACHED();
- }
- } else {
- QueueCloseFDMessage(fd, hops);
- }
- break;
-#endif
+ if (mode_ & MODE_SERVER_FLAG) {
+ // With IPC_USES_READWRITE, the Hello message from the client to the
+ // server also contains the fd_pipe_, which will be used for all
+ // subsequent file descriptor passing.
+ DCHECK_EQ(msg.file_descriptor_set()->size(), 1U);
+ base::FileDescriptor descriptor;
+ if (!msg.ReadFileDescriptor(&iter, &descriptor)) {
+ NOTREACHED();
+ }
+ fd_pipe_ = descriptor.fd;
+ CHECK(descriptor.auto_close);
}
+#endif // IPC_USES_READWRITE
+ peer_pid_ = pid;
+ listener()->OnChannelConnected(pid);
}
void Channel::ChannelImpl::Close() {
diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h
index 1e587c1..645a130 100644
--- a/ipc/ipc_channel_posix.h
+++ b/ipc/ipc_channel_posix.h
@@ -10,7 +10,6 @@
#include <sys/socket.h> // for CMSG macros
#include <queue>
-#include <set>
#include <string>
#include <vector>
@@ -81,8 +80,6 @@ class Channel::ChannelImpl : public internal::ChannelReader,
void ClosePipeOnError();
int GetHelloMessageProcId();
void QueueHelloMessage();
- void CloseFileDescriptors(Message* msg);
- void QueueCloseFDMessage(int fd, int hops);
// ChannelReader implementation.
virtual ReadState ReadData(char* buffer,
@@ -90,7 +87,7 @@ class Channel::ChannelImpl : public internal::ChannelReader,
int* bytes_read) OVERRIDE;
virtual bool WillDispatchInputMessage(Message* msg) OVERRIDE;
virtual bool DidEmptyInputBuffers() OVERRIDE;
- virtual void HandleInternalMessage(const Message& msg) 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
@@ -187,13 +184,6 @@ class Channel::ChannelImpl : public internal::ChannelReader,
// implementation!
std::vector<int> input_fds_;
-#if defined(OS_MACOSX)
- // On OSX, sent FDs must not be closed until we get an ack.
- // Keep track of sent FDs here to make sure the remote is not
- // trying to bamboozle us.
- std::set<int> fds_to_close_;
-#endif
-
// True if we are responsible for unlinking the unix domain socket file.
bool must_unlink_;
diff --git a/ipc/ipc_channel_reader.cc b/ipc/ipc_channel_reader.cc
index 2ee7449..9055deb 100644
--- a/ipc/ipc_channel_reader.cc
+++ b/ipc/ipc_channel_reader.cc
@@ -38,15 +38,9 @@ bool ChannelReader::AsyncReadComplete(int bytes_read) {
return DispatchInputData(input_buf_, bytes_read);
}
-bool ChannelReader::IsInternalMessage(const Message& m) const {
- return m.routing_id() == MSG_ROUTING_NONE &&
- m.type() >= Channel::CLOSE_FD_MESSAGE_TYPE &&
- m.type() <= Channel::HELLO_MESSAGE_TYPE;
-}
-
bool ChannelReader::IsHelloMessage(const Message& m) const {
return m.routing_id() == MSG_ROUTING_NONE &&
- m.type() == Channel::HELLO_MESSAGE_TYPE;
+ m.type() == Channel::HELLO_MESSAGE_TYPE;
}
bool ChannelReader::DispatchInputData(const char* input_data,
@@ -90,8 +84,8 @@ bool ChannelReader::DispatchInputData(const char* input_data,
"line", IPC_MESSAGE_ID_LINE(m.type()));
#endif
m.TraceMessageEnd();
- if (IsInternalMessage(m))
- HandleInternalMessage(m);
+ if (IsHelloMessage(m))
+ HandleHelloMessage(m);
else
listener_->OnMessageReceived(m);
p = message_tail;
diff --git a/ipc/ipc_channel_reader.h b/ipc/ipc_channel_reader.h
index 1303846..9c398bd 100644
--- a/ipc/ipc_channel_reader.h
+++ b/ipc/ipc_channel_reader.h
@@ -42,12 +42,8 @@ class ChannelReader {
// data. See ReadData for more.
bool AsyncReadComplete(int bytes_read);
- // Returns true if the given message is internal to the IPC implementation,
- // like the "hello" message sent on channel set-up.
- bool IsInternalMessage(const Message& m) const;
-
- // Returns true if the given message is an Hello message
- // sent on channel set-up.
+ // Returns true if the given message is the "hello" message sent on channel
+ // set-up.
bool IsHelloMessage(const Message& m) const;
protected:
@@ -80,8 +76,8 @@ class ChannelReader {
// though there could be more data ready to be read from the OS.
virtual bool DidEmptyInputBuffers() = 0;
- // Handles internal messages, like the hello message sent on channel startup.
- virtual void HandleInternalMessage(const Message& msg) = 0;
+ // Handles the first message sent over the pipe which contains setup info.
+ virtual void HandleHelloMessage(const Message& msg) = 0;
private:
// Takes the given data received from the IPC channel and dispatches any
diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc
index 8c08500..6d7cfe8 100644
--- a/ipc/ipc_channel_win.cc
+++ b/ipc/ipc_channel_win.cc
@@ -152,8 +152,7 @@ bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) {
return true;
}
-void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) {
- DCHECK_EQ(msg.type(), static_cast<unsigned>(Channel::HELLO_MESSAGE_TYPE));
+void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) {
// The hello message contains one parameter containing the PID.
PickleIterator it(msg);
int32 claimed_pid;
diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h
index a544f8b..711f57f 100644
--- a/ipc/ipc_channel_win.h
+++ b/ipc/ipc_channel_win.h
@@ -41,7 +41,7 @@ class Channel::ChannelImpl : public internal::ChannelReader,
int* bytes_read) OVERRIDE;
virtual bool WillDispatchInputMessage(Message* msg) OVERRIDE;
bool DidEmptyInputBuffers() OVERRIDE;
- virtual void HandleInternalMessage(const Message& msg) OVERRIDE;
+ virtual void HandleHelloMessage(const Message& msg) OVERRIDE;
static const string16 PipeName(const std::string& channel_id,
int32* secret);
diff --git a/ipc/ipc_send_fds_test.cc b/ipc/ipc_send_fds_test.cc
index 20c3ed5..4cddc1c 100644
--- a/ipc/ipc_send_fds_test.cc
+++ b/ipc/ipc_send_fds_test.cc
@@ -11,18 +11,13 @@ extern "C" {
}
#endif
#include <fcntl.h>
-#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
-#include <queue>
-
-#include "base/callback.h"
#include "base/file_descriptor_posix.h"
#include "base/message_loop/message_loop.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/synchronization/waitable_event.h"
#include "ipc/ipc_message_utils.h"
#include "ipc/ipc_test_base.h"
@@ -31,59 +26,50 @@ namespace {
const unsigned kNumFDsToSend = 20;
const char* kDevZeroPath = "/dev/zero";
-class MyChannelDescriptorListenerBase : public IPC::Listener {
+static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) {
+ // Check that we can read from the FD.
+ char buf;
+ ssize_t amt_read = read(fd, &buf, 1);
+ ASSERT_EQ(amt_read, 1);
+ ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
+
+ struct stat st;
+ ASSERT_EQ(fstat(fd, &st), 0);
+
+ ASSERT_EQ(close(fd), 0);
+
+ // Compare inode numbers to check that the file sent over the wire is actually
+ // the one expected.
+ ASSERT_EQ(inode_num, st.st_ino);
+}
+
+class MyChannelDescriptorListener : public IPC::Listener {
public:
+ explicit MyChannelDescriptorListener(ino_t expected_inode_num)
+ : expected_inode_num_(expected_inode_num),
+ num_fds_received_(0) {}
+
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
PickleIterator iter(message);
+ ++num_fds_received_;
base::FileDescriptor descriptor;
IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
- HandleFD(descriptor.fd);
- return true;
- }
-
- protected:
- virtual void HandleFD(int fd) = 0;
-};
-
-class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
- public:
- explicit MyChannelDescriptorListener(ino_t expected_inode_num)
- : MyChannelDescriptorListenerBase(),
- expected_inode_num_(expected_inode_num),
- num_fds_received_(0) {
- }
+ VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_);
+ if (num_fds_received_ == kNumFDsToSend)
+ base::MessageLoop::current()->Quit();
- bool GotExpectedNumberOfDescriptors() const {
- return num_fds_received_ == kNumFDsToSend;
+ return true;
}
virtual void OnChannelError() OVERRIDE {
base::MessageLoop::current()->Quit();
}
- protected:
- virtual void HandleFD(int fd) OVERRIDE {
- // Check that we can read from the FD.
- char buf;
- ssize_t amt_read = read(fd, &buf, 1);
- ASSERT_EQ(amt_read, 1);
- ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
-
- struct stat st;
- ASSERT_EQ(fstat(fd, &st), 0);
-
- ASSERT_EQ(close(fd), 0);
-
- // Compare inode numbers to check that the file sent over the wire is
- // actually the one expected.
- ASSERT_EQ(expected_inode_num_, st.st_ino);
-
- ++num_fds_received_;
- if (num_fds_received_ == kNumFDsToSend)
- base::MessageLoop::current()->Quit();
+ bool GotExpectedNumberOfDescriptors() const {
+ return num_fds_received_ == kNumFDsToSend;
}
private:
@@ -91,7 +77,6 @@ class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
unsigned num_fds_received_;
};
-
class IPCSendFdsTest : public IPCTestBase {
protected:
void RunServer() {
@@ -193,188 +178,6 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
}
#endif // defined(OS_MACOSX)
-
-class MyCBListener : public MyChannelDescriptorListenerBase {
- public:
- MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
- : MyChannelDescriptorListenerBase(),
- cb_(cb) {
- }
-
- protected:
- virtual void HandleFD(int fd) OVERRIDE {
- cb_.Run(fd);
- }
- private:
- base::Callback<void(int)> cb_;
-};
-
-std::pair<int, int> make_socket_pair() {
- int pipe_fds[2];
- CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
- return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
-}
-
-static void null_cb(int unused_fd) {
- NOTREACHED();
-}
-
-class PipeChannelHelper {
- public:
- PipeChannelHelper(base::Thread* in_thread,
- base::Thread* out_thread,
- base::Callback<void(int)> cb,
- int fds_to_send) :
- in_thread_(in_thread),
- out_thread_(out_thread),
- cb_listener_(cb, fds_to_send),
- null_listener_(base::Bind(&null_cb), 0) {
- }
-
- void Init() {
- IPC::ChannelHandle in_handle("IN");
- in.reset(new IPC::Channel(in_handle,
- IPC::Channel::MODE_SERVER,
- &null_listener_));
- base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false);
- IPC::ChannelHandle out_handle("OUT", out_fd);
- out.reset(new IPC::Channel(out_handle,
- IPC::Channel::MODE_CLIENT,
- &cb_listener_));
- // PostTask the connect calls to make sure the callbacks happens
- // on the right threads.
- in_thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&PipeChannelHelper::Connect, in.get()));
- out_thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&PipeChannelHelper::Connect, out.get()));
- }
-
- static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
- base::WaitableEvent *event) {
- c->reset(0);
- event->Signal();
- }
-
- ~PipeChannelHelper() {
- base::WaitableEvent a(true, false);
- base::WaitableEvent b(true, false);
- in_thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
- out_thread_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
- a.Wait();
- b.Wait();
- }
-
- static void Connect(IPC::Channel *channel) {
- EXPECT_TRUE(channel->Connect());
- }
-
- void Send(int fd) {
- CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
-
- ASSERT_GE(fd, 0);
- base::FileDescriptor descriptor(fd, true);
-
- IPC::Message* message =
- new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
- IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
- ASSERT_TRUE(in->Send(message));
- }
-
- private:
- scoped_ptr<IPC::Channel> in, out;
- base::Thread* in_thread_;
- base::Thread* out_thread_;
- MyCBListener cb_listener_;
- MyCBListener null_listener_;
-};
-
-// This test is meant to provoke a kernel bug on OSX, and to prove
-// that the workaround for it is working. It sets up two pipes and three
-// threads, the producer thread creates socketpairs and sends one of the fds
-// over pipe1 to the middleman thread. The middleman thread simply takes the fd
-// sends it over pipe2 to the consumer thread. The consumer thread writes a byte
-// to each fd it receives and then closes the pipe. The producer thread reads
-// the bytes back from each pair of pipes and make sure that everything worked.
-// This feedback mechanism makes sure that not too many file descriptors are
-// in flight at the same time. For more info on the bug, see:
-// http://crbug.com/298276
-class IPCMultiSendingFdsTest : public testing::Test {
- public:
- IPCMultiSendingFdsTest() : received_(true, false) {}
-
- void Producer(PipeChannelHelper* dest,
- base::Thread* t,
- int pipes_to_send) {
- for (int i = 0; i < pipes_to_send; i++) {
- received_.Reset();
- std::pair<int, int> pipe_fds = make_socket_pair();
- t->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&PipeChannelHelper::Send,
- base::Unretained(dest),
- pipe_fds.second));
- char tmp = 'x';
- CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
- CHECK_EQ(0, HANDLE_EINTR(close(pipe_fds.first)));
- received_.Wait();
- }
- }
-
- void ConsumerHandleFD(int fd) {
- char tmp = 'y';
- CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
- CHECK_EQ(tmp, 'x');
- CHECK_EQ(0, HANDLE_EINTR(close(fd)));
- received_.Signal();
- }
-
- base::Thread* CreateThread(const char* name) {
- base::Thread* ret = new base::Thread(name);
- base::Thread::Options options;
- options.message_loop_type = base::MessageLoop::TYPE_IO;
- ret->StartWithOptions(options);
- return ret;
- }
-
- void Run() {
- // On my mac, this test fails roughly 35 times per
- // million sends with low load, but much more with high load.
- // Unless the workaround is in place. With 10000 sends, we
- // should see at least a 3% failure rate.
- const int pipes_to_send = 20000;
- scoped_ptr<base::Thread> producer(CreateThread("producer"));
- scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
- scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
- PipeChannelHelper pipe1(
- middleman.get(),
- consumer.get(),
- base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
- base::Unretained(this)),
- pipes_to_send);
- PipeChannelHelper pipe2(
- producer.get(),
- middleman.get(),
- base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
- pipes_to_send);
- pipe1.Init();
- pipe2.Init();
- Producer(&pipe2, producer.get(), pipes_to_send);
- }
-
- private:
- base::WaitableEvent received_;
-};
-
-TEST_F(IPCMultiSendingFdsTest, StressTest) {
- Run();
-}
-
} // namespace
#endif // defined(OS_POSIX)