LCOV - code coverage report
Current view: directory - objdir/dist/include/mozilla/ipc - RPCChannel.h (source / functions) Found Hit Coverage
Test: app.info Lines: 88 7 8.0 %
Date: 2012-06-02 Functions: 34 4 11.8 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: sw=4 ts=4 et :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla Plugin App.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  *   Chris Jones <jones.chris.g@gmail.com>
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #ifndef ipc_glue_RPCChannel_h
      40                 : #define ipc_glue_RPCChannel_h 1
      41                 : 
      42                 : #include <stdio.h>
      43                 : 
      44                 : // FIXME/cjones probably shouldn't depend on STL
      45                 : #include <queue>
      46                 : #include <stack>
      47                 : #include <vector>
      48                 : 
      49                 : #include "base/basictypes.h"
      50                 : 
      51                 : #include "nsAtomicRefcnt.h"
      52                 : 
      53                 : #include "mozilla/ipc/SyncChannel.h"
      54                 : #include "nsAutoPtr.h"
      55                 : 
      56                 : namespace mozilla {
      57                 : namespace ipc {
      58                 : //-----------------------------------------------------------------------------
      59                 : 
      60                 : class RPCChannel : public SyncChannel
      61                 : {
      62                 :     friend class CxxStackFrame;
      63                 : 
      64                 : public:
      65                 :     // What happens if RPC calls race?
      66                 :     enum RacyRPCPolicy {
      67                 :         RRPError,
      68                 :         RRPChildWins,
      69                 :         RRPParentWins
      70                 :     };
      71                 : 
      72                 :     class /*NS_INTERFACE_CLASS*/ RPCListener :
      73                 :         public SyncChannel::SyncListener
      74               2 :     {
      75                 :     public:
      76               2 :         virtual ~RPCListener() { }
      77                 : 
      78                 :         virtual void OnChannelClose() = 0;
      79                 :         virtual void OnChannelError() = 0;
      80                 :         virtual Result OnMessageReceived(const Message& aMessage) = 0;
      81                 :         virtual void OnProcessingError(Result aError) = 0;
      82                 :         virtual bool OnReplyTimeout() = 0;
      83                 :         virtual Result OnMessageReceived(const Message& aMessage,
      84                 :                                          Message*& aReply) = 0;
      85                 :         virtual Result OnCallReceived(const Message& aMessage,
      86                 :                                       Message*& aReply) = 0;
      87               0 :         virtual void OnChannelConnected(int32 peer_pid) {};
      88                 : 
      89               0 :         virtual void OnEnteredCxxStack()
      90                 :         {
      91               0 :             NS_RUNTIMEABORT("default impl shouldn't be invoked");
      92               0 :         }
      93                 : 
      94               0 :         virtual void OnExitedCxxStack()
      95                 :         {
      96               0 :             NS_RUNTIMEABORT("default impl shouldn't be invoked");
      97               0 :         }
      98                 : 
      99               0 :         virtual void OnEnteredCall()
     100                 :         {
     101               0 :             NS_RUNTIMEABORT("default impl shouldn't be invoked");
     102               0 :         }
     103                 : 
     104               0 :         virtual void OnExitedCall()
     105                 :         {
     106               0 :             NS_RUNTIMEABORT("default impl shouldn't be invoked");
     107               0 :         }
     108                 : 
     109               0 :         virtual RacyRPCPolicy MediateRPCRace(const Message& parent,
     110                 :                                              const Message& child)
     111                 :         {
     112               0 :             return RRPChildWins;
     113                 :         }
     114               0 :         virtual void ProcessRemoteNativeEventsInRPCCall() {};
     115                 :     };
     116                 : 
     117                 :     RPCChannel(RPCListener* aListener);
     118                 : 
     119                 :     virtual ~RPCChannel();
     120                 : 
     121                 :     NS_OVERRIDE
     122                 :     void Clear();
     123                 : 
     124                 :     // Make an RPC to the other side of the channel
     125                 :     bool Call(Message* msg, Message* reply);
     126                 : 
     127                 :     // RPCChannel overrides these so that the async and sync messages
     128                 :     // can be counted against mStackFrames
     129                 :     NS_OVERRIDE
     130                 :     virtual bool Send(Message* msg);
     131                 :     NS_OVERRIDE
     132                 :     virtual bool Send(Message* msg, Message* reply);
     133                 : 
     134                 :     // Asynchronously, send the child a message that puts it in such a
     135                 :     // state that it can't send messages to the parent unless the
     136                 :     // parent sends a message to it first.  The child stays in this
     137                 :     // state until the parent calls |UnblockChild()|.
     138                 :     //
     139                 :     // It is an error to
     140                 :     //  - call this on the child side of the channel.
     141                 :     //  - nest |BlockChild()| calls
     142                 :     //  - call this when the child is already blocked on a sync or RPC
     143                 :     //    in-/out- message/call
     144                 :     //
     145                 :     // Return true iff successful.
     146                 :     bool BlockChild();
     147                 : 
     148                 :     // Asynchronously undo |BlockChild()|.
     149                 :     //
     150                 :     // It is an error to
     151                 :     //  - call this on the child side of the channel
     152                 :     //  - call this without a matching |BlockChild()|
     153                 :     //
     154                 :     // Return true iff successful.
     155                 :     bool UnblockChild();
     156                 : 
     157                 :     // Return true iff this has code on the C++ stack.
     158               0 :     bool IsOnCxxStack() const {
     159               0 :         return !mCxxStackFrames.empty();
     160                 :     }
     161                 : 
     162                 :     NS_OVERRIDE
     163                 :     virtual bool OnSpecialMessage(uint16 id, const Message& msg);
     164                 : 
     165                 : 
     166                 :     /**
     167                 :      * If there is a pending RPC message, process all pending messages.
     168                 :      *
     169                 :      * @note This method is used on Windows when we detect that an outbound
     170                 :      * OLE RPC call is being made to unblock the parent.
     171                 :      */
     172                 :     void FlushPendingRPCQueue();
     173                 : 
     174                 : #ifdef OS_WIN
     175                 :     void ProcessNativeEventsInRPCCall();
     176                 :     static void NotifyGeckoEventDispatch();
     177                 : 
     178                 : protected:
     179                 :     bool WaitForNotify();
     180                 :     void SpinInternalEventLoop();
     181                 : #endif
     182                 : 
     183                 : protected:
     184                 :     NS_OVERRIDE virtual void OnMessageReceivedFromLink(const Message& msg);
     185                 :     NS_OVERRIDE virtual void OnChannelErrorFromLink();
     186                 : 
     187                 : private:
     188                 :     // Called on worker thread only
     189                 : 
     190               0 :     RPCListener* Listener() const {
     191               0 :         return static_cast<RPCListener*>(mListener);
     192                 :     }
     193                 : 
     194                 :     NS_OVERRIDE
     195               0 :     virtual bool ShouldDeferNotifyMaybeError() const {
     196               0 :         return IsOnCxxStack();
     197                 :     }
     198                 : 
     199                 :     bool EventOccurred() const;
     200                 : 
     201                 :     void MaybeUndeferIncall();
     202                 :     void EnqueuePendingMessages();
     203                 : 
     204                 :     /**
     205                 :      * Process one deferred or pending message.
     206                 :      * @return true if a message was processed
     207                 :      */
     208                 :     bool OnMaybeDequeueOne();
     209                 : 
     210                 :     /**
     211                 :      * The "remote view of stack depth" can be different than the
     212                 :      * actual stack depth when there are out-of-turn replies.  When we
     213                 :      * receive one, our actual RPC stack depth doesn't decrease, but
     214                 :      * the other side (that sent the reply) thinks it has.  So, the
     215                 :      * "view" returned here is |stackDepth| minus the number of
     216                 :      * out-of-turn replies.
     217                 :      *
     218                 :      * Only called from the worker thread.
     219                 :      */
     220                 :     size_t RemoteViewOfStackDepth(size_t stackDepth) const;
     221                 : 
     222                 :     void Incall(const Message& call, size_t stackDepth);
     223                 :     void DispatchIncall(const Message& call);
     224                 : 
     225                 :     void BlockOnParent();
     226                 :     void UnblockFromParent();
     227                 : 
     228                 :     // This helper class managed RPCChannel.mCxxStackDepth on behalf
     229                 :     // of RPCChannel.  When the stack depth is incremented from zero
     230                 :     // to non-zero, it invokes an RPCChannel callback, and similarly
     231                 :     // for when the depth goes from non-zero to zero;
     232               0 :     void EnteredCxxStack()
     233                 :     {
     234               0 :         Listener()->OnEnteredCxxStack();
     235               0 :     }
     236                 : 
     237                 :     void ExitedCxxStack();
     238                 : 
     239               0 :     void EnteredCall()
     240                 :     {
     241               0 :         Listener()->OnEnteredCall();
     242               0 :     }
     243                 : 
     244               0 :     void ExitedCall()
     245                 :     {
     246               0 :         Listener()->OnExitedCall();
     247               0 :     }
     248                 : 
     249                 :     enum Direction { IN_MESSAGE, OUT_MESSAGE };
     250               0 :     struct RPCFrame {
     251               0 :         RPCFrame(Direction direction, const Message* msg) :
     252               0 :             mDirection(direction), mMsg(msg)
     253               0 :         { }
     254                 : 
     255               0 :         bool IsRPCIncall() const
     256                 :         {
     257               0 :             return mMsg->is_rpc() && IN_MESSAGE == mDirection;
     258                 :         }
     259                 : 
     260               0 :         bool IsRPCOutcall() const
     261                 :         {
     262               0 :             return mMsg->is_rpc() && OUT_MESSAGE == mDirection;
     263                 :         }
     264                 : 
     265               0 :         void Describe(int32* id, const char** dir, const char** sems,
     266                 :                       const char** name) const
     267                 :         {
     268               0 :             *id = mMsg->routing_id();
     269               0 :             *dir = (IN_MESSAGE == mDirection) ? "in" : "out";
     270               0 :             *sems = mMsg->is_rpc() ? "rpc" : mMsg->is_sync() ? "sync" : "async";
     271               0 :             *name = mMsg->name();
     272               0 :         }
     273                 : 
     274                 :         Direction mDirection;
     275                 :         const Message* mMsg;
     276                 :     };
     277                 : 
     278                 :     class NS_STACK_CLASS CxxStackFrame
     279                 :     {
     280                 :     public:
     281                 : 
     282               0 :         CxxStackFrame(RPCChannel& that, Direction direction,
     283               0 :                       const Message* msg) : mThat(that) {
     284               0 :             mThat.AssertWorkerThread();
     285                 : 
     286               0 :             if (mThat.mCxxStackFrames.empty())
     287               0 :                 mThat.EnteredCxxStack();
     288                 : 
     289               0 :             mThat.mCxxStackFrames.push_back(RPCFrame(direction, msg));
     290               0 :             const RPCFrame& frame = mThat.mCxxStackFrames.back();
     291                 : 
     292               0 :             if (frame.IsRPCIncall())
     293               0 :                 mThat.EnteredCall();
     294                 : 
     295               0 :             mThat.mSawRPCOutMsg |= frame.IsRPCOutcall();
     296               0 :         }
     297                 : 
     298               0 :         ~CxxStackFrame() {
     299               0 :             bool exitingCall = mThat.mCxxStackFrames.back().IsRPCIncall();
     300               0 :             mThat.mCxxStackFrames.pop_back();
     301               0 :             bool exitingStack = mThat.mCxxStackFrames.empty();
     302                 : 
     303                 :             // mListener could have gone away if Close() was called while
     304                 :             // RPCChannel code was still on the stack
     305               0 :             if (!mThat.mListener)
     306               0 :                 return;
     307                 : 
     308               0 :             mThat.AssertWorkerThread();
     309               0 :             if (exitingCall)
     310               0 :                 mThat.ExitedCall();
     311                 : 
     312               0 :             if (exitingStack)
     313               0 :                 mThat.ExitedCxxStack();
     314               0 :         }
     315                 :     private:
     316                 :         RPCChannel& mThat;
     317                 : 
     318                 :         // disable harmful methods
     319                 :         CxxStackFrame();
     320                 :         CxxStackFrame(const CxxStackFrame&);
     321                 :         CxxStackFrame& operator=(const CxxStackFrame&);
     322                 :     };
     323                 : 
     324                 :     // Called from both threads
     325               0 :     size_t StackDepth() const {
     326               0 :         mMonitor->AssertCurrentThreadOwns();
     327               0 :         return mStack.size();
     328                 :     }
     329                 : 
     330                 :     void DebugAbort(const char* file, int line, const char* cond,
     331                 :                     const char* why,
     332                 :                     const char* type="rpc", bool reply=false) const;
     333                 : 
     334                 :     // This method is only safe to call on the worker thread, or in a
     335                 :     // debugger with all threads paused.  |outfile| defaults to stdout.
     336                 :     void DumpRPCStack(FILE* outfile=NULL, const char* const pfx="") const;
     337                 : 
     338                 :     // 
     339                 :     // Queue of all incoming messages, except for replies to sync
     340                 :     // messages, which are delivered directly to the SyncChannel
     341                 :     // through its mRecvd member.
     342                 :     //
     343                 :     // If both this side and the other side are functioning correctly,
     344                 :     // the queue can only be in certain configurations.  Let
     345                 :     // 
     346                 :     //   |A<| be an async in-message,
     347                 :     //   |S<| be a sync in-message,
     348                 :     //   |C<| be an RPC in-call,
     349                 :     //   |R<| be an RPC reply.
     350                 :     // 
     351                 :     // The queue can only match this configuration
     352                 :     // 
     353                 :     //  A<* (S< | C< | R< (?{mStack.size() == 1} A<* (S< | C<)))
     354                 :     //
     355                 :     // The other side can send as many async messages |A<*| as it
     356                 :     // wants before sending us a blocking message.
     357                 :     //
     358                 :     // The first case is |S<|, a sync in-msg.  The other side must be
     359                 :     // blocked, and thus can't send us any more messages until we
     360                 :     // process the sync in-msg.
     361                 :     //
     362                 :     // The second case is |C<|, an RPC in-call; the other side must be
     363                 :     // blocked.  (There's a subtlety here: this in-call might have
     364                 :     // raced with an out-call, but we detect that with the mechanism
     365                 :     // below, |mRemoteStackDepth|, and races don't matter to the
     366                 :     // queue.)
     367                 :     //
     368                 :     // Final case, the other side replied to our most recent out-call
     369                 :     // |R<|.  If that was the *only* out-call on our stack,
     370                 :     // |?{mStack.size() == 1}|, then other side "finished with us,"
     371                 :     // and went back to its own business.  That business might have
     372                 :     // included sending any number of async message |A<*| until
     373                 :     // sending a blocking message |(S< | C<)|.  If we had more than
     374                 :     // one RPC call on our stack, the other side *better* not have
     375                 :     // sent us another blocking message, because it's blocked on a
     376                 :     // reply from us.
     377                 :     //
     378                 :     typedef std::queue<Message> MessageQueue;
     379                 :     MessageQueue mPending;
     380                 : 
     381                 :     // 
     382                 :     // Stack of all the RPC out-calls on which this RPCChannel is
     383                 :     // awaiting a response.
     384                 :     //
     385                 :     std::stack<Message> mStack;
     386                 : 
     387                 :     //
     388                 :     // Map of replies received "out of turn", because of RPC
     389                 :     // in-calls racing with replies to outstanding in-calls.  See
     390                 :     // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
     391                 :     //
     392                 :     typedef std::map<size_t, Message> MessageMap;
     393                 :     MessageMap mOutOfTurnReplies;
     394                 : 
     395                 :     //
     396                 :     // Stack of RPC in-calls that were deferred because of race
     397                 :     // conditions.
     398                 :     //
     399                 :     std::stack<Message> mDeferred;
     400                 : 
     401                 :     //
     402                 :     // This is what we think the RPC stack depth is on the "other
     403                 :     // side" of this RPC channel.  We maintain this variable so that
     404                 :     // we can detect racy RPC calls.  With each RPC out-call sent, we
     405                 :     // send along what *we* think the stack depth of the remote side
     406                 :     // is *before* it will receive the RPC call.
     407                 :     //
     408                 :     // After sending the out-call, our stack depth is "incremented"
     409                 :     // by pushing that pending message onto mPending.
     410                 :     //
     411                 :     // Then when processing an in-call |c|, it must be true that
     412                 :     //
     413                 :     //   mStack.size() == c.remoteDepth
     414                 :     //
     415                 :     // i.e., my depth is actually the same as what the other side
     416                 :     // thought it was when it sent in-call |c|.  If this fails to
     417                 :     // hold, we have detected racy RPC calls.
     418                 :     //
     419                 :     // We then increment mRemoteStackDepth *just before* processing
     420                 :     // the in-call, since we know the other side is waiting on it, and
     421                 :     // decrement it *just after* finishing processing that in-call,
     422                 :     // since our response will pop the top of the other side's
     423                 :     // |mPending|.
     424                 :     //
     425                 :     // One nice aspect of this race detection is that it is symmetric;
     426                 :     // if one side detects a race, then the other side must also 
     427                 :     // detect the same race.
     428                 :     //
     429                 :     size_t mRemoteStackDepthGuess;
     430                 : 
     431                 :     // True iff the parent has put us in a |BlockChild()| state.
     432                 :     bool mBlockedOnParent;
     433                 : 
     434                 :     // Approximation of Sync/RPCChannel-code frames on the C++ stack.
     435                 :     // It can only be interpreted as the implication
     436                 :     //
     437                 :     //  !mCxxStackFrames.empty() => RPCChannel code on C++ stack
     438                 :     //
     439                 :     // This member is only accessed on the worker thread, and so is
     440                 :     // not protected by mMonitor.  It is managed exclusively by the
     441                 :     // helper |class CxxStackFrame|.
     442                 :     std::vector<RPCFrame> mCxxStackFrames;
     443                 : 
     444                 :     // Did we process an RPC out-call during this stack?  Only
     445                 :     // meaningful in ExitedCxxStack(), from which this variable is
     446                 :     // reset.
     447                 :     bool mSawRPCOutMsg;
     448                 : 
     449                 : private:
     450                 : 
     451                 :     //
     452                 :     // All dequeuing tasks require a single point of cancellation,
     453                 :     // which is handled via a reference-counted task.
     454                 :     //
     455                 :     class RefCountedTask
     456                 :     {
     457                 :       public:
     458               1 :         RefCountedTask(CancelableTask* aTask)
     459                 :         : mTask(aTask)
     460               1 :         , mRefCnt(0) {}
     461               0 :         ~RefCountedTask() { delete mTask; }
     462               0 :         void Run() { mTask->Run(); }
     463               0 :         void Cancel() { mTask->Cancel(); }
     464               1 :         void AddRef() {
     465               1 :             NS_AtomicIncrementRefcnt(mRefCnt);
     466               1 :         }
     467               0 :         void Release() {
     468               0 :             if (NS_AtomicDecrementRefcnt(mRefCnt) == 0)
     469               0 :                 delete this;
     470               0 :         }
     471                 : 
     472                 :       private:
     473                 :         CancelableTask* mTask;
     474                 :         nsrefcnt mRefCnt;
     475                 :     };
     476                 : 
     477                 :     //
     478                 :     // Wrap an existing task which can be cancelled at any time
     479                 :     // without the wrapper's knowledge.
     480                 :     //
     481                 :     class DequeueTask : public Task
     482               0 :     {
     483                 :       public:
     484               0 :         DequeueTask(RefCountedTask* aTask) : mTask(aTask) {}
     485               0 :         void Run() { mTask->Run(); }
     486                 :         
     487                 :       private:
     488                 :         nsRefPtr<RefCountedTask> mTask;
     489                 :     };
     490                 : 
     491                 :     // A task encapsulating dequeuing one pending task
     492                 :     nsRefPtr<RefCountedTask> mDequeueOneTask;
     493                 : };
     494                 : 
     495                 : 
     496                 : } // namespace ipc
     497                 : } // namespace mozilla
     498                 : #endif  // ifndef ipc_glue_RPCChannel_h

Generated by: LCOV version 1.7