diff options
author | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 04:22:34 +0000 |
---|---|---|
committer | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 04:22:34 +0000 |
commit | 787f5e460f6265f961f88b75391edeb6eeb8a720 (patch) | |
tree | 4bd626c508e2b76b83a2d7131c832f0cac70c52c /ipc | |
parent | 29dd580cd7a471d5760649a8546459f31382244b (diff) | |
download | chromium_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.cc | 10 | ||||
-rw-r--r-- | ipc/file_descriptor_set_posix.h | 3 | ||||
-rw-r--r-- | ipc/ipc_channel.h | 23 | ||||
-rw-r--r-- | ipc/ipc_channel_nacl.cc | 2 | ||||
-rw-r--r-- | ipc/ipc_channel_nacl.h | 2 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.cc | 132 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.h | 12 | ||||
-rw-r--r-- | ipc/ipc_channel_reader.cc | 12 | ||||
-rw-r--r-- | ipc/ipc_channel_reader.h | 12 | ||||
-rw-r--r-- | ipc/ipc_channel_win.cc | 3 | ||||
-rw-r--r-- | ipc/ipc_channel_win.h | 2 | ||||
-rw-r--r-- | ipc/ipc_send_fds_test.cc | 255 |
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) |