Bug 545455, part 1: Track when RPCChannel code is first pushed on the C++ stack and last popped. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Tue, 16 Feb 2010 12:44:21 -0600
changeset 38681 dbfb36b8b3816efe95e7e2ee35ebeb007641cc3d
parent 38680 241ac713765039192e12e381e322ac98526ad5cb
child 38682 d0f27565024d39e9a12f76e2f506ae2c69c7a83f
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs545455
milestone1.9.3a2pre
Bug 545455, part 1: Track when RPCChannel code is first pushed on the C++ stack and last popped. r=bent
ipc/glue/AsyncChannel.h
ipc/glue/RPCChannel.cpp
ipc/glue/RPCChannel.h
ipc/glue/SyncChannel.h
--- a/ipc/glue/AsyncChannel.h
+++ b/ipc/glue/AsyncChannel.h
@@ -107,17 +107,17 @@ public:
     // Returns true iff the transport layer was successfully connected,
     // i.e., mChannelState == ChannelConnected.
     bool Open(Transport* aTransport, MessageLoop* aIOLoop=0);
     
     // Close the underlying transport channel.
     void Close();
 
     // Asynchronously send a message to the other side of the channel
-    bool Send(Message* msg);
+    virtual bool Send(Message* msg);
 
     //
     // These methods are called on the "IO" thread
     //
 
     // Implement the IPC::Channel::Listener interface
     NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);
     NS_OVERRIDE virtual void OnChannelConnected(int32 peer_pid);
--- a/ipc/glue/RPCChannel.cpp
+++ b/ipc/glue/RPCChannel.cpp
@@ -93,25 +93,26 @@ RPCChannel::RPCChannel(RPCListener* aLis
                        RacyRPCPolicy aPolicy)
   : SyncChannel(aListener),
     mPending(),
     mStack(),
     mOutOfTurnReplies(),
     mDeferred(),
     mRemoteStackDepthGuess(0),
     mRacePolicy(aPolicy),
-    mBlockedOnParent(false)
+    mBlockedOnParent(false),
+    mCxxStackFrames(0)
 {
     MOZ_COUNT_CTOR(RPCChannel);
 }
 
 RPCChannel::~RPCChannel()
 {
     MOZ_COUNT_DTOR(RPCChannel);
-    // FIXME/cjones: impl
+    RPC_ASSERT(0 == mCxxStackFrames, "mismatched CxxStackFrame ctor/dtors");
 }
 
 #ifdef OS_WIN
 // static
 int RPCChannel::sInnerEventLoopDepth = 0;
 #endif
 
 bool
@@ -124,24 +125,40 @@ RPCChannel::EventOccurred()
     return (!Connected() ||
             !mPending.empty() ||
             (!mOutOfTurnReplies.empty() &&
              mOutOfTurnReplies.find(mStack.top().seqno())
              != mOutOfTurnReplies.end()));
 }
 
 bool
+RPCChannel::Send(Message* msg)
+{
+    CxxStackFrame f(*this);
+    return AsyncChannel::Send(msg);
+}
+
+bool
+RPCChannel::Send(Message* msg, Message* reply)
+{
+    CxxStackFrame f(*this);
+    return SyncChannel::Send(msg, reply);
+}
+
+bool
 RPCChannel::Call(Message* msg, Message* reply)
 {
     AssertWorkerThread();
     mMutex.AssertNotCurrentThreadOwns();
     RPC_ASSERT(!ProcessingSyncMessage(),
                "violation of sync handler invariant");
     RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
 
+    CxxStackFrame f(*this);
+
     MutexAutoLock lock(mMutex);
 
     if (!Connected()) {
         ReportConnectionError("RPCChannel");
         return false;
     }
 
     msg->set_seqno(NextSeqno());
@@ -336,16 +353,18 @@ RPCChannel::OnMaybeDequeueOne()
 
         if (mPending.empty())
             return;
 
         recvd = mPending.front();
         mPending.pop();
     }
 
+    CxxStackFrame f(*this);
+
     if (recvd.is_rpc())
         return Incall(recvd, 0);
     else if (recvd.is_sync())
         return SyncChannel::OnDispatchMessage(recvd);
     else
         return AsyncChannel::OnDispatchMessage(recvd);
 }
 
