summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipc/ipc_sync_channel.cc8
-rw-r--r--ipc/ipc_sync_channel_unittest.cc114
-rw-r--r--ipc/ipc_sync_message_unittest.h5
3 files changed, 123 insertions, 4 deletions
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 2763ebd32..b9dc2c5 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -323,13 +323,13 @@ bool SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
if (TryToUnblockListener(&msg))
return true;
- if (msg.should_unblock()) {
- received_sync_msgs_->QueueMessage(msg, this);
+ if (msg.is_reply()) {
+ received_sync_msgs_->QueueReply(msg, this);
return true;
}
- if (msg.is_reply()) {
- received_sync_msgs_->QueueReply(msg, this);
+ if (msg.should_unblock()) {
+ received_sync_msgs_->QueueMessage(msg, this);
return true;
}
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc
index 511164a..0424b2f 100644
--- a/ipc/ipc_sync_channel_unittest.cc
+++ b/ipc/ipc_sync_channel_unittest.cc
@@ -1750,6 +1750,120 @@ TEST_F(IPCSyncChannelTest, RestrictedDispatch4WayDeadlock) {
EXPECT_EQ(3, success);
}
+
+//-----------------------------------------------------------------------------
+//
+// This test case inspired by crbug.com/122443
+// We want to make sure a reply message with the unblock flag set correctly
+// behaves as a reply, not a regular message.
+// We have 3 workers. Server1 will send a message to Server2 (which will block),
+// during which it will dispatch a message comming from Client, at which point
+// it will send another message to Server2. While sending that second message it
+// will receive a reply from Server1 with the unblock flag.
+
+namespace {
+
+class ReentrantReplyServer1 : public Worker {
+ public:
+ ReentrantReplyServer1(WaitableEvent* server_ready)
+ : Worker("reentrant_reply1", Channel::MODE_SERVER),
+ server_ready_(server_ready) { }
+
+ void Run() {
+ server2_channel_.reset(new SyncChannel(
+ "reentrant_reply2", Channel::MODE_CLIENT, this,
+ ipc_thread().message_loop_proxy(), true, shutdown_event()));
+ server_ready_->Signal();
+ Message* msg = new SyncChannelTestMsg_Reentrant1();
+ server2_channel_->Send(msg);
+ server2_channel_.reset();
+ Done();
+ }
+
+ private:
+ bool OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(ReentrantReplyServer1, message)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Reentrant2, OnReentrant2)
+ IPC_REPLY_HANDLER(OnReply)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnReentrant2() {
+ Message* msg = new SyncChannelTestMsg_Reentrant3();
+ server2_channel_->Send(msg);
+ }
+
+ void OnReply(const Message& message) {
+ // If we get here, the Send() will never receive the reply (thus would
+ // hang), so abort instead.
+ LOG(FATAL) << "Reply message was dispatched";
+ }
+
+ WaitableEvent* server_ready_;
+ scoped_ptr<SyncChannel> server2_channel_;
+};
+
+class ReentrantReplyServer2 : public Worker {
+ public:
+ ReentrantReplyServer2()
+ : Worker("reentrant_reply2", Channel::MODE_SERVER),
+ reply_(NULL) { }
+
+ private:
+ bool OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(ReentrantReplyServer2, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ SyncChannelTestMsg_Reentrant1, OnReentrant1)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Reentrant3, OnReentrant3)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnReentrant1(Message* reply) {
+ DCHECK(!reply_);
+ reply_ = reply;
+ }
+
+ void OnReentrant3() {
+ DCHECK(reply_);
+ Message* reply = reply_;
+ reply_ = NULL;
+ reply->set_unblock(true);
+ Send(reply);
+ Done();
+ }
+
+ Message* reply_;
+};
+
+class ReentrantReplyClient : public Worker {
+ public:
+ ReentrantReplyClient(WaitableEvent* server_ready)
+ : Worker("reentrant_reply1", Channel::MODE_CLIENT),
+ server_ready_(server_ready) { }
+
+ void Run() {
+ server_ready_->Wait();
+ Send(new SyncChannelTestMsg_Reentrant2());
+ Done();
+ }
+
+ private:
+ WaitableEvent* server_ready_;
+};
+
+} // namespace
+
+TEST_F(IPCSyncChannelTest, ReentrantReply) {
+ std::vector<Worker*> workers;
+ WaitableEvent server_ready(false, false);
+ workers.push_back(new ReentrantReplyServer2());
+ workers.push_back(new ReentrantReplyServer1(&server_ready));
+ workers.push_back(new ReentrantReplyClient(&server_ready));
+ RunTest(workers);
+}
+
//-----------------------------------------------------------------------------
// Generate a validated channel ID using Channel::GenerateVerifiedChannelID().
diff --git a/ipc/ipc_sync_message_unittest.h b/ipc/ipc_sync_message_unittest.h
index 66a7876..114aca6 100644
--- a/ipc/ipc_sync_message_unittest.h
+++ b/ipc/ipc_sync_message_unittest.h
@@ -113,3 +113,8 @@ IPC_SYNC_MESSAGE_ROUTED3_4(Msg_R_3_4, bool, int, std::string, int, bool,
IPC_MESSAGE_CONTROL1(SyncChannelTestMsg_Ping, int)
IPC_SYNC_MESSAGE_CONTROL1_1(SyncChannelTestMsg_PingTTL, int, int)
IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_Done)
+
+// Messages for ReentrantReply test.
+IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_Reentrant1)
+IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_Reentrant2)
+IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_Reentrant3)