LCOV - code coverage report
Current view: directory - ipc/glue - RPCChannel.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 326 7 2.1 %
Date: 2012-06-02 Functions: 34 3 8.8 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: sw=4 ts=4 et :
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla Plugin App.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  *   Chris Jones <jones.chris.g@gmail.com>
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/ipc/RPCChannel.h"
      41                 : #include "mozilla/ipc/ProtocolUtils.h"
      42                 : 
      43                 : #include "nsDebug.h"
      44                 : #include "nsTraceRefcnt.h"
      45                 : 
      46                 : #define RPC_ASSERT(_cond, ...)                                      \
      47                 :     do {                                                            \
      48                 :         if (!(_cond))                                               \
      49                 :             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
      50                 :     } while (0)
      51                 : 
      52                 : using mozilla::MonitorAutoLock;
      53                 : using mozilla::MonitorAutoUnlock;
      54                 : 
      55                 : template<>
      56                 : struct RunnableMethodTraits<mozilla::ipc::RPCChannel>
      57               1 : {
      58               1 :     static void RetainCallee(mozilla::ipc::RPCChannel* obj) { }
      59               0 :     static void ReleaseCallee(mozilla::ipc::RPCChannel* obj) { }
      60                 : };
      61                 : 
      62                 : 
      63                 : namespace
      64                 : {
      65                 : 
      66                 : // Async (from the sending side's perspective)
      67                 : class BlockChildMessage : public IPC::Message
      68               0 : {
      69                 : public:
      70                 :     enum { ID = BLOCK_CHILD_MESSAGE_TYPE };
      71               0 :     BlockChildMessage() :
      72               0 :         Message(MSG_ROUTING_NONE, ID, IPC::Message::PRIORITY_NORMAL)
      73               0 :     { }
      74                 : };
      75                 : 
      76                 : // Async
      77                 : class UnblockChildMessage : public IPC::Message
      78               0 : {
      79                 : public:
      80                 :     enum { ID = UNBLOCK_CHILD_MESSAGE_TYPE };
      81               0 :     UnblockChildMessage() :
      82               0 :         Message(MSG_ROUTING_NONE, ID, IPC::Message::PRIORITY_NORMAL)
      83               0 :     { }
      84                 : };
      85                 : 
      86                 : } // namespace <anon>
      87                 : 
      88                 : 
      89                 : namespace mozilla {
      90                 : namespace ipc {
      91                 : 
      92               1 : RPCChannel::RPCChannel(RPCListener* aListener)
      93                 :   : SyncChannel(aListener),
      94                 :     mPending(),
      95                 :     mStack(),
      96                 :     mOutOfTurnReplies(),
      97                 :     mDeferred(),
      98                 :     mRemoteStackDepthGuess(0),
      99                 :     mBlockedOnParent(false),
     100               1 :     mSawRPCOutMsg(false)
     101                 : {
     102               1 :     MOZ_COUNT_CTOR(RPCChannel);
     103                 : 
     104                 :     mDequeueOneTask = new RefCountedTask(NewRunnableMethod(
     105                 :                                                  this,
     106               2 :                                                  &RPCChannel::OnMaybeDequeueOne));
     107               1 : }
     108                 : 
     109               0 : RPCChannel::~RPCChannel()
     110                 : {
     111               0 :     MOZ_COUNT_DTOR(RPCChannel);
     112               0 :     RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
     113               0 : }
     114                 : 
     115                 : void
     116               0 : RPCChannel::Clear()
     117                 : {
     118               0 :     mDequeueOneTask->Cancel();
     119                 : 
     120               0 :     AsyncChannel::Clear();
     121               0 : }
     122                 : 
     123                 : bool
     124               0 : RPCChannel::EventOccurred() const
     125                 : {
     126               0 :     AssertWorkerThread();
     127               0 :     mMonitor->AssertCurrentThreadOwns();
     128               0 :     RPC_ASSERT(StackDepth() > 0, "not in wait loop");
     129                 : 
     130               0 :     return (!Connected() ||
     131               0 :             !mPending.empty() ||
     132               0 :             (!mOutOfTurnReplies.empty() &&
     133               0 :              mOutOfTurnReplies.find(mStack.top().seqno())
     134               0 :              != mOutOfTurnReplies.end()));
     135                 : }
     136                 : 
     137                 : bool
     138               0 : RPCChannel::Send(Message* msg)
     139                 : {
     140               0 :     Message copy = *msg;
     141               0 :     CxxStackFrame f(*this, OUT_MESSAGE, &copy);
     142               0 :     return AsyncChannel::Send(msg);
     143                 : }
     144                 : 
     145                 : bool
     146               0 : RPCChannel::Send(Message* msg, Message* reply)
     147                 : {
     148               0 :     Message copy = *msg;
     149               0 :     CxxStackFrame f(*this, OUT_MESSAGE, &copy);
     150               0 :     return SyncChannel::Send(msg, reply);
     151                 : }
     152                 : 
     153                 : bool
     154               0 : RPCChannel::Call(Message* _msg, Message* reply)
     155                 : {
     156               0 :     nsAutoPtr<Message> msg(_msg);
     157               0 :     AssertWorkerThread();
     158               0 :     mMonitor->AssertNotCurrentThreadOwns();
     159               0 :     RPC_ASSERT(!ProcessingSyncMessage(),
     160                 :                "violation of sync handler invariant");
     161               0 :     RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
     162                 : 
     163                 : #ifdef OS_WIN
     164                 :     SyncStackFrame frame(this, true);
     165                 : #endif
     166                 : 
     167               0 :     Message copy = *msg;
     168               0 :     CxxStackFrame f(*this, OUT_MESSAGE, &copy);
     169                 : 
     170               0 :     MonitorAutoLock lock(*mMonitor);
     171                 : 
     172               0 :     if (!Connected()) {
     173               0 :         ReportConnectionError("RPCChannel");
     174               0 :         return false;
     175                 :     }
     176                 : 
     177               0 :     msg->set_seqno(NextSeqno());
     178               0 :     msg->set_rpc_remote_stack_depth_guess(mRemoteStackDepthGuess);
     179               0 :     msg->set_rpc_local_stack_depth(1 + StackDepth());
     180               0 :     mStack.push(*msg);
     181                 : 
     182               0 :     mLink->SendMessage(msg.forget());
     183                 : 
     184               0 :     while (1) {
     185                 :         // if a handler invoked by *Dispatch*() spun a nested event
     186                 :         // loop, and the connection was broken during that loop, we
     187                 :         // might have already processed the OnError event. if so,
     188                 :         // trying another loop iteration will be futile because
     189                 :         // channel state will have been cleared
     190               0 :         if (!Connected()) {
     191               0 :             ReportConnectionError("RPCChannel");
     192               0 :             return false;
     193                 :         }
     194                 : 
     195                 :         // now might be the time to process a message deferred because
     196                 :         // of race resolution
     197               0 :         MaybeUndeferIncall();
     198                 : 
     199                 :         // here we're waiting for something to happen. see long
     200                 :         // comment about the queue in RPCChannel.h
     201               0 :         while (!EventOccurred()) {
     202               0 :             bool maybeTimedOut = !RPCChannel::WaitForNotify();
     203                 : 
     204               0 :             if (EventOccurred() ||
     205                 :                 // we might have received a "subtly deferred" message
     206                 :                 // in a nested loop that it's now time to process
     207               0 :                 (!maybeTimedOut &&
     208               0 :                  (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
     209               0 :                 break;
     210                 : 
     211               0 :             if (maybeTimedOut && !ShouldContinueFromTimeout())
     212               0 :                 return false;
     213                 :         }
     214                 : 
     215               0 :         if (!Connected()) {
     216               0 :             ReportConnectionError("RPCChannel");
     217               0 :             return false;
     218                 :         }
     219                 : 
     220               0 :         Message recvd;
     221               0 :         MessageMap::iterator it;
     222               0 :         if (!mOutOfTurnReplies.empty() &&
     223               0 :             ((it = mOutOfTurnReplies.find(mStack.top().seqno())) !=
     224               0 :             mOutOfTurnReplies.end())) {
     225               0 :             recvd = it->second;
     226               0 :             mOutOfTurnReplies.erase(it);
     227                 :         }
     228               0 :         else if (!mPending.empty()) {
     229               0 :             recvd = mPending.front();
     230               0 :             mPending.pop();
     231                 :         }
     232                 :         else {
     233                 :             // because of subtleties with nested event loops, it's
     234                 :             // possible that we got here and nothing happened.  or, we
     235                 :             // might have a deferred in-call that needs to be
     236                 :             // processed.  either way, we won't break the inner while
     237                 :             // loop again until something new happens.
     238               0 :             continue;
     239                 :         }
     240                 : 
     241               0 :         if (!recvd.is_sync() && !recvd.is_rpc()) {
     242               0 :             MonitorAutoUnlock unlock(*mMonitor);
     243                 : 
     244               0 :             CxxStackFrame f(*this, IN_MESSAGE, &recvd);
     245               0 :             AsyncChannel::OnDispatchMessage(recvd);
     246                 : 
     247               0 :             continue;
     248                 :         }
     249                 : 
     250               0 :         if (recvd.is_sync()) {
     251               0 :             RPC_ASSERT(mPending.empty(),
     252                 :                        "other side should have been blocked");
     253               0 :             MonitorAutoUnlock unlock(*mMonitor);
     254                 : 
     255               0 :             CxxStackFrame f(*this, IN_MESSAGE, &recvd);
     256               0 :             SyncChannel::OnDispatchMessage(recvd);
     257                 : 
     258               0 :             continue;
     259                 :         }
     260                 : 
     261               0 :         RPC_ASSERT(recvd.is_rpc(), "wtf???");
     262                 : 
     263               0 :         if (recvd.is_reply()) {
     264               0 :             RPC_ASSERT(0 < mStack.size(), "invalid RPC stack");
     265                 : 
     266               0 :             const Message& outcall = mStack.top();
     267                 : 
     268                 :             // in the parent, seqno's increase from 0, and in the
     269                 :             // child, they decrease from 0
     270               0 :             if ((!mChild && recvd.seqno() < outcall.seqno()) ||
     271               0 :                 (mChild && recvd.seqno() > outcall.seqno())) {
     272               0 :                 mOutOfTurnReplies[recvd.seqno()] = recvd;
     273               0 :                 continue;
     274                 :             }
     275                 : 
     276                 :             // FIXME/cjones: handle error
     277               0 :             RPC_ASSERT(
     278                 :                 recvd.is_reply_error() ||
     279                 :                 (recvd.type() == (outcall.type()+1) &&
     280                 :                  recvd.seqno() == outcall.seqno()),
     281                 :                 "somebody's misbehavin'", "rpc", true);
     282                 : 
     283                 :             // we received a reply to our most recent outstanding
     284                 :             // call.  pop this frame and return the reply
     285               0 :             mStack.pop();
     286                 : 
     287               0 :             bool isError = recvd.is_reply_error();
     288               0 :             if (!isError) {
     289               0 :                 *reply = recvd;
     290                 :             }
     291                 : 
     292               0 :             if (0 == StackDepth()) {
     293               0 :                 RPC_ASSERT(
     294                 :                     mOutOfTurnReplies.empty(),
     295                 :                     "still have pending replies with no pending out-calls",
     296                 :                     "rpc", true);
     297                 :             }
     298                 : 
     299                 :             // finished with this RPC stack frame
     300               0 :             return !isError;
     301                 :         }
     302                 : 
     303                 :         // in-call.  process in a new stack frame.
     304                 : 
     305                 :         // "snapshot" the current stack depth while we own the Monitor
     306               0 :         size_t stackDepth = StackDepth();
     307                 :         {
     308               0 :             MonitorAutoUnlock unlock(*mMonitor);
     309                 :             // someone called in to us from the other side.  handle the call
     310               0 :             CxxStackFrame f(*this, IN_MESSAGE, &recvd);
     311               0 :             Incall(recvd, stackDepth);
     312                 :             // FIXME/cjones: error handling
     313                 :         }
     314                 :     }
     315                 : 
     316                 :     return true;
     317                 : }
     318                 : 
     319                 : void
     320               0 : RPCChannel::MaybeUndeferIncall()
     321                 : {
     322               0 :     AssertWorkerThread();
     323               0 :     mMonitor->AssertCurrentThreadOwns();
     324                 : 
     325               0 :     if (mDeferred.empty())
     326               0 :         return;
     327                 : 
     328               0 :     size_t stackDepth = StackDepth();
     329                 : 
     330                 :     // the other side can only *under*-estimate our actual stack depth
     331               0 :     RPC_ASSERT(mDeferred.top().rpc_remote_stack_depth_guess() <= stackDepth,
     332                 :                "fatal logic error");
     333                 : 
     334               0 :     if (mDeferred.top().rpc_remote_stack_depth_guess() < RemoteViewOfStackDepth(stackDepth))
     335               0 :         return;
     336                 : 
     337                 :     // maybe time to process this message
     338               0 :     Message call = mDeferred.top();
     339               0 :     mDeferred.pop();
     340                 : 
     341                 :     // fix up fudge factor we added to account for race
     342               0 :     RPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
     343               0 :     --mRemoteStackDepthGuess;
     344                 : 
     345               0 :     mPending.push(call);
     346                 : }
     347                 : 
     348                 : void
     349               0 : RPCChannel::EnqueuePendingMessages()
     350                 : {
     351               0 :     AssertWorkerThread();
     352               0 :     mMonitor->AssertCurrentThreadOwns();
     353                 : 
     354               0 :     MaybeUndeferIncall();
     355                 : 
     356               0 :     for (size_t i = 0; i < mDeferred.size(); ++i)
     357                 :         mWorkerLoop->PostTask(
     358                 :             FROM_HERE,
     359               0 :             new DequeueTask(mDequeueOneTask));
     360                 : 
     361                 :     // XXX performance tuning knob: could process all or k pending
     362                 :     // messages here, rather than enqueuing for later processing
     363                 : 
     364               0 :     for (size_t i = 0; i < mPending.size(); ++i)
     365                 :         mWorkerLoop->PostTask(
     366                 :             FROM_HERE,
     367               0 :             new DequeueTask(mDequeueOneTask));
     368               0 : }
     369                 : 
     370                 : void
     371               0 : RPCChannel::FlushPendingRPCQueue()
     372                 : {
     373               0 :     AssertWorkerThread();
     374               0 :     mMonitor->AssertNotCurrentThreadOwns();
     375                 : 
     376                 :     {
     377               0 :         MonitorAutoLock lock(*mMonitor);
     378                 : 
     379               0 :         if (mDeferred.empty()) {
     380               0 :             if (mPending.empty())
     381                 :                 return;
     382                 : 
     383               0 :             const Message& last = mPending.back();
     384               0 :             if (!last.is_rpc() || last.is_reply())
     385                 :                 return;
     386                 :         }
     387                 :     }
     388                 : 
     389               0 :     while (OnMaybeDequeueOne());
     390                 : }
     391                 : 
     392                 : bool
     393               0 : RPCChannel::OnMaybeDequeueOne()
     394                 : {
     395                 :     // XXX performance tuning knob: could process all or k pending
     396                 :     // messages here
     397                 : 
     398               0 :     AssertWorkerThread();
     399               0 :     mMonitor->AssertNotCurrentThreadOwns();
     400                 : 
     401               0 :     Message recvd;
     402                 :     {
     403               0 :         MonitorAutoLock lock(*mMonitor);
     404                 : 
     405               0 :         if (!Connected()) {
     406               0 :             ReportConnectionError("RPCChannel");
     407               0 :             return false;
     408                 :         }
     409                 : 
     410               0 :         if (!mDeferred.empty())
     411               0 :             MaybeUndeferIncall();
     412                 : 
     413               0 :         if (mPending.empty())
     414               0 :             return false;
     415                 : 
     416               0 :         recvd = mPending.front();
     417               0 :         mPending.pop();
     418                 :     }
     419                 : 
     420               0 :     if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) {
     421                 :         // We probably just received a reply in a nested loop for an
     422                 :         // RPC call sent before entering that loop.
     423               0 :         mOutOfTurnReplies[recvd.seqno()] = recvd;
     424               0 :         return false;
     425                 :     }
     426                 : 
     427               0 :     CxxStackFrame f(*this, IN_MESSAGE, &recvd);
     428                 : 
     429               0 :     if (recvd.is_rpc())
     430               0 :         Incall(recvd, 0);
     431               0 :     else if (recvd.is_sync())
     432               0 :         SyncChannel::OnDispatchMessage(recvd);
     433                 :     else
     434               0 :         AsyncChannel::OnDispatchMessage(recvd);
     435                 : 
     436               0 :     return true;
     437                 : }
     438                 : 
     439                 : size_t
     440               0 : RPCChannel::RemoteViewOfStackDepth(size_t stackDepth) const
     441                 : {
     442               0 :     AssertWorkerThread();
     443               0 :     return stackDepth - mOutOfTurnReplies.size();
     444                 : }
     445                 : 
     446                 : void
     447               0 : RPCChannel::Incall(const Message& call, size_t stackDepth)
     448                 : {
     449               0 :     AssertWorkerThread();
     450               0 :     mMonitor->AssertNotCurrentThreadOwns();
     451               0 :     RPC_ASSERT(call.is_rpc() && !call.is_reply(), "wrong message type");
     452                 : 
     453                 :     // Race detection: see the long comment near
     454                 :     // mRemoteStackDepthGuess in RPCChannel.h.  "Remote" stack depth
     455                 :     // means our side, and "local" means other side.
     456               0 :     if (call.rpc_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) {
     457                 :         // RPC in-calls have raced.
     458                 :         // the "winner", if there is one, gets to defer processing of
     459                 :         // the other side's in-call
     460                 :         bool defer;
     461                 :         const char* winner;
     462               0 :         switch (Listener()->MediateRPCRace(mChild ? call : mStack.top(),
     463               0 :                                            mChild ? mStack.top() : call)) {
     464                 :         case RRPChildWins:
     465               0 :             winner = "child";
     466               0 :             defer = mChild;
     467               0 :             break;
     468                 :         case RRPParentWins:
     469               0 :             winner = "parent";
     470               0 :             defer = !mChild;
     471               0 :             break;
     472                 :         case RRPError:
     473               0 :             NS_RUNTIMEABORT("NYI: 'Error' RPC race policy");
     474               0 :             return;
     475                 :         default:
     476               0 :             NS_RUNTIMEABORT("not reached");
     477               0 :             return;
     478                 :         }
     479                 : 
     480               0 :         if (LoggingEnabled()) {
     481                 :             fprintf(stderr, "  (%s: %s won, so we're%sdeferring)\n",
     482               0 :                     mChild ? "child" : "parent", winner, defer ? " " : " not ");
     483                 :         }
     484                 : 
     485               0 :         if (defer) {
     486                 :             // we now know the other side's stack has one more frame
     487                 :             // than we thought
     488               0 :             ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
     489               0 :             mDeferred.push(call);
     490               0 :             return;
     491                 :         }
     492                 : 
     493                 :         // we "lost" and need to process the other side's in-call.
     494                 :         // don't need to fix up the mRemoteStackDepthGuess here,
     495                 :         // because we're just about to increment it in DispatchCall(),
     496                 :         // which will make it correct again
     497                 :     }
     498                 : 
     499                 : #ifdef OS_WIN
     500                 :     SyncStackFrame frame(this, true);
     501                 : #endif
     502                 : 
     503               0 :     DispatchIncall(call);
     504                 : }
     505                 : 
     506                 : void
     507               0 : RPCChannel::DispatchIncall(const Message& call)
     508                 : {
     509               0 :     AssertWorkerThread();
     510               0 :     mMonitor->AssertNotCurrentThreadOwns();
     511               0 :     RPC_ASSERT(call.is_rpc() && !call.is_reply(),
     512                 :                "wrong message type");
     513                 : 
     514               0 :     Message* reply = nsnull;
     515                 : 
     516               0 :     ++mRemoteStackDepthGuess;
     517               0 :     Result rv = Listener()->OnCallReceived(call, reply);
     518               0 :     --mRemoteStackDepthGuess;
     519                 : 
     520               0 :     if (!MaybeHandleError(rv, "RPCChannel")) {
     521               0 :         delete reply;
     522               0 :         reply = new Message();
     523               0 :         reply->set_rpc();
     524               0 :         reply->set_reply();
     525               0 :         reply->set_reply_error();
     526                 :     }
     527                 : 
     528               0 :     reply->set_seqno(call.seqno());
     529                 : 
     530                 :     {
     531               0 :         MonitorAutoLock lock(*mMonitor);
     532               0 :         if (ChannelConnected == mChannelState)
     533               0 :             mLink->SendMessage(reply);
     534                 :     }
     535               0 : }
     536                 : 
     537                 : bool
     538               0 : RPCChannel::BlockChild()
     539                 : {
     540               0 :     AssertWorkerThread();
     541                 : 
     542               0 :     if (mChild)
     543               0 :         NS_RUNTIMEABORT("child tried to block parent");
     544                 : 
     545               0 :     MonitorAutoLock lock(*mMonitor);
     546               0 :     SendSpecialMessage(new BlockChildMessage());
     547               0 :     return true;
     548                 : }
     549                 : 
     550                 : bool
     551               0 : RPCChannel::UnblockChild()
     552                 : {
     553               0 :     AssertWorkerThread();
     554                 : 
     555               0 :     if (mChild)
     556               0 :         NS_RUNTIMEABORT("child tried to unblock parent");
     557                 : 
     558               0 :     MonitorAutoLock lock(*mMonitor);
     559               0 :     SendSpecialMessage(new UnblockChildMessage());
     560               0 :     return true;
     561                 : }
     562                 : 
     563                 : bool
     564               0 : RPCChannel::OnSpecialMessage(uint16 id, const Message& msg)
     565                 : {
     566               0 :     AssertWorkerThread();
     567                 : 
     568               0 :     switch (id) {
     569                 :     case BLOCK_CHILD_MESSAGE_TYPE:
     570               0 :         BlockOnParent();
     571               0 :         return true;
     572                 : 
     573                 :     case UNBLOCK_CHILD_MESSAGE_TYPE:
     574               0 :         UnblockFromParent();
     575               0 :         return true;
     576                 : 
     577                 :     default:
     578               0 :         return SyncChannel::OnSpecialMessage(id, msg);
     579                 :     }
     580                 : }
     581                 : 
     582                 : void
     583               0 : RPCChannel::BlockOnParent()
     584                 : {
     585               0 :     AssertWorkerThread();
     586                 : 
     587               0 :     if (!mChild)
     588               0 :         NS_RUNTIMEABORT("child tried to block parent");
     589                 : 
     590               0 :     MonitorAutoLock lock(*mMonitor);
     591                 : 
     592               0 :     if (mBlockedOnParent || AwaitingSyncReply() || 0 < StackDepth())
     593               0 :         NS_RUNTIMEABORT("attempt to block child when it's already blocked");
     594                 : 
     595               0 :     mBlockedOnParent = true;
     596               0 :     do {
     597                 :         // XXX this dispatch loop shares some similarities with the
     598                 :         // one in Call(), but the logic is simpler and different
     599                 :         // enough IMHO to warrant its own dispatch loop
     600               0 :         while (Connected() && mPending.empty() && mBlockedOnParent) {
     601               0 :             WaitForNotify();
     602                 :         }
     603                 : 
     604               0 :         if (!Connected()) {
     605               0 :             mBlockedOnParent = false;
     606               0 :             ReportConnectionError("RPCChannel");
     607               0 :             break;
     608                 :         }
     609                 : 
     610               0 :         if (!mPending.empty()) {
     611               0 :             Message recvd = mPending.front();
     612               0 :             mPending.pop();
     613                 : 
     614               0 :             MonitorAutoUnlock unlock(*mMonitor);
     615                 : 
     616               0 :             CxxStackFrame f(*this, IN_MESSAGE, &recvd);
     617               0 :             if (recvd.is_rpc()) {
     618                 :                 // stack depth must be 0 here
     619               0 :                 Incall(recvd, 0);
     620                 :             }
     621               0 :             else if (recvd.is_sync()) {
     622               0 :                 SyncChannel::OnDispatchMessage(recvd);
     623                 :             }
     624                 :             else {
     625               0 :                 AsyncChannel::OnDispatchMessage(recvd);
     626                 :             }
     627                 :         }
     628                 :     } while (mBlockedOnParent);
     629                 : 
     630               0 :     EnqueuePendingMessages();
     631               0 : }
     632                 : 
     633                 : void
     634               0 : RPCChannel::UnblockFromParent()
     635                 : {
     636               0 :     AssertWorkerThread();
     637                 : 
     638               0 :     if (!mChild)
     639               0 :         NS_RUNTIMEABORT("child tried to block parent");
     640               0 :     MonitorAutoLock lock(*mMonitor);
     641               0 :     mBlockedOnParent = false;
     642               0 : }
     643                 : 
     644                 : void
     645               0 : RPCChannel::ExitedCxxStack()
     646                 : {
     647               0 :     Listener()->OnExitedCxxStack();
     648               0 :     if (mSawRPCOutMsg) {
     649               0 :         MonitorAutoLock lock(*mMonitor);
     650                 :         // see long comment in OnMaybeDequeueOne()
     651               0 :         EnqueuePendingMessages();
     652               0 :         mSawRPCOutMsg = false;
     653                 :     }
     654               0 : }
     655                 : 
     656                 : void
     657               0 : RPCChannel::DebugAbort(const char* file, int line, const char* cond,
     658                 :                        const char* why,
     659                 :                        const char* type, bool reply) const
     660                 : {
     661                 :     fprintf(stderr,
     662                 :             "###!!! [RPCChannel][%s][%s:%d] "
     663                 :             "Assertion (%s) failed.  %s (triggered by %s%s)\n",
     664                 :             mChild ? "Child" : "Parent",
     665                 :             file, line, cond,
     666                 :             why,
     667               0 :             type, reply ? "reply" : "");
     668                 :     // technically we need the mutex for this, but we're dying anyway
     669               0 :     DumpRPCStack(stderr, "  ");
     670                 :     fprintf(stderr, "  remote RPC stack guess: %lu\n",
     671               0 :             mRemoteStackDepthGuess);
     672                 :     fprintf(stderr, "  deferred stack size: %lu\n",
     673               0 :             mDeferred.size());
     674                 :     fprintf(stderr, "  out-of-turn RPC replies stack size: %lu\n",
     675               0 :             mOutOfTurnReplies.size());
     676                 :     fprintf(stderr, "  Pending queue size: %lu, front to back:\n",
     677               0 :             mPending.size());
     678                 : 
     679               0 :     MessageQueue pending = mPending;
     680               0 :     while (!pending.empty()) {
     681                 :         fprintf(stderr, "    [ %s%s ]\n",
     682               0 :                 pending.front().is_rpc() ? "rpc" :
     683               0 :                 (pending.front().is_sync() ? "sync" : "async"),
     684               0 :                 pending.front().is_reply() ? "reply" : "");
     685               0 :         pending.pop();
     686                 :     }
     687                 : 
     688               0 :     NS_RUNTIMEABORT(why);
     689               0 : }
     690                 : 
     691                 : void
     692               0 : RPCChannel::DumpRPCStack(FILE* outfile, const char* const pfx) const
     693                 : {
     694               0 :     NS_WARN_IF_FALSE(MessageLoop::current() != mWorkerLoop,
     695                 :                      "The worker thread had better be paused in a debugger!");
     696                 : 
     697               0 :     if (!outfile)
     698               0 :         outfile = stdout;
     699                 : 
     700               0 :     fprintf(outfile, "%sRPCChannel 'backtrace':\n", pfx);
     701                 : 
     702                 :     // print a python-style backtrace, first frame to last
     703               0 :     for (PRUint32 i = 0; i < mCxxStackFrames.size(); ++i) {
     704                 :         int32 id;
     705                 :         const char* dir, *sems, *name;
     706               0 :         mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
     707                 : 
     708                 :         fprintf(outfile, "%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
     709               0 :                 i, dir, sems, name, id);
     710                 :     }
     711               0 : }
     712                 : 
     713                 : //
     714                 : // The methods below run in the context of the link thread, and can proxy
     715                 : // back to the methods above
     716                 : //
     717                 : 
     718                 : void
     719               0 : RPCChannel::OnMessageReceivedFromLink(const Message& msg)
     720                 : {
     721               0 :     AssertLinkThread();
     722               0 :     mMonitor->AssertCurrentThreadOwns();
     723                 : 
     724               0 :     if (MaybeInterceptSpecialIOMessage(msg))
     725               0 :         return;
     726                 : 
     727                 :     // regardless of the RPC stack, if we're awaiting a sync reply, we
     728                 :     // know that it needs to be immediately handled to unblock us.
     729               0 :     if (AwaitingSyncReply() && msg.is_sync()) {
     730                 :         // wake up worker thread waiting at SyncChannel::Send
     731               0 :         mRecvd = msg;
     732               0 :         NotifyWorkerThread();
     733               0 :         return;
     734                 :     }
     735                 : 
     736               0 :     mPending.push(msg);
     737                 : 
     738               0 :     if (0 == StackDepth() && !mBlockedOnParent) {
     739                 :         // the worker thread might be idle, make sure it wakes up
     740                 :         mWorkerLoop->PostTask(FROM_HERE,
     741               0 :                                      new DequeueTask(mDequeueOneTask));
     742                 :     }
     743               0 :     else if (!AwaitingSyncReply())
     744               0 :         NotifyWorkerThread();
     745                 : }
     746                 : 
     747                 : 
     748                 : void
     749               0 : RPCChannel::OnChannelErrorFromLink()
     750                 : {
     751               0 :     AssertLinkThread();
     752               0 :     mMonitor->AssertCurrentThreadOwns();
     753                 : 
     754               0 :     if (0 < StackDepth())
     755               0 :         NotifyWorkerThread();
     756                 : 
     757               0 :     SyncChannel::OnChannelErrorFromLink();
     758               0 : }
     759                 : 
     760                 : } // namespace ipc
     761                 : } // namespace mozilla
     762                 : 

Generated by: LCOV version 1.7