LCOV - code coverage report
Current view: directory - ipc/chromium/src/chrome/common - ipc_sync_channel.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 216 2 0.9 %
Date: 2012-06-02 Functions: 42 2 4.8 %

       1                 : // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
       2                 : // Use of this source code is governed by a BSD-style license that can be
       3                 : // found in the LICENSE file.
       4                 : 
       5                 : #include "chrome/common/ipc_sync_channel.h"
       6                 : 
       7                 : #include "base/lazy_instance.h"
       8                 : #include "base/logging.h"
       9                 : #include "base/thread_local.h"
      10                 : #include "base/message_loop.h"
      11                 : #include "base/waitable_event.h"
      12                 : #include "base/waitable_event_watcher.h"
      13                 : #include "chrome/common/ipc_sync_message.h"
      14                 : 
      15                 : using base::TimeDelta;
      16                 : using base::TimeTicks;
      17                 : using base::WaitableEvent;
      18                 : 
      19                 : namespace IPC {
      20                 : // When we're blocked in a Send(), we need to process incoming synchronous
      21                 : // messages right away because it could be blocking our reply (either
      22                 : // directly from the same object we're calling, or indirectly through one or
      23                 : // more other channels).  That means that in SyncContext's OnMessageReceived,
      24                 : // we need to process sync message right away if we're blocked.  However a
      25                 : // simple check isn't sufficient, because the listener thread can be in the
      26                 : // process of calling Send.
      27                 : // To work around this, when SyncChannel filters a sync message, it sets
      28                 : // an event that the listener thread waits on during its Send() call.  This
      29                 : // allows us to dispatch incoming sync messages when blocked.  The race
      30                 : // condition is handled because if Send is in the process of being called, it
      31                 : // will check the event.  In case the listener thread isn't sending a message,
      32                 : // we queue a task on the listener thread to dispatch the received messages.
      33                 : // The messages are stored in this queue object that's shared among all
      34                 : // SyncChannel objects on the same thread (since one object can receive a
      35                 : // sync message while another one is blocked).
      36                 : 
      37                 : class SyncChannel::ReceivedSyncMsgQueue :
      38                 :     public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> {
      39                 :  public:
      40                 :   // Returns the ReceivedSyncMsgQueue instance for this thread, creating one
      41                 :   // if necessary.  Call RemoveContext on the same thread when done.
      42               0 :   static ReceivedSyncMsgQueue* AddContext() {
      43                 :     // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple
      44                 :     // SyncChannel objects can block the same thread).
      45               0 :     ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get();
      46               0 :     if (!rv) {
      47               0 :       rv = new ReceivedSyncMsgQueue();
      48               0 :       ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv);
      49                 :     }
      50               0 :     rv->listener_count_++;
      51               0 :     return rv;
      52                 :   }
      53                 : 
      54               0 :   ~ReceivedSyncMsgQueue() {
      55               0 :   }
      56                 : 
      57                 :   // Called on IPC thread when a synchronous message or reply arrives.
      58               0 :   void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) {
      59                 :     bool was_task_pending;
      60                 :     {
      61               0 :       AutoLock auto_lock(message_lock_);
      62                 : 
      63               0 :       was_task_pending = task_pending_;
      64               0 :       task_pending_ = true;
      65                 : 
      66                 :       // We set the event in case the listener thread is blocked (or is about
      67                 :       // to). In case it's not, the PostTask dispatches the messages.
      68               0 :       message_queue_.push_back(QueuedMessage(new Message(msg), context));
      69                 :     }
      70                 : 
      71               0 :     dispatch_event_.Signal();
      72               0 :     if (!was_task_pending) {
      73                 :       listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
      74               0 :           this, &ReceivedSyncMsgQueue::DispatchMessagesTask));
      75                 :     }
      76               0 :   }
      77                 : 
      78               0 :   void QueueReply(const Message &msg, SyncChannel::SyncContext* context) {
      79               0 :     received_replies_.push_back(QueuedMessage(new Message(msg), context));
      80               0 :   }
      81                 : 
      82                 :   // Called on the listener's thread to process any queues synchronous
      83                 :   // messages.
      84               0 :   void DispatchMessagesTask() {
      85                 :     {
      86               0 :       AutoLock auto_lock(message_lock_);
      87               0 :       task_pending_ = false;
      88                 :     }
      89               0 :     DispatchMessages();
      90               0 :   }
      91                 : 
      92               0 :   void DispatchMessages() {
      93               0 :     while (true) {
      94                 :       Message* message;
      95               0 :       scoped_refptr<SyncChannel::SyncContext> context;
      96                 :       {
      97               0 :         AutoLock auto_lock(message_lock_);
      98               0 :         if (message_queue_.empty())
      99                 :           break;
     100                 : 
     101               0 :         message = message_queue_.front().message;
     102               0 :         context = message_queue_.front().context;
     103               0 :         message_queue_.pop_front();
     104                 :       }
     105                 : 
     106               0 :       context->OnDispatchMessage(*message);
     107               0 :       delete message;
     108                 :     }
     109               0 :   }
     110                 : 
     111                 :   // SyncChannel calls this in its destructor.
     112               0 :   void RemoveContext(SyncContext* context) {
     113               0 :     AutoLock auto_lock(message_lock_);
     114                 : 
     115               0 :     SyncMessageQueue::iterator iter = message_queue_.begin();
     116               0 :     while (iter != message_queue_.end()) {
     117               0 :       if (iter->context == context) {
     118               0 :         delete iter->message;
     119               0 :         iter = message_queue_.erase(iter);
     120                 :       } else {
     121               0 :         iter++;
     122                 :       }
     123                 :     }
     124                 : 
     125               0 :     if (--listener_count_ == 0) {
     126               0 :       DCHECK(lazy_tls_ptr_.Pointer()->Get());
     127               0 :       lazy_tls_ptr_.Pointer()->Set(NULL);
     128                 :     }
     129               0 :   }
     130                 : 
     131               0 :   WaitableEvent* dispatch_event() { return &dispatch_event_; }
     132                 :   MessageLoop* listener_message_loop() { return listener_message_loop_; }
     133                 : 
     134                 :   // Holds a pointer to the per-thread ReceivedSyncMsgQueue object.
     135                 :   static base::LazyInstance<base::ThreadLocalPointer<ReceivedSyncMsgQueue> >
     136                 :       lazy_tls_ptr_;
     137                 : 
     138                 :   // Called on the ipc thread to check if we can unblock any current Send()
     139                 :   // calls based on a queued reply.
     140               0 :   void DispatchReplies() {
     141               0 :     for (size_t i = 0; i < received_replies_.size(); ++i) {
     142               0 :       Message* message = received_replies_[i].message;
     143               0 :       if (received_replies_[i].context->TryToUnblockListener(message)) {
     144               0 :         delete message;
     145               0 :         received_replies_.erase(received_replies_.begin() + i);
     146               0 :         return;
     147                 :       }
     148                 :     }
     149                 :   }
     150                 : 
     151                 :  private:
     152                 :   // See the comment in SyncChannel::SyncChannel for why this event is created
     153                 :   // as manual reset.
     154               0 :   ReceivedSyncMsgQueue() :
     155                 :       dispatch_event_(true, false),
     156               0 :       listener_message_loop_(MessageLoop::current()),
     157                 :       task_pending_(false),
     158               0 :       listener_count_(0) {
     159               0 :   }
     160                 : 
     161                 :   // Holds information about a queued synchronous message or reply.
     162               0 :   struct QueuedMessage {
     163               0 :     QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { }
     164                 :     Message* message;
     165                 :     scoped_refptr<SyncChannel::SyncContext> context;
     166                 :   };
     167                 : 
     168                 :   typedef std::deque<QueuedMessage> SyncMessageQueue;
     169                 :   SyncMessageQueue message_queue_;
     170                 : 
     171                 :   std::vector<QueuedMessage> received_replies_;
     172                 : 
     173                 :   // Set when we got a synchronous message that we must respond to as the
     174                 :   // sender needs its reply before it can reply to our original synchronous
     175                 :   // message.
     176                 :   WaitableEvent dispatch_event_;
     177                 :   MessageLoop* listener_message_loop_;
     178                 :   Lock message_lock_;
     179                 :   bool task_pending_;
     180                 :   int listener_count_;
     181                 : };
     182                 : 
     183                 : base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> >
     184            1464 :     SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED);
     185                 : 
     186               0 : SyncChannel::SyncContext::SyncContext(
     187                 :     Channel::Listener* listener,
     188                 :     MessageFilter* filter,
     189                 :     MessageLoop* ipc_thread,
     190                 :     WaitableEvent* shutdown_event)
     191                 :     : ChannelProxy::Context(listener, filter, ipc_thread),
     192                 :       received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()),
     193               0 :       shutdown_event_(shutdown_event) {
     194               0 : }
     195                 : 
     196               0 : SyncChannel::SyncContext::~SyncContext() {
     197               0 :   while (!deserializers_.empty())
     198               0 :     Pop();
     199               0 : }
     200                 : 
     201                 : // Adds information about an outgoing sync message to the context so that
     202                 : // we know how to deserialize the reply.  Returns a handle that's set when
     203                 : // the reply has arrived.
     204               0 : void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) {
     205                 :   // The event is created as manual reset because in between Signal and
     206                 :   // OnObjectSignalled, another Send can happen which would stop the watcher
     207                 :   // from being called.  The event would get watched later, when the nested
     208                 :   // Send completes, so the event will need to remain set.
     209                 :   PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg),
     210                 :                          sync_msg->GetReplyDeserializer(),
     211               0 :                          new WaitableEvent(true, false));
     212               0 :   AutoLock auto_lock(deserializers_lock_);
     213               0 :   deserializers_.push_back(pending);
     214               0 : }
     215                 : 
     216               0 : bool SyncChannel::SyncContext::Pop() {
     217                 :   bool result;
     218                 :   {
     219               0 :     AutoLock auto_lock(deserializers_lock_);
     220               0 :     PendingSyncMsg msg = deserializers_.back();
     221               0 :     delete msg.deserializer;
     222               0 :     delete msg.done_event;
     223               0 :     msg.done_event = NULL;
     224               0 :     deserializers_.pop_back();
     225               0 :     result = msg.send_result;
     226                 :   }
     227                 : 
     228                 :   // We got a reply to a synchronous Send() call that's blocking the listener
     229                 :   // thread.  However, further down the call stack there could be another
     230                 :   // blocking Send() call, whose reply we received after we made this last
     231                 :   // Send() call.  So check if we have any queued replies available that
     232                 :   // can now unblock the listener thread.
     233                 :   ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
     234               0 :       received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies));
     235                 : 
     236               0 :   return result;
     237                 : }
     238                 : 
     239               0 : WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
     240               0 :   AutoLock auto_lock(deserializers_lock_);
     241               0 :   return deserializers_.back().done_event;
     242                 : }
     243                 : 
     244               0 : WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
     245               0 :   return received_sync_msgs_->dispatch_event();
     246                 : }
     247                 : 
     248               0 : void SyncChannel::SyncContext::DispatchMessages() {
     249               0 :   received_sync_msgs_->DispatchMessages();
     250               0 : }
     251                 : 
     252               0 : bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
     253               0 :   AutoLock auto_lock(deserializers_lock_);
     254               0 :   if (deserializers_.empty() ||
     255               0 :       !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) {
     256               0 :     return false;
     257                 :   }
     258                 : 
     259               0 :   if (!msg->is_reply_error()) {
     260               0 :     deserializers_.back().send_result = deserializers_.back().deserializer->
     261               0 :         SerializeOutputParameters(*msg);
     262                 :   }
     263               0 :   deserializers_.back().done_event->Signal();
     264                 : 
     265               0 :   return true;
     266                 : }
     267                 : 
     268               0 : void SyncChannel::SyncContext::Clear() {
     269               0 :   CancelPendingSends();
     270               0 :   received_sync_msgs_->RemoveContext(this);
     271                 : 
     272               0 :   Context::Clear();
     273               0 : }
     274                 : 
     275               0 : void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
     276                 :   // Give the filters a chance at processing this message.
     277               0 :   if (TryFilters(msg))
     278               0 :     return;
     279                 : 
     280               0 :   if (TryToUnblockListener(&msg))
     281               0 :     return;
     282                 : 
     283               0 :   if (msg.should_unblock()) {
     284               0 :     received_sync_msgs_->QueueMessage(msg, this);
     285               0 :     return;
     286                 :   }
     287                 : 
     288               0 :   if (msg.is_reply()) {
     289               0 :     received_sync_msgs_->QueueReply(msg, this);
     290               0 :     return;
     291                 :   }
     292                 : 
     293               0 :   return Context::OnMessageReceivedNoFilter(msg);
     294                 : }
     295                 : 
     296               0 : void SyncChannel::SyncContext::OnChannelError() {
     297               0 :   CancelPendingSends();
     298               0 :   shutdown_watcher_.StopWatching();
     299               0 :   Context::OnChannelError();
     300               0 : }
     301                 : 
     302               0 : void SyncChannel::SyncContext::OnChannelOpened() {
     303               0 :   shutdown_watcher_.StartWatching(shutdown_event_, this);
     304               0 :   Context::OnChannelOpened();
     305               0 : }
     306                 : 
     307               0 : void SyncChannel::SyncContext::OnChannelClosed() {
     308               0 :   shutdown_watcher_.StopWatching();
     309               0 :   Context::OnChannelClosed();
     310               0 : }
     311                 : 
     312               0 : void SyncChannel::SyncContext::OnSendTimeout(int message_id) {
     313               0 :   AutoLock auto_lock(deserializers_lock_);
     314               0 :   PendingSyncMessageQueue::iterator iter;
     315               0 :   for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
     316               0 :     if (iter->id == message_id) {
     317               0 :       iter->done_event->Signal();
     318               0 :       break;
     319                 :     }
     320                 :   }
     321               0 : }
     322                 : 
     323               0 : void SyncChannel::SyncContext::CancelPendingSends() {
     324               0 :   AutoLock auto_lock(deserializers_lock_);
     325               0 :   PendingSyncMessageQueue::iterator iter;
     326               0 :   for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++)
     327               0 :     iter->done_event->Signal();
     328               0 : }
     329                 : 
     330               0 : void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) {
     331               0 :   DCHECK(event == shutdown_event_);
     332                 :   // Process shut down before we can get a reply to a synchronous message.
     333                 :   // Cancel pending Send calls, which will end up setting the send done event.
     334               0 :   CancelPendingSends();
     335               0 : }
     336                 : 
     337                 : 
     338               0 : SyncChannel::SyncChannel(
     339                 :     const std::wstring& channel_id, Channel::Mode mode,
     340                 :     Channel::Listener* listener, MessageFilter* filter,
     341                 :     MessageLoop* ipc_message_loop, bool create_pipe_now,
     342                 :     WaitableEvent* shutdown_event)
     343                 :     : ChannelProxy(
     344                 :           channel_id, mode, ipc_message_loop,
     345               0 :           new SyncContext(listener, filter, ipc_message_loop, shutdown_event),
     346                 :           create_pipe_now),
     347               0 :       sync_messages_with_no_timeout_allowed_(true) {
     348                 :   // Ideally we only want to watch this object when running a nested message
     349                 :   // loop.  However, we don't know when it exits if there's another nested
     350                 :   // message loop running under it or not, so we wouldn't know whether to
     351                 :   // stop or keep watching.  So we always watch it, and create the event as
     352                 :   // manual reset since the object watcher might otherwise reset the event
     353                 :   // when we're doing a WaitMany.
     354               0 :   dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this);
     355               0 : }
     356                 : 
     357               0 : SyncChannel::~SyncChannel() {
     358               0 : }
     359                 : 
     360               0 : bool SyncChannel::Send(Message* message) {
     361               0 :   return SendWithTimeout(message, base::kNoTimeout);
     362                 : }
     363                 : 
     364               0 : bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
     365               0 :   if (!message->is_sync()) {
     366               0 :     ChannelProxy::Send(message);
     367               0 :     return true;
     368                 :   }
     369                 : 
     370                 :   // *this* might get deleted in WaitForReply.
     371               0 :   scoped_refptr<SyncContext> context(sync_context());
     372               0 :   if (context->shutdown_event()->IsSignaled()) {
     373               0 :     delete message;
     374               0 :     return false;
     375                 :   }
     376                 : 
     377               0 :   DCHECK(sync_messages_with_no_timeout_allowed_ ||
     378               0 :          timeout_ms != base::kNoTimeout);
     379               0 :   SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
     380               0 :   context->Push(sync_msg);
     381               0 :   int message_id = SyncMessage::GetMessageId(*sync_msg);
     382               0 :   WaitableEvent* pump_messages_event = sync_msg->pump_messages_event();
     383                 : 
     384               0 :   ChannelProxy::Send(message);
     385                 : 
     386               0 :   if (timeout_ms != base::kNoTimeout) {
     387                 :     // We use the sync message id so that when a message times out, we don't
     388                 :     // confuse it with another send that is either above/below this Send in
     389                 :     // the call stack.
     390               0 :     context->ipc_message_loop()->PostDelayedTask(FROM_HERE,
     391                 :         NewRunnableMethod(context.get(),
     392               0 :             &SyncContext::OnSendTimeout, message_id), timeout_ms);
     393                 :   }
     394                 : 
     395                 :   // Wait for reply, or for any other incoming synchronous messages.
     396               0 :   WaitForReply(pump_messages_event);
     397                 : 
     398               0 :   return context->Pop();
     399                 : }
     400                 : 
     401               0 : void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) {
     402               0 :   while (true) {
     403                 :     WaitableEvent* objects[] = {
     404               0 :       sync_context()->GetDispatchEvent(),
     405               0 :       sync_context()->GetSendDoneEvent(),
     406                 :       pump_messages_event
     407               0 :     };
     408                 : 
     409               0 :     unsigned count = pump_messages_event ? 3: 2;
     410               0 :     unsigned result = WaitableEvent::WaitMany(objects, count);
     411               0 :     if (result == 0 /* dispatch event */) {
     412                 :       // We're waiting for a reply, but we received a blocking synchronous
     413                 :       // call.  We must process it or otherwise a deadlock might occur.
     414               0 :       sync_context()->GetDispatchEvent()->Reset();
     415               0 :       sync_context()->DispatchMessages();
     416               0 :       continue;
     417                 :     }
     418                 : 
     419               0 :     if (result == 2 /* pump_messages_event */)
     420               0 :       WaitForReplyWithNestedMessageLoop();  // Start a nested message loop.
     421                 : 
     422                 :     break;
     423                 :   }
     424               0 : }
     425                 : 
     426               0 : void SyncChannel::WaitForReplyWithNestedMessageLoop() {
     427               0 :   WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent();
     428               0 :   send_done_watcher_.StopWatching();
     429               0 :   send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this);
     430               0 :   bool old_state = MessageLoop::current()->NestableTasksAllowed();
     431               0 :   MessageLoop::current()->SetNestableTasksAllowed(true);
     432               0 :   MessageLoop::current()->Run();
     433               0 :   MessageLoop::current()->SetNestableTasksAllowed(old_state);
     434               0 :   if (old_done_event)
     435               0 :     send_done_watcher_.StartWatching(old_done_event, this);
     436               0 : }
     437                 : 
     438               0 : void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
     439               0 :   WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent();
     440               0 :   if (event == dispatch_event) {
     441                 :     // The call to DispatchMessages might delete this object, so reregister
     442                 :     // the object watcher first.
     443               0 :     dispatch_event->Reset();
     444               0 :     dispatch_watcher_.StartWatching(dispatch_event, this);
     445               0 :     sync_context()->DispatchMessages();
     446                 :   } else {
     447                 :     // We got the reply, timed out or the process shutdown.
     448               0 :     DCHECK(event == sync_context()->GetSendDoneEvent());
     449               0 :     MessageLoop::current()->Quit();
     450                 :   }
     451               0 : }
     452                 : 
     453            4392 : }  // namespace IPC

Generated by: LCOV version 1.7