--- a/ipc/glue/RPCChannel.h
+++ b/ipc/glue/RPCChannel.h
@@ -46,16 +46,18 @@
 #include "mozilla/ipc/SyncChannel.h"
 
 namespace mozilla {
 namespace ipc {
 //-----------------------------------------------------------------------------
 
 class RPCChannel : public SyncChannel
 {
+    friend class CxxStackFrame;
+
 public:
     class /*NS_INTERFACE_CLASS*/ RPCListener :
         public SyncChannel::SyncListener
     {
     public:
         virtual ~RPCListener() { }
 
         virtual void OnChannelClose() = 0;
@@ -77,16 +79,23 @@ public:
 
     RPCChannel(RPCListener* aListener, RacyRPCPolicy aPolicy=RRPChildWins);
 
     virtual ~RPCChannel();
 
     // Make an RPC to the other side of the channel
     bool Call(Message* msg, Message* reply);
 
+    // RPCChannel overrides these so that the async and sync messages
+    // can be counted against mStackFrames
+    NS_OVERRIDE
+    virtual bool Send(Message* msg);
+    NS_OVERRIDE
+    virtual bool Send(Message* msg, Message* reply);
+
     // Asynchronously, send the child a message that puts it in such a
     // state that it can't send messages to the parent unless the
     // parent sends a message to it first.  The child stays in this
     // state until the parent calls |UnblockChild()|.
     //
     // It is an error to
     //  - call this on the child side of the channel.
     //  - nest |BlockChild()| calls
@@ -146,16 +155,65 @@ protected:
 
     void OnMaybeDequeueOne();
     void Incall(const Message& call, size_t stackDepth);
     void DispatchIncall(const Message& call);
 
     void BlockOnParent();
     void UnblockFromParent();
 
+    // This helper class managed RPCChannel.mCxxStackDepth on behalf
+    // of RPCChannel.  When the stack depth is incremented from zero
+    // to non-zero, it invokes an RPCChannel callback, and similarly
+    // for when the depth goes from non-zero to zero;
+    void EnteredCxxStack()
+    {
+        // FIXME/bug 545455: call mListener hook
+        printf("[%s] +++ CXX STACK\n", mChild ? "child" : "parent");
+    }
+
+    void ExitedCxxStack()
+    {
+        // FIXME/bug 545455: call mListener hook
+        printf("[%s] --- CXX STACK\n", mChild ? "child" : "parent");
+    }
+
+    class NS_STACK_CLASS CxxStackFrame
+    {
+    public:
+        CxxStackFrame(RPCChannel& that) : mThat(that) {
+            NS_ABORT_IF_FALSE(0 <= mThat.mCxxStackFrames,
+                              "mismatched CxxStackFrame ctor/dtor");
+            mThat.AssertWorkerThread();
+
+            if (0 == mThat.mCxxStackFrames++)
+                mThat.EnteredCxxStack();
+        }
+
+        ~CxxStackFrame() {
+            bool exitingStack = (0 == --mThat.mCxxStackFrames);
+
+            // mListener could have gone away if Close() was called while
+            // RPCChannel code was still on the stack
+            if (!mThat.mListener)
+                return;
+
+            mThat.AssertWorkerThread();
+            if (exitingStack)
+                mThat.ExitedCxxStack();
+        }
+    private:
+        RPCChannel& mThat;
+
+        // disable harmful methods
+        CxxStackFrame();
+        CxxStackFrame(const CxxStackFrame&);
+        CxxStackFrame& operator=(const CxxStackFrame&);
+    };
+
     // Called from both threads
     size_t StackDepth() {
         mMutex.AssertCurrentThreadOwns();
         return mStack.size();
     }
 
     void DebugAbort(const char* file, int line, const char* cond,
                     const char* why,
@@ -251,14 +309,24 @@ protected:
     // if one side detects a race, then the other side must also 
     // detect the same race.
     //
     size_t mRemoteStackDepthGuess;
     RacyRPCPolicy mRacePolicy;
 
     // True iff the parent has put us in a |BlockChild()| state.
     bool mBlockedOnParent;
+
+    // Approximation of number of Sync/RPCChannel-code frames on the
+    // C++ stack.  It can only be interpreted as the implication
+    //
+    //  mCxxStackDepth > 0 => RPCChannel code on C++ stack
+    //
+    // This member is only accessed on the worker thread, and so is
+    // not protected by mMutex.  It is managed exclusively by the
+    // helper |class CxxStackFrame|.
+    int mCxxStackFrames;
 };
 
 
 } // namespace ipc
 } // namespace mozilla
 #endif  // ifndef ipc_glue_RPCChannel_h
--- a/ipc/glue/SyncChannel.h
+++ b/ipc/glue/SyncChannel.h
@@ -66,22 +66,23 @@ public:
         virtual bool OnReplyTimeout() = 0;
         virtual Result OnMessageReceived(const Message& aMessage,
                                          Message*& aReply) = 0;
     };
 
     SyncChannel(SyncListener* aListener);
     virtual ~SyncChannel();
 
-    bool Send(Message* msg) {
+    NS_OVERRIDE
+    virtual bool Send(Message* msg) {
         return AsyncChannel::Send(msg);
     }
 
     // Synchronously send |msg| (i.e., wait for |reply|)
-    bool Send(Message* msg, Message* reply);
+    virtual bool Send(Message* msg, Message* reply);
 
     void SetReplyTimeoutMs(int32 aTimeoutMs) {
         AssertWorkerThread();
         mTimeoutMs = (aTimeoutMs <= 0) ? kNoTimeout : aTimeoutMs;
     }
 
     // Override the AsyncChannel handler so we can dispatch sync messages
     NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);