--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -44,16 +44,17 @@
#endif
#include "nsILocalFile.h"
#include "pratom.h"
#include "nsDebug.h"
#include "nsCOMPtr.h"
#include "nsPluginsDir.h"
+#include "nsXULAppAPI.h"
#include "mozilla/plugins/PluginInstanceChild.h"
#include "mozilla/plugins/StreamNotifyChild.h"
#include "mozilla/plugins/BrowserStreamChild.h"
#include "mozilla/plugins/PluginStreamChild.h"
#include "nsNPAPIPlugin.h"
@@ -189,23 +190,36 @@ PluginModuleChild::InitGraphics()
return true;
}
bool
PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
{
AssertPluginThread();
- // FIXME/cjones: should all instances be dead by now?
+ // the PluginModuleParent shuts down this process after this RPC
+ // call pops off its stack
*rv = mShutdownFunc ? mShutdownFunc() : NPERR_NO_ERROR;
+
+ // weakly guard against re-entry after NP_Shutdown
+ memset(&mFunctions, 0, sizeof(mFunctions));
+
return true;
}
void
+PluginModuleChild::ActorDestroy(ActorDestroyReason why)
+{
+ // doesn't matter why we're being destroyed; it's up to us to
+ // initiate (clean) shutdown
+ XRE_ShutdownChildProcess();
+}
+
+void
PluginModuleChild::CleanUp()
{
}
const char*
PluginModuleChild::GetUserAgent()
{
if (!CallNPN_UserAgent(&mUserAgent))
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -116,16 +116,19 @@ protected:
const nsCString& aMimeType,
const uint16_t& aMode,
const nsTArray<nsCString>& aNames,
const nsTArray<nsCString>& aValues,
NPError* rv);
virtual bool
AnswerNP_Shutdown(NPError *rv);
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+
public:
PluginModuleChild();
virtual ~PluginModuleChild();
bool Init(const std::string& aPluginFilename,
base::ProcessHandle aParentProcessHandle,
MessageLoop* aIOLoop,
IPC::Channel* aChannel);
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -468,19 +468,22 @@ PluginModuleParent::NP_Initialize(NPNets
}
#endif
nsresult
PluginModuleParent::NP_Shutdown(NPError* error)
{
_MOZ_LOG(__FUNCTION__);
- // FIXME/cjones: should all sub-actors be dead by now?
+ bool ok = CallNP_Shutdown(error);
- bool ok = CallNP_Shutdown(error);
+ // if NP_Shutdown() is nested within another RPC call, this will
+ // break things. but lord help us if we're doing that anyway; the
+ // plugin dso will have been unloaded on the other side by the
+ // CallNP_Shutdown() message
Close();
return ok ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
PluginModuleParent::NP_GetMIMEDescription(char** mimeDesc)
{
--- a/ipc/glue/AsyncChannel.cpp
+++ b/ipc/glue/AsyncChannel.cpp
@@ -34,16 +34,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/ipc/AsyncChannel.h"
#include "mozilla/ipc/GeckoThread.h"
+#include "mozilla/ipc/ProtocolUtils.h"
#include "nsDebug.h"
#include "nsTraceRefcnt.h"
#include "nsXULAppAPI.h"
using mozilla::MutexAutoLock;
template<>
@@ -66,21 +67,17 @@ AsyncChannel::AsyncChannel(AsyncListener
mWorkerLoop()
{
MOZ_COUNT_CTOR(AsyncChannel);
}
AsyncChannel::~AsyncChannel()
{
MOZ_COUNT_DTOR(AsyncChannel);
- if (!mChild && mTransport)
- Close();
- // we only hold a weak ref to the transport, which is "owned"
- // by GeckoChildProcess/GeckoThread
- mTransport = 0;
+ Clear();
}
bool
AsyncChannel::Open(Transport* aTransport, MessageLoop* aIOLoop)
{
NS_PRECONDITION(!mTransport, "Open() called > once");
NS_PRECONDITION(aTransport, "need transport layer");
@@ -124,29 +121,44 @@ AsyncChannel::Open(Transport* aTransport
}
return true;
}
void
AsyncChannel::Close()
{
- MutexAutoLock lock(mMutex);
+ {
+ MutexAutoLock lock(mMutex);
- if (!mChild && ChannelConnected == mChannelState) {
+ if (ChannelConnected != mChannelState)
+ // XXX be strict about this until there's a compelling reason
+ // to relax
+ NS_RUNTIMEABORT("Close() called on closed channel!");
+
AssertWorkerThread();
- mIOLoop->PostTask(
- FROM_HERE, NewRunnableMethod(this, &AsyncChannel::OnClose));
+ // notify the other side that we're about to close our socket
+ SendGoodbye();
+
+ mChannelState = ChannelClosing;
- while (ChannelConnected == mChannelState)
+ // and post the task will do the actual close
+ mIOLoop->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &AsyncChannel::OnCloseChannel));
+
+ while (ChannelClosing == mChannelState)
mCvar.Wait();
+
+ // TODO sort out Close() on this side racing with Close() on the
+ // other side
+ mChannelState = ChannelClosed;
}
- mTransport = NULL;
+ return NotifyChannelClosed();
}
bool
AsyncChannel::Send(Message* msg)
{
AssertWorkerThread();
mMutex.AssertNotCurrentThreadOwns();
NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
@@ -168,19 +180,128 @@ AsyncChannel::Send(Message* msg)
void
AsyncChannel::OnDispatchMessage(const Message& msg)
{
AssertWorkerThread();
NS_ASSERTION(!msg.is_reply(), "can't process replies here");
NS_ASSERTION(!(msg.is_sync() || msg.is_rpc()), "async dispatch only");
+ if (MaybeInterceptGoodbye(msg))
+ // there's a NotifyMaybeChannelError event waiting for us, or
+ // will be soon
+ return;
+
+ // it's OK to dispatch messages if the channel is closed/error'd,
+ // since we don't have a reply to send back
+
(void)MaybeHandleError(mListener->OnMessageReceived(msg), "AsyncChannel");
}
+// This is an async message
+class GoodbyeMessage : public IPC::Message
+{
+public:
+ enum { ID = GOODBYE_MESSAGE_TYPE };
+ GoodbyeMessage() :
+ IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL)
+ {
+ }
+ // XXX not much point in implementing this; maybe could help with
+ // debugging?
+ static bool Read(const Message* msg)
+ {
+ return true;
+ }
+ void Log(const std::string& aPrefix,
+ FILE* aOutf) const
+ {
+ fputs("(special `Goodbye' message)", aOutf);
+ }
+};
+
+void
+AsyncChannel::SendGoodbye()
+{
+ AssertWorkerThread();
+
+ mIOLoop->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &AsyncChannel::OnSend, new GoodbyeMessage()));
+}
+
+bool
+AsyncChannel::MaybeInterceptGoodbye(const Message& msg)
+{
+ // IPDL code isn't allowed to send MSG_ROUTING_NONE messages, so
+ // there's no chance of confusion here
+ if (MSG_ROUTING_NONE != msg.routing_id())
+ return false;
+
+ if (msg.is_sync() || msg.is_rpc() || GOODBYE_MESSAGE_TYPE != msg.type())
+ NS_RUNTIMEABORT("received unknown MSG_ROUTING_NONE message when expecting `Goodbye'");
+
+ MutexAutoLock lock(mMutex);
+ // TODO sort out Close() on this side racing with Close() on the
+ // other side
+ mChannelState = ChannelClosing;
+
+ printf("NOTE: %s process received `Goodbye', closing down\n",
+ mChild ? "child" : "parent");
+
+ return true;
+}
+
+void
+AsyncChannel::NotifyChannelClosed()
+{
+ if (ChannelClosed != mChannelState)
+ NS_RUNTIMEABORT("channel should have been closed!");
+
+ // OK, the IO thread just closed the channel normally. Let the
+ // listener know about it.
+ mListener->OnChannelClose();
+ Clear();
+}
+
+void
+AsyncChannel::NotifyMaybeChannelError()
+{
+ // TODO sort out Close() on this side racing with Close() on the
+ // other side
+ if (ChannelClosing == mChannelState) {
+ // the channel closed, but we received a "Goodbye" message
+ // warning us about it. no worries
+ mChannelState = ChannelClosed;
+ return NotifyChannelClosed();
+ }
+
+ // Oops, error! Let the listener know about it.
+ mChannelState = ChannelError;
+ mListener->OnChannelError();
+
+ Clear();
+}
+
+void
+AsyncChannel::Clear()
+{
+ mListener = 0;
+ mIOLoop = 0;
+ mWorkerLoop = 0;
+
+ if (mTransport) {
+ mTransport->set_listener(0);
+
+ // we only hold a weak ref to the transport, which is "owned"
+ // by GeckoChildProcess/GeckoThread
+ mTransport = 0;
+ }
+}
+
bool
AsyncChannel::MaybeHandleError(Result code, const char* channelName)
{
if (MsgProcessed == code)
return true;
const char* errorMsg;
switch (code) {
@@ -196,17 +317,17 @@ AsyncChannel::MaybeHandleError(Result co
case MsgRouteError:
errorMsg = "Route error: message sent to unknown actor ID";
break;
case MsgValueError:
errorMsg = "Value error: message was deserialized, but contained an illegal value";
break;
default:
- NOTREACHED();
+ NS_RUNTIMEABORT("unknown Result code");
return false;
}
PrintErrorMessage(channelName, errorMsg);
return false;
}
void
@@ -227,100 +348,81 @@ AsyncChannel::ReportConnectionError(cons
default:
NOTREACHED();
}
PrintErrorMessage(channelName, errorMsg);
}
//
-// The methods below run in the context of the IO thread, and can proxy
-// back to the methods above
+// The methods below run in the context of the IO thread
//
void
AsyncChannel::OnMessageReceived(const Message& msg)
{
AssertIOThread();
NS_ASSERTION(mChannelState != ChannelError, "Shouldn't get here!");
// wake up the worker, there's work to do
mWorkerLoop->PostTask(
FROM_HERE,
NewRunnableMethod(this, &AsyncChannel::OnDispatchMessage, msg));
}
void
+AsyncChannel::OnChannelOpened()
+{
+ AssertIOThread();
+ mChannelState = ChannelOpening;
+ /*assert*/mTransport->Connect();
+}
+
+void
AsyncChannel::OnChannelConnected(int32 peer_pid)
{
AssertIOThread();
MutexAutoLock lock(mMutex);
mChannelState = ChannelConnected;
mCvar.Notify();
}
void
AsyncChannel::OnChannelError()
{
AssertIOThread();
- {
- MutexAutoLock lock(mMutex);
- mChannelState = ChannelError;
- }
-
- if (XRE_GetProcessType() == GeckoProcessType_Default) {
- // Parent process, one of our children died. Notify?
- }
- else {
- // Child process, initiate quit sequence.
-#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
- // XXXbent this is totally out of place, but works for now.
- XRE_ShutdownChildProcess(mWorkerLoop);
+ MutexAutoLock lock(mMutex);
- // Must exit the IO loop, which will then join with the UI loop.
- MessageLoop::current()->Quit();
-#else
- // FIXME need to devote some thought to the most
- // effective/least easily overrideable, yet quiet, way to
- // exit. abort() is a little loud
- _exit(0);
-#endif
- }
-}
+ // NB: this can race with the `Goodbye' event being processed by
+ // the worker thread
+ if (ChannelClosing != mChannelState)
+ mChannelState = ChannelError;
-void
-AsyncChannel::OnChannelOpened()
-{
- AssertIOThread();
- mChannelState = ChannelOpening;
- /*assert*/mTransport->Connect();
+ mWorkerLoop->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &AsyncChannel::NotifyMaybeChannelError));
}
void
AsyncChannel::OnSend(Message* aMsg)
{
AssertIOThread();
mTransport->Send(aMsg);
- // mTransport deletes aMsg
+ // mTransport assumes ownership of aMsg
}
void
-AsyncChannel::OnClose()
+AsyncChannel::OnCloseChannel()
{
AssertIOThread();
mTransport->Close();
- // don't lose error-state information
- if (ChannelError != mChannelState)
- mChannelState = ChannelClosed;
-
- if (!mChild) {
- MutexAutoLock lock(mMutex);
- mCvar.Notify();
- }
+ MutexAutoLock lock(mMutex);
+ mChannelState = ChannelClosed;
+ mCvar.Notify();
}
} // namespace ipc
} // namespace mozilla
--- a/ipc/glue/AsyncChannel.h
+++ b/ipc/glue/AsyncChannel.h
@@ -70,47 +70,58 @@ class AsyncChannel : public IPC::Channel
protected:
typedef mozilla::CondVar CondVar;
typedef mozilla::Mutex Mutex;
enum ChannelState {
ChannelClosed,
ChannelOpening,
ChannelConnected,
+ ChannelClosing,
ChannelError
};
public:
typedef IPC::Channel Transport;
typedef IPC::Message Message;
class /*NS_INTERFACE_CLASS*/ AsyncListener: protected HasResultCodes
{
public:
virtual ~AsyncListener() { }
+
+ virtual void OnChannelClose() = 0;
+ virtual void OnChannelError() = 0;
virtual Result OnMessageReceived(const Message& aMessage) = 0;
};
public:
+ //
+ // These methods are called on the "worker" thread
+ //
AsyncChannel(AsyncListener* aListener);
virtual ~AsyncChannel();
// Open from the perspective of the transport layer; the underlying
// socketpair/pipe should already be created.
//
// 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);
+ //
+ // 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);
NS_OVERRIDE virtual void OnChannelError();
protected:
// Can be run on either thread
void AssertWorkerThread()
@@ -136,20 +147,31 @@ protected:
void ReportConnectionError(const char* channelName);
void PrintErrorMessage(const char* channelName, const char* msg)
{
fprintf(stderr, "\n###!!! [%s][%s] Error: %s\n\n",
mChild ? "Child" : "Parent", channelName, msg);
}
+ // Run on the worker thread
+
+ void SendGoodbye();
+ bool MaybeInterceptGoodbye(const Message& msg);
+
+ void NotifyChannelClosed();
+ void NotifyMaybeChannelError();
+
+ void Clear();
+
// Run on the IO thread
+
void OnChannelOpened();
void OnSend(Message* aMsg);
- void OnClose();
+ void OnCloseChannel();
Transport* mTransport;
AsyncListener* mListener;
ChannelState mChannelState;
Mutex mMutex;
CondVar mCvar;
MessageLoop* mIOLoop; // thread where IO happens
MessageLoop* mWorkerLoop; // thread where work is done
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -41,17 +41,24 @@
#define mozilla_ipc_ProtocolUtils_h 1
#include "base/process.h"
#include "base/process_util.h"
#include "chrome/common/ipc_message_utils.h"
#include "prenv.h"
-#include "mozilla/ipc/RPCChannel.h"
+
+// WARNING: this takes into account the private, special-message-type
+// enum in ipc_channel.h. They need to be kept in sync.
+namespace {
+enum {
+ GOODBYE_MESSAGE_TYPE = kuint16max - 1,
+};
+}
namespace mozilla {
namespace ipc {
// Used to pass references to protocol actors across the wire.
// Actors created on the parent-side have a positive ID, and actors
// allocated on the child side have a negative ID.
@@ -59,16 +66,23 @@ struct ActorHandle
{
int mId;
};
template<class ListenerT>
class /*NS_INTERFACE_CLASS*/ IProtocolManager
{
public:
+ enum ActorDestroyReason {
+ Deletion,
+ AncestorDeletion,
+ NormalShutdown,
+ AbnormalShutdown
+ };
+
typedef base::ProcessHandle ProcessHandle;
virtual int32 Register(ListenerT*) = 0;
virtual int32 RegisterID(ListenerT*, int32) = 0;
virtual ListenerT* Lookup(int32) = 0;
virtual void Unregister(int32) = 0;
// XXX odd duck, acknowledged
virtual ProcessHandle OtherProcess() const = 0;
--- a/ipc/glue/RPCChannel.cpp
+++ b/ipc/glue/RPCChannel.cpp
@@ -79,16 +79,17 @@ RPCChannel::~RPCChannel()
MOZ_COUNT_DTOR(RPCChannel);
// FIXME/cjones: impl
}
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");
MutexAutoLock lock(mMutex);
if (!Connected()) {
ReportConnectionError("RPCChannel");
@@ -412,27 +413,22 @@ RPCChannel::OnMessageReceived(const Mess
NotifyWorkerThread();
}
void
RPCChannel::OnChannelError()
{
AssertIOThread();
- {
- MutexAutoLock lock(mMutex);
- mChannelState = ChannelError;
-
- if (AwaitingSyncReply()
- || 0 < StackDepth())
- NotifyWorkerThread();
- }
+ AsyncChannel::OnChannelError();
// skip SyncChannel::OnError(); we subsume its duties
-
- return AsyncChannel::OnChannelError();
+ MutexAutoLock lock(mMutex);
+ if (AwaitingSyncReply()
+ || 0 < StackDepth())
+ NotifyWorkerThread();
}
} // namespace ipc
} // namespace mozilla
--- a/ipc/glue/RPCChannel.h
+++ b/ipc/glue/RPCChannel.h
@@ -52,16 +52,19 @@ namespace ipc {
class RPCChannel : public SyncChannel
{
public:
class /*NS_INTERFACE_CLASS*/ RPCListener :
public SyncChannel::SyncListener
{
public:
virtual ~RPCListener() { }
+
+ virtual void OnChannelClose() = 0;
+ virtual void OnChannelError() = 0;
virtual Result OnMessageReceived(const Message& aMessage) = 0;
virtual Result OnMessageReceived(const Message& aMessage,
Message*& aReply) = 0;
virtual Result OnCallReceived(const Message& aMessage,
Message*& aReply) = 0;
};
// What happens if RPC calls race?
--- a/ipc/glue/SyncChannel.cpp
+++ b/ipc/glue/SyncChannel.cpp
@@ -71,16 +71,17 @@ SyncChannel::~SyncChannel()
// static
bool SyncChannel::sIsPumpingMessages = false;
bool
SyncChannel::Send(Message* msg, Message* reply)
{
AssertWorkerThread();
+ mMutex.AssertNotCurrentThreadOwns();
NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
"violation of sync handler invariant");
NS_ABORT_IF_FALSE(msg->is_sync(), "can only Send() sync messages here");
MutexAutoLock lock(mMutex);
if (!Connected()) {
ReportConnectionError("SyncChannel");
@@ -172,27 +173,22 @@ SyncChannel::OnMessageReceived(const Mes
NotifyWorkerThread();
}
}
void
SyncChannel::OnChannelError()
{
AssertIOThread();
- {
- MutexAutoLock lock(mMutex);
- mChannelState = ChannelError;
+ AsyncChannel::OnChannelError();
- if (AwaitingSyncReply()) {
- NotifyWorkerThread();
- }
- }
-
- return AsyncChannel::OnChannelError();
+ MutexAutoLock lock(mMutex);
+ if (AwaitingSyncReply())
+ NotifyWorkerThread();
}
//
// Synchronization between worker and IO threads
//
// Windows versions of the following two functions live in
// WindowsMessageLoop.cpp.
--- a/ipc/glue/SyncChannel.h
+++ b/ipc/glue/SyncChannel.h
@@ -52,16 +52,19 @@ protected:
typedef uint16 MessageId;
public:
class /*NS_INTERFACE_CLASS*/ SyncListener :
public AsyncChannel::AsyncListener
{
public:
virtual ~SyncListener() { }
+
+ virtual void OnChannelClose() = 0;
+ virtual void OnChannelError() = 0;
virtual Result OnMessageReceived(const Message& aMessage) = 0;
virtual Result OnMessageReceived(const Message& aMessage,
Message*& aReply) = 0;
};
SyncChannel(SyncListener* aListener);
virtual ~SyncChannel();
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -45,38 +45,38 @@ op = optparse.OptionParser(usage='ipdl.p
op.add_option('-I', '--include', dest='includedirs', default=[ ],
action='append',
help='Additional directory to search for included protocol specifications')
op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
help='Verbose logging (specify -vv or -vvv for very verbose logging)')
op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
help="Suppress logging output")
op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
- help="""Director into which C++ headers will be generated.
+ help="""Directory into which C++ headers will be generated.
A protocol Foo in the namespace bar will cause the headers
dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
to be generated""")
op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
- help="""Director into which C++ sources will be generated
+ help="""Directory into which C++ sources will be generated
A protocol Foo in the namespace bar will cause the sources
cppdir/FooParent.cpp, cppdir/FooChild.cpp
to be generated""")
options, files = op.parse_args()
_verbosity = options.verbosity
headersdir = options.headersdir
cppdir = options.cppdir
includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
if not len(files):
op.error("No IPDL files specified")
log(1, 'Generated C++ headers will be generated relative to "%s"', headersdir)
-log(1, 'Generated C++ sources will be generated relative to "%s"', cppdir)
+log(1, 'Generated C++ sources will be generated in "%s"', cppdir)
allprotocols = []
for f in files:
log(1, 'Parsing specification %s', f)
if f == '-':
fd = sys.stdin
filename = '<stdin>'
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -334,16 +334,19 @@ def _callCxxArraySetLength(arr, lenexpr)
def _callCxxArrayInsertSorted(arr, elt):
return ExprCall(ExprSelect(arr, '.', 'InsertElementSorted'),
args=[ elt ])
def _callCxxArrayRemoveSorted(arr, elt):
return ExprCall(ExprSelect(arr, '.', 'RemoveElementSorted'),
args=[ elt ])
+def _callCxxArrayClear(arr):
+ return ExprCall(ExprSelect(arr, '.', 'Clear'))
+
def _otherSide(side):
if side == 'child': return 'parent'
if side == 'parent': return 'child'
assert 0
def _ifLogging(stmts):
iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabled')))
iflogging.addifstmts(stmts)
@@ -405,16 +408,28 @@ def errfnSendDtor(msg):
# used in |OnMessage*()| handlers that hand in-messages off to Recv*()
# interface methods
def errfnRecv(msg, errcode=_Result.ValuError):
return [
_fatalError(msg),
StmtReturn(errcode)
]
+def _destroyMethod():
+ return ExprVar('ActorDestroy')
+
+class _DestroyReason:
+ @staticmethod
+ def Type(): return Type('ActorDestroyReason')
+
+ Deletion = ExprVar('Deletion')
+ AncestorDeletion = ExprVar('AncestorDeletion')
+ NormalShutdown = ExprVar('NormalShutdown')
+ AbnormalShutdown = ExprVar('AbnormalShutdown')
+
##-----------------------------------------------------------------------------
## Intermediate representation (IR) nodes used during lowering
class _ConvertToCxxType(TypeVisitor):
def __init__(self, side): self.side = side
def visitBuiltinCxxType(self, t):
return Type(t.name())
@@ -1364,16 +1379,21 @@ class Protocol(ipdl.ast.Protocol):
def managedMethod(self, actortype, side):
assert self.decl.type.isManagerOf(actortype)
return ExprVar('Managed'+ _actorName(actortype.name(), side))
def managedVar(self, actortype, side):
assert self.decl.type.isManagerOf(actortype)
return ExprVar('mManaged'+ _actorName(actortype.name(), side))
+ def managedVarType(self, actortype, side, const=0, ref=0):
+ assert self.decl.type.isManagerOf(actortype)
+ return _cxxArrayType(self.managedCxxType(actortype, side),
+ const=const, ref=ref)
+
def managerArrayExpr(self, thisvar, side):
"""The member var my manager keeps of actors of my type."""
assert self.decl.type.isManaged()
return ExprSelect(
ExprCall(self.managerMethod(thisvar)),
'->', 'mManaged'+ _actorName(self.decl.type.name(), side))
# shmem stuff
@@ -2476,20 +2496,21 @@ class _GenerateProtocolActorCode(ipdl.as
# FIXME: all actors impl Iface for now
if p.decl.type.isManager() or 1:
self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
self.hdrfile.addthings([
CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
Whitespace.NL ])
- inherits = [ Inherit(Type(p.fqListenerName())) ]
- if p.decl.type.isManager():
- inherits.append(Inherit(p.managerInterfaceType()))
- self.cls = Class(self.clsname, inherits=inherits, abstract=True)
+ self.cls = Class(
+ self.clsname,
+ inherits=[ Inherit(Type(p.fqListenerName()), viz='protected'),
+ Inherit(p.managerInterfaceType(), viz='protected') ],
+ abstract=True)
friends = _FindFriends().findFriends(p.decl.type)
if p.decl.type.isManaged():
friends.add(p.decl.type.manager)
# |friend| managed actors so that they can call our Dealloc*()
friends.update(p.decl.type.manages)
@@ -2547,16 +2568,24 @@ class _GenerateProtocolActorCode(ipdl.as
virtual=1, pure=1)))
self.cls.addstmt(StmtDecl(MethodDecl(
_deallocMethod(managed).name,
params=[ Decl(actortype, 'actor') ],
ret=Type.BOOL,
virtual=1, pure=1)))
+ # optional Shutdown() method; default is no-op
+ self.cls.addstmts([
+ Whitespace.NL,
+ MethodDefn(MethodDecl(
+ _destroyMethod().name,
+ params=[ Decl(_DestroyReason.Type(), 'why') ],
+ virtual=1))
+ ])
self.cls.addstmt(Whitespace.NL)
self.cls.addstmts((
[ Label.PRIVATE ]
+ self.standardTypedefs()
+ [ Whitespace.NL ]
))
@@ -2628,19 +2657,18 @@ class _GenerateProtocolActorCode(ipdl.as
self.cls.addstmts([ managermeth, Whitespace.NL ])
## managed[T]()
for managed in p.decl.type.manages:
arrvar = ExprVar('aArr')
meth = MethodDefn(MethodDecl(
p.managedMethod(managed, self.side).name,
- params=[ Decl(
- _cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
- arrvar.name) ],
+ params=[ Decl(p.managedVarType(managed, self.side, ref=1),
+ arrvar.name) ],
const=1))
meth.addstmt(StmtExpr(ExprAssn(
arrvar, p.managedVar(managed, self.side))))
self.cls.addstmts([ meth, Whitespace.NL ])
## OnMessageReceived()/OnCallReceived()
# save these away for use in message handler case stmts
@@ -2732,16 +2760,33 @@ class _GenerateProtocolActorCode(ipdl.as
])
if p.decl.type.toplevel().talksRpc():
self.cls.addstmts([
makeHandlerMethod('OnCallReceived', self.rpcSwitch,
hasReply=1, dispatches=dispatches),
Whitespace.NL
])
+ destroysubtreevar = ExprVar('DestroySubtree')
+ deallocsubtreevar = ExprVar('DeallocSubtree')
+
+ # OnChannelClose()
+ onclose = MethodDefn(MethodDecl('OnChannelClose'))
+ onclose.addstmt(StmtExpr(ExprCall(
+ destroysubtreevar,
+ args=[ _DestroyReason.NormalShutdown ])))
+ self.cls.addstmts([ onclose, Whitespace.NL ])
+
+ # OnChannelClose()
+ onerror = MethodDefn(MethodDecl('OnChannelError'))
+ onerror.addstmt(StmtExpr(ExprCall(
+ destroysubtreevar,
+ args=[ _DestroyReason.AbnormalShutdown ])))
+ self.cls.addstmts([ onerror, Whitespace.NL ])
+
# FIXME: only manager protocols and non-manager protocols with
# union types need Lookup(). we'll give it to all for the
# time being (simpler)
if 1 or p.decl.type.isManager():
self.cls.addstmts(self.implementManagerIface())
if p.usesShmem():
self.cls.addstmts(self.makeShmemIface())
@@ -2776,16 +2821,112 @@ class _GenerateProtocolActorCode(ipdl.as
_printErrorMessage(" may have failed to kill child!"))
fatalerror.addstmt(ifkill)
else:
# and if it happens on the child side, the child commits
# seppuko
fatalerror.addstmt(
_runtimeAbort('['+ actorname +'] abort()ing as a result'))
self.cls.addstmts([ fatalerror, Whitespace.NL ])
+
+ ## DestroySubtree(bool normal)
+ whyvar = ExprVar('why')
+ subtreewhyvar = ExprVar('subtreewhy')
+ kidsvar = ExprVar('kids')
+ ivar = ExprVar('i')
+ ithkid = ExprIndex(kidsvar, ivar)
+
+ destroysubtree = MethodDefn(MethodDecl(
+ destroysubtreevar.name,
+ params=[ Decl(_DestroyReason.Type(), whyvar.name) ]))
+
+ if p.decl.type.isManager():
+ # only declare this for managers to avoid unused var warnings
+ destroysubtree.addstmts([
+ StmtDecl(
+ Decl(_DestroyReason.Type(), subtreewhyvar.name),
+ init=ExprConditional(
+ ExprBinary(_DestroyReason.Deletion, '==', whyvar),
+ _DestroyReason.AncestorDeletion, whyvar)),
+ Whitespace.NL
+ ])
+
+ for managed in p.decl.type.manages:
+ foreachdestroy = StmtFor(
+ init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
+ cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
+ update=ExprPrefixUnop(ivar, '++'))
+ foreachdestroy.addstmt(StmtExpr(ExprCall(
+ ExprSelect(ithkid, '->', destroysubtreevar.name),
+ args=[ subtreewhyvar ])))
+
+ block = StmtBlock()
+ block.addstmts([
+ Whitespace(
+ '// Recursively shutting down %s kids\n'% (managed.name()),
+ indent=1),
+ StmtDecl(
+ Decl(p.managedVarType(managed, self.side), kidsvar.name),
+ init=p.managedVar(managed, self.side)),
+ foreachdestroy,
+ ])
+ destroysubtree.addstmt(block)
+ # finally, destroy "us"
+ destroysubtree.addstmt(StmtExpr(
+ ExprCall(_destroyMethod(), args=[ whyvar ])))
+
+ # XXX kick off DeallocSubtree() here rather than in a new
+ # event because that may be tricky on shutdown. revisit if
+ # need be
+ if p.decl.type.isToplevel():
+ destroysubtree.addstmt(StmtExpr(ExprCall(deallocsubtreevar)))
+
+ self.cls.addstmts([ destroysubtree, Whitespace.NL ])
+
+ ## DeallocSubtree()
+ deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name))
+ for managed in p.decl.type.manages:
+ foreachrecurse = StmtFor(
+ init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
+ cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
+ update=ExprPrefixUnop(ivar, '++'))
+ foreachrecurse.addstmt(StmtExpr(ExprCall(
+ ExprSelect(ithkid, '->', deallocsubtreevar.name))))
+
+ foreachdealloc = StmtFor(
+ init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
+ cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
+ update=ExprPrefixUnop(ivar, '++'))
+ foreachdealloc.addstmts([
+ StmtExpr(ExprCall(_deallocMethod(managed),
+ args=[ ithkid ]))
+ ])
+
+ block = StmtBlock()
+ block.addstmts([
+ Whitespace(
+ '// Recursively deleting %s kids\n'% (managed.name()),
+ indent=1),
+ StmtDecl(
+ Decl(p.managedVarType(managed, self.side, ref=1),
+ kidsvar.name),
+ init=p.managedVar(managed, self.side)),
+ foreachrecurse,
+ Whitespace.NL,
+ # no need to copy |kids| here; we're the ones deleting
+ # stragglers, no outside C++ is being invoked (except
+ # Dealloc(subactor))
+ foreachdealloc,
+ StmtExpr(_callCxxArrayClear(p.managedVar(managed, self.side))),
+
+ ])
+ deallocsubtree.addstmt(block)
+ # don't delete outselves: either the manager will do it, or
+ # we're toplevel
+ self.cls.addstmts([ deallocsubtree, Whitespace.NL ])
## private members
self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
if p.decl.type.isToplevel():
self.cls.addstmts([
StmtDecl(Decl(Type('IDMap', T=Type('ChannelListener')),
p.actorMapVar().name)),
StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)),
@@ -2804,20 +2945,19 @@ class _GenerateProtocolActorCode(ipdl.as
p.shmemMapVar().name)),
StmtDecl(Decl(_shmemIdType(), p.lastShmemIdVar().name))
])
for managed in p.decl.type.manages:
self.cls.addstmts([
Whitespace('// Sorted by pointer value\n', indent=1),
StmtDecl(Decl(
- _cxxArrayType(p.managedCxxType(managed, self.side)),
+ p.managedVarType(managed, self.side),
p.managedVar(managed, self.side).name)) ])
-
def implementManagerIface(self):
p = self.protocol
routedvar = ExprVar('aRouted')
idvar = ExprVar('aId')
listenertype = Type('ChannelListener', ptr=1)
register = MethodDefn(MethodDecl(
p.registerMethod().name,
@@ -3277,18 +3417,19 @@ class _GenerateProtocolActorCode(ipdl.as
return method
def dtorPrologue(self, actorexpr):
return [ self.failIfNullActor(actorexpr), Whitespace.NL ]
def dtorEpilogue(self, md, actorexpr):
return (self.unregisterActor(actorexpr)
- + [ StmtExpr(self.callRemoveActor(md, actorexpr)),
- StmtExpr(self.callDeallocActor(md, actorexpr))
+ + [ StmtExpr(self.callActorDestroy(actorexpr)) ]
+ + [ StmtExpr(self.callRemoveActor(actorexpr)) ]
+ + [ StmtExpr(self.callDeallocActor(md, actorexpr))
])
def genAsyncSendMethod(self, md):
method = MethodDefn(self.makeSendMethodDecl(md))
msgvar, stmts = self.makeMessage(md, errfnSend)
sendok, sendstmts = self.sendAsync(md, msgvar)
method.addstmts(stmts
+[ Whitespace.NL ]
@@ -3576,24 +3717,27 @@ class _GenerateProtocolActorCode(ipdl.as
])
def callAllocActor(self, md, retsems):
return ExprCall(
_allocMethod(md.decl.type.constructedType()),
args=md.makeCxxArgs(params=1, retsems=retsems, retcallsems='out',
implicit=0))
- def callRemoveActor(self, md, actorexpr):
- actortype = md.decl.type.constructedType()
- if not actortype.isManaged():
- return Whitespace('// unmanaged protocol\n')
-
- return _callCxxArrayRemoveSorted(
- self.protocol.managerArrayExpr(actorexpr, self.side),
- actorexpr)
+ def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
+ return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
+ args=[ why ])
+
+ def callRemoveActor(self, actorexpr, actorarray=None):
+ if not self.protocol.decl.type.isManaged():
+ return Whitespace('// unmanaged protocol')
+
+ if actorarray is None:
+ actorarray = self.protocol.managerArrayExpr(actorexpr, self.side)
+ return _callCxxArrayRemoveSorted(actorarray, actorexpr)
def callDeallocActor(self, md, actorexpr):
actor = md.decl.type.constructedType()
return ExprCall(
ExprSelect(ExprCall(self.protocol.managerMethod(actorexpr)), '->',
_deallocMethod(md.decl.type.constructedType()).name),
args=[ actorexpr ])
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -849,16 +849,19 @@ class GatherDecls(TcheckVisitor):
# check the trigger message
mname = t.msg
if mname in self.seentriggers:
self.error(loc, "trigger `%s' appears multiple times", mname)
self.seentriggers.add(mname)
mdecl = self.symtab.lookup(mname)
+ if mdecl.type.isIPDL() and mdecl.type.isProtocol():
+ mdecl = self.symtab.lookup(mname +'Constructor')
+
if mdecl is None:
self.error(loc, "message `%s' has not been declared", mname)
elif not mdecl.type.isMessage():
self.error(
loc,
"`%s' should have message type, but instead has type `%s'",
mname, mdecl.type.typename())
else:
--- a/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
@@ -34,43 +34,44 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
#define mozilla__ipdltest_IPDLUnitTestTypes_h
+#include "mozilla/ipc/ProtocolUtils.h" // ActorDestroyReason
namespace mozilla {
namespace _ipdltest {
struct DirtyRect
{
- int x; int y; int w; int h;
+ int x; int y; int w; int h;
};
}
}
namespace IPC {
template<>
struct ParamTraits<mozilla::_ipdltest::DirtyRect>
{
- typedef mozilla::_ipdltest::DirtyRect paramType;
- static void Write(Message* aMsg, const paramType& aParam) {
- WriteParam(aMsg, aParam.x);
- WriteParam(aMsg, aParam.y);
- WriteParam(aMsg, aParam.w);
- WriteParam(aMsg, aParam.h);
- }
- static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
- {
- return (ReadParam(aMsg, aIter, &aResult->x) &&
- ReadParam(aMsg, aIter, &aResult->y) &&
- ReadParam(aMsg, aIter, &aResult->w) &&
- ReadParam(aMsg, aIter, &aResult->h));
- }
+ typedef mozilla::_ipdltest::DirtyRect paramType;
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ WriteParam(aMsg, aParam.w);
+ WriteParam(aMsg, aParam.h);
+ }
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->x) &&
+ ReadParam(aMsg, aIter, &aResult->y) &&
+ ReadParam(aMsg, aIter, &aResult->w) &&
+ ReadParam(aMsg, aIter, &aResult->h));
+ }
};
}
#endif // ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.h
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h
@@ -38,17 +38,25 @@
#ifndef mozilla__ipdltest_IPDLUnitTests_h
#define mozilla__ipdltest_IPDLUnitTests_h 1
#include "base/message_loop.h"
#include "base/process.h"
#include "chrome/common/ipc_channel.h"
+#include "mozilla/ipc/GeckoThread.h"
+
+#include "nsIAppShell.h"
+
+#include "nsCOMPtr.h"
#include "nsDebug.h"
+#include "nsServiceManagerUtils.h" // do_GetService()
+#include "nsWidgetsCID.h" // NS_APPSHELL_CID
+
#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
namespace mozilla {
namespace _ipdltest {
@@ -88,23 +96,38 @@ inline void passed(const char* fmt, ...)
fputc('\n', stdout);
}
//-----------------------------------------------------------------------------
// parent process only
void IPDLUnitTestMain(void* aData);
-// TODO: could clean up parent actor/subprocess
-
+inline void
+QuitParent()
+{
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+}
//-----------------------------------------------------------------------------
// child process only
void IPDLUnitTestChildInit(IPC::Channel* transport,
base::ProcessHandle parent,
MessageLoop* worker);
+inline void
+QuitChild()
+{
+ mozilla::ipc::BrowserProcessSubThread::
+ GetMessageLoop(mozilla::ipc::BrowserProcessSubThread::IO)->
+ PostTask(FROM_HERE, new MessageLoop::QuitTask());
+
+ MessageLoopForUI::current()->Quit();
+}
+
} // namespace _ipdltest
} // namespace mozilla
#endif // ifndef mozilla__ipdltest_IPDLUnitTests_h
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -58,16 +58,17 @@ FORCE_STATIC_LIB = 1
EXPORT_LIBRARY = 1
# Please keep these organized in the order "easy"-to-"hard"
IPDLTESTS = \
TestSanity \
TestLatency \
TestManyChildAllocs \
TestDesc \
+ TestShutdown \
TestArrays \
$(NULL)
IPDLTESTSRCS = $(addsuffix .cpp,$(IPDLTESTS))
IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
GENTESTER := $(srcdir)/genIPDLUnitTests.py
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
@@ -0,0 +1,37 @@
+include protocol "PTestShutdownSub.ipdl";
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdown {
+ manages PTestShutdownSub;
+
+child:
+ Start();
+
+parent:
+ // NB: we test deletion and crashing only, not shutdown, because
+ // crashing is the same code path as shutdown, and other IPDL unit
+ // tests check shutdown semantics
+ PTestShutdownSub(bool expectCrash);
+
+ // Used to synchronize between parent and child, to avoid racies
+ // around flushing socket write queues
+ sync Sync();
+
+ __delete__();
+
+
+state START:
+ send Start goto TESTING;
+
+state TESTING:
+ recv PTestShutdownSub goto TESTING;
+ recv Sync goto DYING;
+
+state DYING:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
@@ -0,0 +1,21 @@
+include protocol "PTestShutdown.ipdl";
+include protocol "PTestShutdownSubsub.ipdl";
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdownSub {
+ manager PTestShutdown;
+ manages PTestShutdownSubsub;
+
+parent:
+ PTestShutdownSubsub(bool expectParentDeleted);
+ __delete__();
+
+state CREATING:
+ recv PTestShutdownSubsub goto CREATING;
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
@@ -0,0 +1,17 @@
+include protocol "PTestShutdownSub.ipdl";
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdownSubsub {
+ manager PTestShutdownSub;
+
+parent:
+ __delete__();
+
+state LIVE:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
--- a/ipc/ipdl/test/cxx/TestArrays.cpp
+++ b/ipc/ipdl/test/cxx/TestArrays.cpp
@@ -1,16 +1,10 @@
#include "TestArrays.h"
-#include "nsIAppShell.h"
-
-#include "nsCOMPtr.h"
-#include "nsServiceManagerUtils.h" // do_GetService()
-#include "nsWidgetsCID.h" // NS_APPSHELL_CID
-
#include "IPDLUnitTests.h" // fail etc.
namespace mozilla {
namespace _ipdltest {
static const uint32 nactors = 10;
#define test_assert(_cond, _msg) \
@@ -66,22 +60,16 @@ TestArraysParent::DeallocPTestArraysSub(
{
test_assert(Cast(actor).mI == Cast(mKids[0]).mI,
"dtor sent to wrong actor");
mKids.RemoveElementAt(0);
delete actor;
if (mKids.Length() > 0)
return true;
- passed("with flying colors");
-
- static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
- nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
- appShell->Exit();
-
return true;
}
bool TestArraysParent::RecvTest1(
const nsTArray<int>& ia,
nsTArray<int>* oa)
{
test_assert(5 == ia.Length(), "wrong length");
@@ -320,16 +308,18 @@ TestArraysChild::RecvStart()
Test8();
Test9();
Test10();
for (uint32 i = 0; i < nactors; ++i)
if (!PTestArraysSubChild::Send__delete__(mKids[i]))
fail("can't send dtor");
+ Close();
+
return true;
}
void
TestArraysChild::Test1()
{
nsTArray<int> ia;
--- a/ipc/ipdl/test/cxx/TestArrays.h
+++ b/ipc/ipdl/test/cxx/TestArrays.h
@@ -1,11 +1,12 @@
#ifndef mozilla__ipdltest_TestArrays_h
#define mozilla__ipdltest_TestArrays_h 1
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
#include "mozilla/_ipdltest/PTestArraysParent.h"
#include "mozilla/_ipdltest/PTestArraysChild.h"
#include "mozilla/_ipdltest/PTestArraysSubParent.h"
#include "mozilla/_ipdltest/PTestArraysSubChild.h"
namespace mozilla {
@@ -34,106 +35,141 @@ class TestArraysParent :
{
public:
TestArraysParent();
virtual ~TestArraysParent();
void Main();
protected:
+ NS_OVERRIDE
virtual PTestArraysSubParent* AllocPTestArraysSub(const int& i)
{
PTestArraysSubParent* actor = new TestArraysSub(i);
mKids.AppendElement(actor);
return actor;
}
+ NS_OVERRIDE
virtual bool DeallocPTestArraysSub(PTestArraysSubParent* actor);
+ NS_OVERRIDE
virtual bool RecvTest1(
const nsTArray<int>& i1,
nsTArray<int>* o1);
+ NS_OVERRIDE
virtual bool RecvTest2(
const nsTArray<PTestArraysSubParent*>& i1,
nsTArray<PTestArraysSubParent*>* o1);
+ NS_OVERRIDE
virtual bool RecvTest3(
const IntDouble& i1,
const IntDouble& i2,
IntDouble* o1,
IntDouble* o2);
+ NS_OVERRIDE
virtual bool RecvTest4(
const nsTArray<IntDouble>& i1,
nsTArray<IntDouble>* o1);
+ NS_OVERRIDE
virtual bool RecvTest5(
const IntDoubleArrays& i1,
const IntDoubleArrays& i2,
const IntDoubleArrays& i3,
IntDoubleArrays* o1,
IntDoubleArrays* o2,
IntDoubleArrays* o3);
+ NS_OVERRIDE
virtual bool RecvTest6(
const nsTArray<IntDoubleArrays>& i1,
nsTArray<IntDoubleArrays>* o1);
+ NS_OVERRIDE
virtual bool RecvTest7(
const Actors& i1,
const Actors& i2,
const Actors& i3,
Actors* o1,
Actors* o2,
Actors* o3);
+ NS_OVERRIDE
virtual bool RecvTest8(
const nsTArray<Actors>& i1,
nsTArray<Actors>* o1);
+
+ NS_OVERRIDE
virtual bool RecvTest9(
const Unions& i1,
const Unions& i2,
const Unions& i3,
const Unions& i4,
Unions* o1,
Unions* o2,
Unions* o3,
Unions* o4);
+
+ NS_OVERRIDE
virtual bool RecvTest10(
const nsTArray<Unions>& i1,
nsTArray<Unions>* o1);
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
private:
nsTArray<PTestArraysSubParent*> mKids;
};
class TestArraysChild :
public PTestArraysChild
{
public:
TestArraysChild();
virtual ~TestArraysChild();
protected:
+ NS_OVERRIDE
virtual PTestArraysSubChild* AllocPTestArraysSub(const int& i)
{
PTestArraysSubChild* actor = new TestArraysSub(i);
mKids.AppendElement(actor);
return actor;
}
+
+ NS_OVERRIDE
virtual bool DeallocPTestArraysSub(PTestArraysSubChild* actor)
{
delete actor;
return true;
}
+ NS_OVERRIDE
virtual bool RecvStart();
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
private:
void Test1();
void Test2();
void Test3();
void Test4();
void Test5();
void Test6();
void Test7();
--- a/ipc/ipdl/test/cxx/TestDesc.cpp
+++ b/ipc/ipdl/test/cxx/TestDesc.cpp
@@ -1,16 +1,10 @@
#include "TestDesc.h"
-#include "nsIAppShell.h"
-
-#include "nsCOMPtr.h"
-#include "nsServiceManagerUtils.h" // do_GetService()
-#include "nsWidgetsCID.h" // NS_APPSHELL_CID
-
#include "IPDLUnitTests.h" // fail etc.
namespace mozilla {
namespace _ipdltest {
//-----------------------------------------------------------------------------
// parent
void
@@ -29,21 +23,17 @@ TestDescParent::Main()
}
bool
TestDescParent::RecvOk(PTestDescSubsubParent* a)
{
if (!a)
fail("didn't receive Subsub");
- passed("ok");
-
- static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
- nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
- appShell->Exit();
+ Close();
return true;
}
PTestDescSubParent*
TestDescParent::AllocPTestDescSub() {
return new TestDescSubParent();
--- a/ipc/ipdl/test/cxx/TestDesc.h
+++ b/ipc/ipdl/test/cxx/TestDesc.h
@@ -1,11 +1,13 @@
#ifndef mozilla_ipdltest_TestDesc_h
#define mozilla_ipdltest_TestDesc_h
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
#include "mozilla/_ipdltest/PTestDescParent.h"
#include "mozilla/_ipdltest/PTestDescChild.h"
#include "mozilla/_ipdltest/PTestDescSubParent.h"
#include "mozilla/_ipdltest/PTestDescSubChild.h"
#include "mozilla/_ipdltest/PTestDescSubsubParent.h"
#include "mozilla/_ipdltest/PTestDescSubsubChild.h"
@@ -20,63 +22,93 @@ class TestDescParent :
public PTestDescParent
{
public:
TestDescParent() { }
virtual ~TestDescParent() { }
void Main();
+ NS_OVERRIDE
virtual bool RecvOk(PTestDescSubsubParent* a);
protected:
+ NS_OVERRIDE
virtual PTestDescSubParent* AllocPTestDescSub();
+ NS_OVERRIDE
virtual bool DeallocPTestDescSub(PTestDescSubParent* actor);
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
};
class TestDescChild :
public PTestDescChild
{
public:
TestDescChild() { }
virtual ~TestDescChild() { }
protected:
+ NS_OVERRIDE
virtual PTestDescSubChild* AllocPTestDescSub();
+
+ NS_OVERRIDE
virtual bool DeallocPTestDescSub(PTestDescSubChild* actor);
+
+ NS_OVERRIDE
virtual bool RecvTest(PTestDescSubsubChild* a);
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
};
//-----------------------------------------------------------------------------
// First descendent
//
class TestDescSubParent :
public PTestDescSubParent
{
public:
TestDescSubParent() { }
virtual ~TestDescSubParent() { }
protected:
+ NS_OVERRIDE
virtual PTestDescSubsubParent* AllocPTestDescSubsub();
+
+ NS_OVERRIDE
virtual bool DeallocPTestDescSubsub(PTestDescSubsubParent* actor);
};
class TestDescSubChild :
public PTestDescSubChild
{
public:
TestDescSubChild() { }
virtual ~TestDescSubChild() { }
protected:
+ NS_OVERRIDE
virtual PTestDescSubsubChild* AllocPTestDescSubsub();
+ NS_OVERRIDE
virtual bool DeallocPTestDescSubsub(PTestDescSubsubChild* actor);
};
//-----------------------------------------------------------------------------
// Grand-descendent
//
class TestDescSubsubParent :
--- a/ipc/ipdl/test/cxx/TestLatency.cpp
+++ b/ipc/ipdl/test/cxx/TestLatency.cpp
@@ -1,21 +1,13 @@
#include "TestLatency.h"
-#include "nsIAppShell.h"
-
-#include "nsCOMPtr.h"
-#include "nsServiceManagerUtils.h" // do_GetService()
-#include "nsWidgetsCID.h" // NS_APPSHELL_CID
-
#include "IPDLUnitTests.h" // fail etc.
-#define NR_TRIALS 10000
-
namespace mozilla {
namespace _ipdltest {
//-----------------------------------------------------------------------------
// parent
TestLatencyParent::TestLatencyParent() :
mStart(),
@@ -61,23 +53,17 @@ TestLatencyParent::Ping5Pong5Trial()
SendPing5();
SendPing5();
SendPing5();
}
void
TestLatencyParent::Exit()
{
- passed("average ping/pong latency: %g sec, average ping5/pong5 latency: %g sec",
- mPPTimeTotal.ToSeconds() / (double) NR_TRIALS,
- mPP5TimeTotal.ToSeconds() / (double) NR_TRIALS);
-
- static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
- nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
- appShell->Exit();
+ Close();
}
bool
TestLatencyParent::RecvPong()
{
TimeDuration thisTrial = (TimeStamp::Now() - mStart);
mPPTimeTotal += thisTrial;
--- a/ipc/ipdl/test/cxx/TestLatency.h
+++ b/ipc/ipdl/test/cxx/TestLatency.h
@@ -1,38 +1,55 @@
#ifndef mozilla__ipdltest_TestLatency_h
#define mozilla__ipdltest_TestLatency_h 1
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
#include "mozilla/_ipdltest/PTestLatencyParent.h"
#include "mozilla/_ipdltest/PTestLatencyChild.h"
#include "mozilla/TimeStamp.h"
+#define NR_TRIALS 10000
+
namespace mozilla {
namespace _ipdltest {
-
class TestLatencyParent :
public PTestLatencyParent
{
private:
typedef mozilla::TimeStamp TimeStamp;
typedef mozilla::TimeDuration TimeDuration;
public:
TestLatencyParent();
virtual ~TestLatencyParent();
void Main();
-protected:
+protected:
+ NS_OVERRIDE
virtual bool RecvPong();
+ NS_OVERRIDE
virtual bool RecvPong5();
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ passed("average ping/pong latency: %g sec, average ping5/pong5 latency: %g sec",
+ mPPTimeTotal.ToSeconds() / (double) NR_TRIALS,
+ mPP5TimeTotal.ToSeconds() / (double) NR_TRIALS);
+
+ QuitParent();
+ }
+
private:
void PingPongTrial();
void Ping5Pong5Trial();
void Exit();
TimeStamp mStart;
TimeDuration mPPTimeTotal;
TimeDuration mPP5TimeTotal;
@@ -48,18 +65,28 @@ private:
class TestLatencyChild :
public PTestLatencyChild
{
public:
TestLatencyChild();
virtual ~TestLatencyChild();
protected:
+ NS_OVERRIDE
virtual bool RecvPing();
+ NS_OVERRIDE
virtual bool RecvPing5();
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
};
} // namespace _ipdltest
} // namespace mozilla
#endif // ifndef mozilla__ipdltest_TestLatency_h
--- a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
@@ -1,16 +1,10 @@
#include "TestManyChildAllocs.h"
-#include "nsIAppShell.h"
-
-#include "nsCOMPtr.h"
-#include "nsServiceManagerUtils.h" // do_GetService()
-#include "nsWidgetsCID.h" // NS_APPSHELL_CID
-
#include "IPDLUnitTests.h" // fail etc.
#define NALLOCS 10
namespace mozilla {
namespace _ipdltest {
@@ -31,23 +25,19 @@ TestManyChildAllocsParent::Main()
{
if (!SendGo())
fail("can't send Go()");
}
bool
TestManyChildAllocsParent::RecvDone()
{
- // should clean up ...
-
- passed("allocs were successfuly");
-
- static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
- nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
- appShell->Exit();
+ // explicitly *not* cleaning up, so we can sanity-check IPDL's
+ // auto-shutdown/cleanup handling
+ Close();
return true;
}
bool
TestManyChildAllocsParent::DeallocPTestManyChildAllocsSub(
PTestManyChildAllocsSubParent* __a)
{
--- a/ipc/ipdl/test/cxx/TestManyChildAllocs.h
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
@@ -1,11 +1,13 @@
#ifndef mozilla__ipdltest_TestManyChildAllocs_h
#define mozilla__ipdltest_TestManyChildAllocs_h 1
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
#include "mozilla/_ipdltest/PTestManyChildAllocsParent.h"
#include "mozilla/_ipdltest/PTestManyChildAllocsChild.h"
#include "mozilla/_ipdltest/PTestManyChildAllocsSubParent.h"
#include "mozilla/_ipdltest/PTestManyChildAllocsSubChild.h"
namespace mozilla {
namespace _ipdltest {
@@ -17,46 +19,70 @@ class TestManyChildAllocsParent :
{
public:
TestManyChildAllocsParent();
virtual ~TestManyChildAllocsParent();
void Main();
protected:
+ NS_OVERRIDE
virtual bool RecvDone();
+ NS_OVERRIDE
virtual bool DeallocPTestManyChildAllocsSub(PTestManyChildAllocsSubParent* __a);
+ NS_OVERRIDE
virtual PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSub();
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
};
class TestManyChildAllocsChild :
public PTestManyChildAllocsChild
{
public:
TestManyChildAllocsChild();
virtual ~TestManyChildAllocsChild();
protected:
+ NS_OVERRIDE
virtual bool RecvGo();
+ NS_OVERRIDE
virtual bool DeallocPTestManyChildAllocsSub(PTestManyChildAllocsSubChild* __a);
+ NS_OVERRIDE
virtual PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSub();
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
};
// do-nothing sub-protocol actors
class TestManyChildAllocsSubParent :
public PTestManyChildAllocsSubParent
{
public:
TestManyChildAllocsSubParent() { }
virtual ~TestManyChildAllocsSubParent() { }
protected:
+ NS_OVERRIDE
virtual bool RecvHello() { return true; }
};
class TestManyChildAllocsSubChild :
public PTestManyChildAllocsSubChild
{
public:
--- a/ipc/ipdl/test/cxx/TestSanity.cpp
+++ b/ipc/ipdl/test/cxx/TestSanity.cpp
@@ -1,16 +1,10 @@
#include "TestSanity.h"
-#include "nsIAppShell.h"
-
-#include "nsCOMPtr.h"
-#include "nsServiceManagerUtils.h" // do_GetService()
-#include "nsWidgetsCID.h" // NS_APPSHELL_CID
-
#include "IPDLUnitTests.h" // fail etc.
namespace mozilla {
namespace _ipdltest {
//-----------------------------------------------------------------------------
// parent
@@ -36,32 +30,21 @@ bool
TestSanityParent::RecvPong(const int& one, const float& zeroPtTwoFive)
{
if (1 != one)
fail("invalid argument `%d', should have been `1'", one);
if (0.25f != zeroPtTwoFive)
fail("invalid argument `%g', should have been `0.25'", zeroPtTwoFive);
- passed("sent ping/received pong");
-
- static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
- nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
- appShell->Exit();
+ Close();
return true;
}
-bool
-TestSanityParent::RecvUNREACHED()
-{
- fail("unreached");
- return false; // not reached
-}
-
//-----------------------------------------------------------------------------
// child
TestSanityChild::TestSanityChild()
{
MOZ_COUNT_CTOR(TestSanityChild);
}
@@ -80,18 +63,11 @@ TestSanityChild::RecvPing(const int& zer
if (0.5f != zeroPtFive)
fail("invalid argument `%g', should have been `0.5'", zeroPtFive);
if (!SendPong(1, 0.25f))
fail("sending Pong");
return true;
}
-bool
-TestSanityChild::RecvUNREACHED()
-{
- fail("unreached");
- return false; // not reached
-}
-
} // namespace _ipdltest
} // namespace mozilla
--- a/ipc/ipdl/test/cxx/TestSanity.h
+++ b/ipc/ipdl/test/cxx/TestSanity.h
@@ -1,11 +1,12 @@
#ifndef mozilla__ipdltest_TestSanity_h
#define mozilla__ipdltest_TestSanity_h 1
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
#include "mozilla/_ipdltest/PTestSanityParent.h"
#include "mozilla/_ipdltest/PTestSanityChild.h"
namespace mozilla {
namespace _ipdltest {
@@ -14,31 +15,48 @@ class TestSanityParent :
{
public:
TestSanityParent();
virtual ~TestSanityParent();
void Main();
protected:
+ NS_OVERRIDE
virtual bool RecvPong(const int& one, const float& zeroPtTwoFive);
- virtual bool RecvUNREACHED();
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
};
class TestSanityChild :
public PTestSanityChild
{
public:
TestSanityChild();
virtual ~TestSanityChild();
protected:
+ NS_OVERRIDE
virtual bool RecvPing(const int& zero, const float& zeroPtFive);
- virtual bool RecvUNREACHED();
+
+ NS_OVERRIDE
+ virtual void ActorDestroy(ActorDestroyReason why)
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
};
} // namespace _ipdltest
} // namespace mozilla
#endif // ifndef mozilla__ipdltest_TestSanity_h
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.cpp
@@ -0,0 +1,227 @@
+#include "TestShutdown.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+void
+TestShutdownParent::Main()
+{
+ SendStart();
+}
+
+void
+TestShutdownParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (AbnormalShutdown != why)
+ fail("should have ended test with crash!");
+
+ passed("ok");
+
+ QuitParent();
+}
+
+void
+TestShutdownSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ nsTArray<PTestShutdownSubParent*> broArr; // grumble grumble
+ Manager()->ManagedPTestShutdownSubParent(broArr);
+ if (broArr.Length() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ nsTArray<PTestShutdownSubsubParent*> kidsArr;
+ ManagedPTestShutdownSubsubParent(kidsArr);
+ if (mExpectCrash && 0 == kidsArr.Length())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why)
+{
+ nsTArray<PTestShutdownSubsubParent*> broArr;
+ Manager()->ManagedPTestShutdownSubsubParent(broArr);
+ if (broArr.Length() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+//-----------------------------------------------------------------------------
+// Child side
+
+bool
+TestShutdownChild::RecvStart()
+{
+ // test 1: alloc some actors and subactors, delete in
+ // managee-before-manager order
+ {
+ bool expectCrash = false, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild::Send__delete__(c1s1);
+ PTestShutdownSubsubChild::Send__delete__(c1s2);
+ PTestShutdownSubsubChild::Send__delete__(c2s1);
+ PTestShutdownSubsubChild::Send__delete__(c2s2);
+
+ PTestShutdownSubChild::Send__delete__(c1);
+ PTestShutdownSubChild::Send__delete__(c2);
+ }
+
+ // test 2: alloc some actors and subactors, delete managers first
+ {
+ bool expectCrash = false, expectParentDeleted = true;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // delete parents without deleting kids
+ PTestShutdownSubChild::Send__delete__(c1);
+ PTestShutdownSubChild::Send__delete__(c2);
+ }
+
+ // test 3: alloc some actors and subactors, then crash
+ {
+ bool expectCrash = true, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // make sure the ctors have been processed by the other side;
+ // the write end of the socket may temporarily be unwriteable
+ if (!SendSync())
+ fail("can't synchronize with parent");
+
+ // "crash", but without tripping tinderbox assert/abort
+ // detectors
+ _exit(0);
+ }
+}
+
+void
+TestShutdownChild::ActorDestroy(ActorDestroyReason why)
+{
+ fail("hey wait ... we should have crashed!");
+}
+
+void
+TestShutdownSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ nsTArray<PTestShutdownSubChild*> broArr;
+ Manager()->ManagedPTestShutdownSubChild(broArr);
+ if (broArr.Length() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ nsTArray<PTestShutdownSubsubChild*> kidsArr;
+ ManagedPTestShutdownSubsubChild(kidsArr);
+ if (mExpectCrash && 0 == kidsArr.Length())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why)
+{
+ nsTArray<PTestShutdownSubsubChild*> broArr;
+ Manager()->ManagedPTestShutdownSubsubChild(broArr);
+ if (broArr.Length() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.h
@@ -0,0 +1,222 @@
+#ifndef mozilla__ipdltest_TestShutdown_h
+#define mozilla__ipdltest_TestShutdown_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShutdownParent.h"
+#include "mozilla/_ipdltest/PTestShutdownChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubsubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+
+class TestShutdownSubsubParent :
+ public PTestShutdownSubsubParent
+{
+public:
+ TestShutdownSubsubParent(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+
+ virtual ~TestShutdownSubsubParent()
+ {
+ }
+
+protected:
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubParent :
+ public PTestShutdownSubParent
+{
+public:
+ TestShutdownSubParent(bool expectCrash) : mExpectCrash(expectCrash)
+ {
+ }
+
+ virtual ~TestShutdownSubParent()
+ {
+ }
+
+protected:
+ NS_OVERRIDE
+ virtual PTestShutdownSubsubParent*
+ AllocPTestShutdownSubsub(const bool& expectParentDelete)
+ {
+ return new TestShutdownSubsubParent(expectParentDelete);
+ }
+
+ NS_OVERRIDE
+ virtual bool
+ DeallocPTestShutdownSubsub(PTestShutdownSubsubParent* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+
+private:
+ bool mExpectCrash;
+};
+
+
+class TestShutdownParent :
+ public PTestShutdownParent
+{
+public:
+ TestShutdownParent()
+ {
+ }
+ virtual ~TestShutdownParent()
+ {
+ }
+
+ void Main();
+
+protected:
+ NS_OVERRIDE virtual bool RecvSync() { return true; }
+
+ NS_OVERRIDE
+ virtual PTestShutdownSubParent*
+ AllocPTestShutdownSub(const bool& expectCrash)
+ {
+ return new TestShutdownSubParent(expectCrash);
+ }
+
+ NS_OVERRIDE
+ virtual bool
+ DeallocPTestShutdownSub(PTestShutdownSubParent* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+};
+
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestShutdownSubsubChild :
+ public PTestShutdownSubsubChild
+{
+public:
+ TestShutdownSubsubChild(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+ virtual ~TestShutdownSubsubChild()
+ {
+ }
+
+protected:
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubChild :
+ public PTestShutdownSubChild
+{
+public:
+ TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash)
+ {
+ }
+
+ virtual ~TestShutdownSubChild()
+ {
+ }
+
+protected:
+ NS_OVERRIDE
+ virtual PTestShutdownSubsubChild*
+ AllocPTestShutdownSubsub(const bool& expectParentDelete)
+ {
+ return new TestShutdownSubsubChild(expectParentDelete);
+ }
+
+ NS_OVERRIDE
+ virtual bool
+ DeallocPTestShutdownSubsub(PTestShutdownSubsubChild* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+
+private:
+ bool mExpectCrash;
+};
+
+
+class TestShutdownChild :
+ public PTestShutdownChild
+{
+public:
+ TestShutdownChild()
+ {
+ }
+ virtual ~TestShutdownChild()
+ {
+ }
+
+protected:
+ NS_OVERRIDE
+ virtual bool
+ RecvStart();
+
+ NS_OVERRIDE
+ virtual PTestShutdownSubChild*
+ AllocPTestShutdownSub(
+ const bool& expectCrash)
+ {
+ return new TestShutdownSubChild(expectCrash);
+ }
+
+ NS_OVERRIDE
+ virtual bool
+ DeallocPTestShutdownSub(PTestShutdownSubChild* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ NS_OVERRIDE
+ virtual void
+ ActorDestroy(ActorDestroyReason why);
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestShutdown_h
--- a/ipc/ipdl/test/cxx/ipdl.mk
+++ b/ipc/ipdl/test/cxx/ipdl.mk
@@ -3,9 +3,12 @@ IPDLSRCS = \
PTestArraysSub.ipdl \
PTestDesc.ipdl \
PTestDescSub.ipdl \
PTestDescSubsub.ipdl \
PTestLatency.ipdl \
PTestManyChildAllocs.ipdl \
PTestManyChildAllocsSub.ipdl \
PTestSanity.ipdl \
+ PTestShutdown.ipdl \
+ PTestShutdownSub.ipdl \
+ PTestShutdownSubsub.ipdl \
$(NULL)
--- a/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl
@@ -3,9 +3,11 @@ include protocol "managedProtocol.ipdl";
// sanity check of managed/manager protocols
protocol managerProtocol {
manages managedProtocol;
parent:
managedProtocol(int i);
+state CREATING:
+ recv managedProtocol goto CREATING;
};
--- a/toolkit/library/dlldeps-xul.cpp
+++ b/toolkit/library/dlldeps-xul.cpp
@@ -51,12 +51,12 @@ void xxxNeverCalledXUL()
XRE_FreeAppData(nsnull);
XRE_ChildProcessTypeToString(GeckoProcessType_Default);
XRE_StringToChildProcessType("");
XRE_GetProcessType();
#ifdef MOZ_IPC
XRE_InitChildProcess(0, nsnull, GeckoProcessType_Default);
XRE_InitParentProcess(0, nsnull, nsnull, nsnull);
XRE_RunAppShell();
- XRE_ShutdownChildProcess(nsnull);
+ XRE_ShutdownChildProcess();
XRE_SendTestShellCommand(nsnull, nsnull, nsnull);
#endif
}
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -454,26 +454,35 @@ XRE_RunAppShell()
template<>
struct RunnableMethodTraits<ContentProcessChild>
{
static void RetainCallee(ContentProcessChild* obj) { }
static void ReleaseCallee(ContentProcessChild* obj) { }
};
void
-XRE_ShutdownChildProcess(MessageLoop* aUILoop)
+XRE_ShutdownChildProcess()
{
- NS_ASSERTION(aUILoop, "Shouldn't be null!");
- if (aUILoop) {
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- if (GeckoProcessType_Content == XRE_GetProcessType())
- aUILoop->PostTask(
- FROM_HERE,
- NewRunnableMethod(ContentProcessChild::GetSingleton(),
- &ContentProcessChild::Quit));
+ NS_ABORT_IF_FALSE(NS_IsMainThread(), "Wrong thread!");
+
+ MessageLoop* uiLoop = MessageLoop::current();
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+
+ NS_ABORT_IF_FALSE(!!ioLoop, "Bad shutdown order");
+ ioLoop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+
+ NS_ABORT_IF_FALSE(!!uiLoop, "Bad shutdown order");
+ if (GeckoProcessType_Content == XRE_GetProcessType()) {
+ uiLoop->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(ContentProcessChild::GetSingleton(),
+ &ContentProcessChild::Quit));
+ }
+ else {
+ uiLoop->Quit();
}
}
namespace {
TestShellParent* gTestShellParent = nsnull;
}
bool
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -473,17 +473,17 @@ XRE_API(nsresult,
XRE_RunAppShell, ())
XRE_API(nsresult,
XRE_InitCommandLine, (int aArgc, char* aArgv[]))
class MessageLoop;
XRE_API(void,
- XRE_ShutdownChildProcess, (MessageLoop* aUILoop))
+ XRE_ShutdownChildProcess, ())
XRE_API(MessageLoop*,
XRE_GetIOMessageLoop, ())
struct JSContext;
struct JSString;
XRE_API(bool,