Bug 545455, part 1: Track when RPCChannel code is first pushed on the C++ stack and last popped. r=bent
--- 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());
@@ -337,16 +354,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);