LCOV - app.info - objdir/dist/include/mozilla/ipc/RPCChannel.h

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-07-07 | 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