Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Unified Diff: ipc/ipc_sync_channel_unittest.cc

Issue 9022038: Reimplement ReceivedSyncMsgQueue::DispatchMessages (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add descriptive comment to unittest Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ipc/ipc_sync_channel.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ipc/ipc_sync_channel_unittest.cc
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc
index e7903f48abdf27ab66e089f1c552c7ae0cca5989..7ffbf79f6fe21b806ee79eabd1eeeb772a671ab9 100644
--- a/ipc/ipc_sync_channel_unittest.cc
+++ b/ipc/ipc_sync_channel_unittest.cc
@@ -1334,4 +1334,262 @@ TEST_F(IPCSyncChannelTest, RestrictedDispatch) {
EXPECT_EQ(3, success);
}
+//-----------------------------------------------------------------------------
+
+// This test case inspired by crbug.com/108491
+// We create two servers that use the same ListenerThread but have
+// SetRestrictDispatchToSameChannel set to true.
+// We create clients, then use some specific WaitableEvent wait/signalling to
+// ensure that messages get dispatched in a way that causes a deadlock due to
+// a nested dispatch and an eligible message in a higher-level dispatch's
+// delayed_queue. Specifically, we start with client1 about so send an
piman 2012/01/10 20:52:14 typo: "about so send" -> "about to send"
+// unblocking message to server1, while the shared listener thread for the
+// servers server1 and server2 is about to send a non-unblocking message to
+// client1. At the same time, client2 will be about to send an unblocking
+// message to server2. Server1 will handle the client1->server1 message by
+// telling server2 to send a non-unblocking message to client2.
+// What should happen is that the send to server2 should find the pending,
+// same-context client2->server2 message to dispatch, causing client2 to
+// unblock then handle the server2->client2 message, so that the shared
+// servers' listener thread can then respond to the client1->server1 message.
+// Then client1 can handle the non-unblocking server1->client1 message.
+// The old code would end up in a state where the server2->client2 message is
+// sent, but the client2->server2 message (which is eligible for dispatch, and
+// which is what client2 is waiting for) is stashed in a local delayed_queue
+// that has server1's channel context, causing a deadlock.
+// WaitableEvents in the events array are used to:
+// event 0: indicate to client1 that server listener is in OnDoServerTask
+// event 1: indicate to client1 that client2 listener is in OnDoClient2Task
+// event 2: indicate to server1 that client2 listener is in OnDoClient2Task
+// event 3: indicate to client2 that server listener is in OnDoServerTask
+
+namespace {
+
+class RestrictedDispatchDeadlockServer : public Worker {
+ public:
+ RestrictedDispatchDeadlockServer(int server_num,
+ WaitableEvent* server_ready_event,
+ WaitableEvent** events,
+ RestrictedDispatchDeadlockServer* peer)
+ : Worker(server_num == 1 ? "channel1" : "channel2", Channel::MODE_SERVER),
+ server_num_(server_num),
+ server_ready_event_(server_ready_event),
+ events_(events),
+ peer_(peer),
+ client_kicked_(false) { }
+
+ void OnDoServerTask() {
+ events_[3]->Signal();
+ events_[2]->Wait();
+ events_[0]->Signal();
+ SendMessageToClient();
+ }
+
+ void Run() {
+ channel()->SetRestrictDispatchToSameChannel(true);
+ server_ready_event_->Signal();
+ }
+
+ base::Thread* ListenerThread() { return Worker::ListenerThread(); }
+
+ private:
+ bool OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchDeadlockServer, message)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_NoArgs, OnNoArgs)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Done, Done)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnNoArgs() {
+ if (server_num_ == 1) {
+ DCHECK(peer_ != NULL);
+ peer_->SendMessageToClient();
+ }
+ }
+
+ void SendMessageToClient() {
+ Message* msg = new SyncChannelTestMsg_NoArgs;
+ msg->set_unblock(false);
+ DCHECK(!msg->should_unblock());
+ Send(msg);
+ }
+
+ int server_num_;
+ WaitableEvent* server_ready_event_;
+ WaitableEvent** events_;
+ RestrictedDispatchDeadlockServer* peer_;
+ bool client_kicked_;
+};
+
+class RestrictedDispatchDeadlockClient2 : public Worker {
+ public:
+ RestrictedDispatchDeadlockClient2(RestrictedDispatchDeadlockServer* server,
+ WaitableEvent* server_ready_event,
+ WaitableEvent** events)
+ : Worker("channel2", Channel::MODE_CLIENT),
+ server_(server),
+ server_ready_event_(server_ready_event),
+ events_(events),
+ received_msg_(false),
+ received_noarg_reply_(false),
+ done_issued_(false) {}
+
+ void Run() {
+ server_ready_event_->Wait();
+ }
+
+ void OnDoClient2Task() {
+ events_[3]->Wait();
+ events_[1]->Signal();
+ events_[2]->Signal();
+ DCHECK(received_msg_ == false);
+
+ Message* message = new SyncChannelTestMsg_NoArgs;
+ message->set_unblock(true);
+ Send(message);
+ received_noarg_reply_ = true;
+ }
+
+ base::Thread* ListenerThread() { return Worker::ListenerThread(); }
+ private:
+ bool OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchDeadlockClient2, message)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_NoArgs, OnNoArgs)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnNoArgs() {
+ received_msg_ = true;
+ PossiblyDone();
+ }
+
+ void PossiblyDone() {
+ if (received_noarg_reply_ && received_msg_) {
+ DCHECK(done_issued_ == false);
+ done_issued_ = true;
+ Send(new SyncChannelTestMsg_Done);
+ Done();
+ }
+ }
+
+ RestrictedDispatchDeadlockServer* server_;
+ WaitableEvent* server_ready_event_;
+ WaitableEvent** events_;
+ bool received_msg_;
+ bool received_noarg_reply_;
+ bool done_issued_;
+};
+
+class RestrictedDispatchDeadlockClient1 : public Worker {
+ public:
+ RestrictedDispatchDeadlockClient1(RestrictedDispatchDeadlockServer* server,
+ RestrictedDispatchDeadlockClient2* peer,
+ WaitableEvent* server_ready_event,
+ WaitableEvent** events)
+ : Worker("channel1", Channel::MODE_CLIENT),
+ server_(server),
+ peer_(peer),
+ server_ready_event_(server_ready_event),
+ events_(events),
+ received_msg_(false),
+ received_noarg_reply_(false),
+ done_issued_(false) {}
+
+ void Run() {
+ server_ready_event_->Wait();
+ server_->ListenerThread()->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&RestrictedDispatchDeadlockServer::OnDoServerTask, server_));
+ peer_->ListenerThread()->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&RestrictedDispatchDeadlockClient2::OnDoClient2Task, peer_));
+ events_[0]->Wait();
+ events_[1]->Wait();
+ DCHECK(received_msg_ == false);
+
+ Message* message = new SyncChannelTestMsg_NoArgs;
+ message->set_unblock(true);
+ Send(message);
+ received_noarg_reply_ = true;
+ PossiblyDone();
+ }
+
+ base::Thread* ListenerThread() { return Worker::ListenerThread(); }
+ private:
+ bool OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchDeadlockClient1, message)
+ IPC_MESSAGE_HANDLER(SyncChannelTestMsg_NoArgs, OnNoArgs)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnNoArgs() {
+ received_msg_ = true;
+ PossiblyDone();
+ }
+
+ void PossiblyDone() {
+ if (received_noarg_reply_ && received_msg_) {
+ DCHECK(done_issued_ == false);
+ done_issued_ = true;
+ Send(new SyncChannelTestMsg_Done);
+ Done();
+ }
+ }
+
+ RestrictedDispatchDeadlockServer* server_;
+ RestrictedDispatchDeadlockClient2* peer_;
+ WaitableEvent* server_ready_event_;
+ WaitableEvent** events_;
+ bool received_msg_;
+ bool received_noarg_reply_;
+ bool done_issued_;
+};
+
+} // namespace
+
+TEST_F(IPCSyncChannelTest, RestrictedDispatchDeadlock) {
+ std::vector<Worker*> workers;
+
+ // A shared worker thread so that server1 and server2 run on one thread.
+ base::Thread worker_thread("RestrictedDispatchDeadlock");
+ ASSERT_TRUE(worker_thread.Start());
+
+ WaitableEvent server1_ready(false, false);
+ WaitableEvent server2_ready(false, false);
+
+ WaitableEvent event0(false, false);
+ WaitableEvent event1(false, false);
+ WaitableEvent event2(false, false);
+ WaitableEvent event3(false, false);
+ WaitableEvent* events[4] = {&event0, &event1, &event2, &event3};
+
+ RestrictedDispatchDeadlockServer* server1;
+ RestrictedDispatchDeadlockServer* server2;
+ RestrictedDispatchDeadlockClient1* client1;
+ RestrictedDispatchDeadlockClient2* client2;
+
+ server2 = new RestrictedDispatchDeadlockServer(2, &server2_ready, events,
+ NULL);
+ server2->OverrideThread(&worker_thread);
+ workers.push_back(server2);
+
+ client2 = new RestrictedDispatchDeadlockClient2(server2, &server2_ready,
+ events);
+ workers.push_back(client2);
+
+ server1 = new RestrictedDispatchDeadlockServer(1, &server1_ready, events,
+ server2);
+ server1->OverrideThread(&worker_thread);
+ workers.push_back(server1);
+
+ client1 = new RestrictedDispatchDeadlockClient1(server1, client2,
+ &server1_ready, events);
+ workers.push_back(client1);
+
+ RunTest(workers);
+}
+
} // namespace IPC
« no previous file with comments | « ipc/ipc_sync_channel.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698