| Index: ipc/ipc_sync_channel_unittest.cc | 
| diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc | 
| index 6cb745773295b14efbb54b5a72afa53b6019d08d..81f5d11917c08d20ef415ab7014ba58329be8f29 100644 | 
| --- a/ipc/ipc_sync_channel_unittest.cc | 
| +++ b/ipc/ipc_sync_channel_unittest.cc | 
| @@ -1192,9 +1192,11 @@ namespace { | 
|  | 
| class RestrictedDispatchServer : public Worker { | 
| public: | 
| -  RestrictedDispatchServer(WaitableEvent* sent_ping_event) | 
| +  RestrictedDispatchServer(WaitableEvent* sent_ping_event, | 
| +                           WaitableEvent* wait_event) | 
| : Worker("restricted_channel", Channel::MODE_SERVER), | 
| -        sent_ping_event_(sent_ping_event) { } | 
| +        sent_ping_event_(sent_ping_event), | 
| +        wait_event_(wait_event) { } | 
|  | 
| void OnDoPing(int ping) { | 
| // Send an asynchronous message that unblocks the caller. | 
| @@ -1207,12 +1209,18 @@ class RestrictedDispatchServer : public Worker { | 
| FROM_HERE, base::Bind(&RestrictedDispatchServer::OnPingSent, this)); | 
| } | 
|  | 
| +  void OnPingTTL(int ping, int* out) { | 
| +    *out = ping; | 
| +    wait_event_->Wait(); | 
| +  } | 
| + | 
| base::Thread* ListenerThread() { return Worker::ListenerThread(); } | 
|  | 
| private: | 
| bool OnMessageReceived(const Message& message) { | 
| IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchServer, message) | 
| IPC_MESSAGE_HANDLER(SyncChannelTestMsg_NoArgs, OnNoArgs) | 
| +     IPC_MESSAGE_HANDLER(SyncChannelTestMsg_PingTTL, OnPingTTL) | 
| IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Done, Done) | 
| IPC_END_MESSAGE_MAP() | 
| return true; | 
| @@ -1224,12 +1232,22 @@ class RestrictedDispatchServer : public Worker { | 
|  | 
| void OnNoArgs() { } | 
| WaitableEvent* sent_ping_event_; | 
| +  WaitableEvent* wait_event_; | 
| }; | 
|  | 
| class NonRestrictedDispatchServer : public Worker { | 
| public: | 
| -  NonRestrictedDispatchServer() | 
| -      : Worker("non_restricted_channel", Channel::MODE_SERVER) {} | 
| +  NonRestrictedDispatchServer(WaitableEvent* signal_event) | 
| +      : Worker("non_restricted_channel", Channel::MODE_SERVER), | 
| +        signal_event_(signal_event) {} | 
| + | 
| +  base::Thread* ListenerThread() { return Worker::ListenerThread(); } | 
| + | 
| +  void OnDoPingTTL(int ping) { | 
| +    int value = 0; | 
| +    Send(new SyncChannelTestMsg_PingTTL(ping, &value)); | 
| +    signal_event_->Signal(); | 
| +  } | 
|  | 
| private: | 
| bool OnMessageReceived(const Message& message) { | 
| @@ -1241,23 +1259,26 @@ class NonRestrictedDispatchServer : public Worker { | 
| } | 
|  | 
| void OnNoArgs() { } | 
| +  WaitableEvent* signal_event_; | 
| }; | 
|  | 
| class RestrictedDispatchClient : public Worker { | 
| public: | 
| RestrictedDispatchClient(WaitableEvent* sent_ping_event, | 
| RestrictedDispatchServer* server, | 
| +                           NonRestrictedDispatchServer* server2, | 
| int* success) | 
| : Worker("restricted_channel", Channel::MODE_CLIENT), | 
| ping_(0), | 
| server_(server), | 
| +        server2_(server2), | 
| success_(success), | 
| sent_ping_event_(sent_ping_event) {} | 
|  | 
| void Run() { | 
| // Incoming messages from our channel should only be dispatched when we | 
| // send a message on that same channel. | 
| -    channel()->SetRestrictDispatchToSameChannel(true); | 
| +    channel()->SetRestrictDispatchChannelGroup(1); | 
|  | 
| server_->ListenerThread()->message_loop()->PostTask( | 
| FROM_HERE, base::Bind(&RestrictedDispatchServer::OnDoPing, server_, 1)); | 
| @@ -1268,7 +1289,7 @@ class RestrictedDispatchClient : public Worker { | 
| else | 
| LOG(ERROR) << "Send failed to dispatch incoming message on same channel"; | 
|  | 
| -    scoped_ptr<SyncChannel> non_restricted_channel(new SyncChannel( | 
| +    non_restricted_channel_.reset(new SyncChannel( | 
| "non_restricted_channel", Channel::MODE_CLIENT, this, | 
| ipc_thread().message_loop_proxy(), true, shutdown_event())); | 
|  | 
| @@ -1283,7 +1304,7 @@ class RestrictedDispatchClient : public Worker { | 
| // without hooking into the internals of SyncChannel. I haven't seen it in | 
| // practice (i.e. not setting SetRestrictDispatchToSameChannel does cause | 
| // the following to fail). | 
| -    non_restricted_channel->Send(new SyncChannelTestMsg_NoArgs); | 
| +    non_restricted_channel_->Send(new SyncChannelTestMsg_NoArgs); | 
| if (ping_ == 1) | 
| ++*success_; | 
| else | 
| @@ -1295,8 +1316,20 @@ class RestrictedDispatchClient : public Worker { | 
| else | 
| LOG(ERROR) << "Send failed to dispatch incoming message on same channel"; | 
|  | 
| -    non_restricted_channel->Send(new SyncChannelTestMsg_Done); | 
| -    non_restricted_channel.reset(); | 
| +    // Check that the incoming message on the non-restricted channel is | 
| +    // dispatched when sending on the restricted channel. | 
| +    server2_->ListenerThread()->message_loop()->PostTask( | 
| +        FROM_HERE, | 
| +        base::Bind(&NonRestrictedDispatchServer::OnDoPingTTL, server2_, 3)); | 
| +    int value = 0; | 
| +    Send(new SyncChannelTestMsg_PingTTL(4, &value)); | 
| +    if (ping_ == 3 && value == 4) | 
| +      ++*success_; | 
| +    else | 
| +      LOG(ERROR) << "Send failed to dispatch message from unrestricted channel"; | 
| + | 
| +    non_restricted_channel_->Send(new SyncChannelTestMsg_Done); | 
| +    non_restricted_channel_.reset(); | 
| Send(new SyncChannelTestMsg_Done); | 
| Done(); | 
| } | 
| @@ -1305,6 +1338,7 @@ class RestrictedDispatchClient : public Worker { | 
| bool OnMessageReceived(const Message& message) { | 
| IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchClient, message) | 
| IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Ping, OnPing) | 
| +     IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_PingTTL, OnPingTTL) | 
| IPC_END_MESSAGE_MAP() | 
| return true; | 
| } | 
| @@ -1313,27 +1347,40 @@ class RestrictedDispatchClient : public Worker { | 
| ping_ = ping; | 
| } | 
|  | 
| +  void OnPingTTL(int ping, IPC::Message* reply) { | 
| +    ping_ = ping; | 
| +    // This message comes from the NonRestrictedDispatchServer, we have to send | 
| +    // the reply back manually. | 
| +    SyncChannelTestMsg_PingTTL::WriteReplyParams(reply, ping); | 
| +    non_restricted_channel_->Send(reply); | 
| +  } | 
| + | 
| int ping_; | 
| RestrictedDispatchServer* server_; | 
| +  NonRestrictedDispatchServer* server2_; | 
| int* success_; | 
| WaitableEvent* sent_ping_event_; | 
| +  scoped_ptr<SyncChannel> non_restricted_channel_; | 
| }; | 
|  | 
| }  // namespace | 
|  | 
| TEST_F(IPCSyncChannelTest, RestrictedDispatch) { | 
| WaitableEvent sent_ping_event(false, false); | 
| - | 
| +  WaitableEvent wait_event(false, false); | 
| RestrictedDispatchServer* server = | 
| -      new RestrictedDispatchServer(&sent_ping_event); | 
| +      new RestrictedDispatchServer(&sent_ping_event, &wait_event); | 
| +  NonRestrictedDispatchServer* server2 = | 
| +      new NonRestrictedDispatchServer(&wait_event); | 
| + | 
| int success = 0; | 
| std::vector<Worker*> workers; | 
| -  workers.push_back(new NonRestrictedDispatchServer); | 
| workers.push_back(server); | 
| -  workers.push_back( | 
| -      new RestrictedDispatchClient(&sent_ping_event, server, &success)); | 
| +  workers.push_back(server2); | 
| +  workers.push_back(new RestrictedDispatchClient( | 
| +      &sent_ping_event, server, server2, &success)); | 
| RunTest(workers); | 
| -  EXPECT_EQ(3, success); | 
| +  EXPECT_EQ(4, success); | 
| } | 
|  | 
| //----------------------------------------------------------------------------- | 
| @@ -1388,7 +1435,7 @@ class RestrictedDispatchDeadlockServer : public Worker { | 
| } | 
|  | 
| void Run() { | 
| -    channel()->SetRestrictDispatchToSameChannel(true); | 
| +    channel()->SetRestrictDispatchChannelGroup(1); | 
| server_ready_event_->Signal(); | 
| } | 
|  | 
| @@ -1596,6 +1643,114 @@ TEST_F(IPCSyncChannelTest, RestrictedDispatchDeadlock) { | 
|  | 
| //----------------------------------------------------------------------------- | 
|  | 
| +// This test case inspired by crbug.com/120530 | 
| +// We create 4 workers that pipe to each other W1->W2->W3->W4->W1 then we send a | 
| +// message that recurses through 3, 4 or 5 steps to make sure, say, W1 can | 
| +// re-enter when called from W4 while it's sending a message to W2. | 
| +// The first worker drives the whole test so it must be treated specially. | 
| +namespace { | 
| + | 
| +class RestrictedDispatchPipeWorker : public Worker { | 
| + public: | 
| +  RestrictedDispatchPipeWorker( | 
| +      const std::string &channel1, | 
| +      WaitableEvent* event1, | 
| +      const std::string &channel2, | 
| +      WaitableEvent* event2, | 
| +      int group, | 
| +      int* success) | 
| +      : Worker(channel1, Channel::MODE_SERVER), | 
| +        event1_(event1), | 
| +        event2_(event2), | 
| +        other_channel_name_(channel2), | 
| +        group_(group), | 
| +        success_(success) { | 
| +  } | 
| + | 
| +  void OnPingTTL(int ping, int* ret) { | 
| +    *ret = 0; | 
| +    if (!ping) | 
| +      return; | 
| +    other_channel_->Send(new SyncChannelTestMsg_PingTTL(ping - 1, ret)); | 
| +    ++*ret; | 
| +  } | 
| + | 
| +  void OnDone() { | 
| +    if (is_first()) | 
| +      return; | 
| +    other_channel_->Send(new SyncChannelTestMsg_Done); | 
| +    other_channel_.reset(); | 
| +    Done(); | 
| +  } | 
| + | 
| +  void Run() { | 
| +    channel()->SetRestrictDispatchChannelGroup(group_); | 
| +    if (is_first()) | 
| +      event1_->Signal(); | 
| +    event2_->Wait(); | 
| +    other_channel_.reset(new SyncChannel( | 
| +        other_channel_name_, Channel::MODE_CLIENT, this, | 
| +        ipc_thread().message_loop_proxy(), true, shutdown_event())); | 
| +    other_channel_->SetRestrictDispatchChannelGroup(group_); | 
| +    if (!is_first()) { | 
| +      event1_->Signal(); | 
| +      return; | 
| +    } | 
| +    *success_ = 0; | 
| +    int value = 0; | 
| +    OnPingTTL(3, &value); | 
| +    *success_ += (value == 3); | 
| +    OnPingTTL(4, &value); | 
| +    *success_ += (value == 4); | 
| +    OnPingTTL(5, &value); | 
| +    *success_ += (value == 5); | 
| +    other_channel_->Send(new SyncChannelTestMsg_Done); | 
| +    other_channel_.reset(); | 
| +    Done(); | 
| +  } | 
| + | 
| +  bool is_first() { return !!success_; } | 
| + | 
| + private: | 
| +  bool OnMessageReceived(const Message& message) { | 
| +    IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchPipeWorker, message) | 
| +     IPC_MESSAGE_HANDLER(SyncChannelTestMsg_PingTTL, OnPingTTL) | 
| +     IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Done, OnDone) | 
| +    IPC_END_MESSAGE_MAP() | 
| +    return true; | 
| +  } | 
| + | 
| +  scoped_ptr<SyncChannel> other_channel_; | 
| +  WaitableEvent* event1_; | 
| +  WaitableEvent* event2_; | 
| +  std::string other_channel_name_; | 
| +  int group_; | 
| +  int* success_; | 
| +}; | 
| + | 
| +}  // namespace | 
| + | 
| +TEST_F(IPCSyncChannelTest, RestrictedDispatch4WayDeadlock) { | 
| +  int success = 0; | 
| +  std::vector<Worker*> workers; | 
| +  WaitableEvent event0(true, false); | 
| +  WaitableEvent event1(true, false); | 
| +  WaitableEvent event2(true, false); | 
| +  WaitableEvent event3(true, false); | 
| +  workers.push_back(new RestrictedDispatchPipeWorker( | 
| +        "channel0", &event0, "channel1", &event1, 1, &success)); | 
| +  workers.push_back(new RestrictedDispatchPipeWorker( | 
| +        "channel1", &event1, "channel2", &event2, 2, NULL)); | 
| +  workers.push_back(new RestrictedDispatchPipeWorker( | 
| +        "channel2", &event2, "channel3", &event3, 3, NULL)); | 
| +  workers.push_back(new RestrictedDispatchPipeWorker( | 
| +        "channel3", &event3, "channel0", &event0, 4, NULL)); | 
| +  RunTest(workers); | 
| +  EXPECT_EQ(3, success); | 
| +} | 
| + | 
| +//----------------------------------------------------------------------------- | 
| + | 
| // Generate a validated channel ID using Channel::GenerateVerifiedChannelID(). | 
| namespace { | 
|  | 
|  |