Index: ipc/ipc_sync_channel_unittest.cc |
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc |
index 511164a70d9a98b2ca02091a8481daec6510624d..0424b2fb17d667668d826c8b6ddf993334aa3ec0 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(). |