diff --git a/ipc/ipdl/test/cxx/Makefile.in b/ipc/ipdl/test/cxx/Makefile.in index 5a21432e6db5..2ecc3d49844b 100644 --- a/ipc/ipdl/test/cxx/Makefile.in +++ b/ipc/ipdl/test/cxx/Makefile.in @@ -61,6 +61,7 @@ EXPORT_LIBRARY = 1 IPDLTESTS = \ TestSanity \ TestRPCErrorCleanup \ + TestCrashCleanup \ TestLatency \ TestRPCRaces \ TestManyChildAllocs \ diff --git a/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl new file mode 100644 index 000000000000..7be129248aed --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl @@ -0,0 +1,22 @@ +// See bug 538586: if the top-level protocol's actor is deleted before +// the "connection error" notification comes in from the IO thread, +// IPDL teardown never occurs, even if Channel::Close() is called +// after the error. + +namespace mozilla { +namespace _ipdltest { + +// NB: needs to be RPC so that the parent blocks on the child's crash. +rpc protocol PTestCrashCleanup { +child: + rpc DIEDIEDIE(); + __delete__(); + +state ALIVE: + call DIEDIEDIE goto CRASH; +state CRASH: + send __delete__; +}; + +} +} diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp new file mode 100644 index 000000000000..69f353f28d79 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp @@ -0,0 +1,116 @@ +#include "TestCrashCleanup.h" + +#include "mozilla/CondVar.h" +#include "mozilla/Mutex.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +using mozilla::CondVar; +using mozilla::Mutex; +using mozilla::MutexAutoLock; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +namespace { + +// NB: this test does its own shutdown, rather than going through +// QuitParent(), because it's testing degenerate edge cases + +void DeleteSubprocess(Mutex* mutex, CondVar* cvar) +{ + MutexAutoLock lock(*mutex); + + delete gSubprocess; + gSubprocess = NULL; + + cvar->Notify(); +} + +void DeleteTheWorld() +{ + delete static_cast(gParentActor); + gParentActor = NULL; + + // needs to be synchronous to avoid affecting event ordering on + // the main thread + Mutex mutex("TestCrashCleanup.DeleteTheWorld.mutex"); + CondVar cvar(mutex, "TestCrashCleanup.DeleteTheWorld.cvar"); + + MutexAutoLock lock(mutex); + + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); + + cvar.Wait(); +} + +void Done() +{ + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr appShell (do_GetService(kAppShellCID)); + appShell->Exit(); + + passed(__FILE__); +} + +} // namespace + +TestCrashCleanupParent::TestCrashCleanupParent() : mCleanedUp(false) +{ + MOZ_COUNT_CTOR(TestCrashCleanupParent); +} + +TestCrashCleanupParent::~TestCrashCleanupParent() +{ + MOZ_COUNT_DTOR(TestCrashCleanupParent); + + if (!mCleanedUp) + fail("should have been ActorDestroy()d!"); +} + +void +TestCrashCleanupParent::Main() +{ + // NB: has to be enqueued before IO thread's error notification + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableFunction(DeleteTheWorld)); + + if (CallDIEDIEDIE()) + fail("expected an error!"); + + Close(); + + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(Done)); +} + + +//----------------------------------------------------------------------------- +// child + +TestCrashCleanupChild::TestCrashCleanupChild() +{ + MOZ_COUNT_CTOR(TestCrashCleanupChild); +} + +TestCrashCleanupChild::~TestCrashCleanupChild() +{ + MOZ_COUNT_DTOR(TestCrashCleanupChild); +} + +bool +TestCrashCleanupChild::AnswerDIEDIEDIE() +{ + _exit(0); + NS_RUNTIMEABORT("unreached"); + return false; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.h b/ipc/ipdl/test/cxx/TestCrashCleanup.h new file mode 100644 index 000000000000..938a7b33c0be --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h @@ -0,0 +1,58 @@ +#ifndef mozilla__ipdltest_TestCrashCleanup_h +#define mozilla__ipdltest_TestCrashCleanup_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestCrashCleanupParent.h" +#include "mozilla/_ipdltest/PTestCrashCleanupChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestCrashCleanupParent : + public PTestCrashCleanupParent +{ +public: + TestCrashCleanupParent(); + virtual ~TestCrashCleanupParent(); + + void Main(); + +protected: + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + mCleanedUp = true; + } + + bool mCleanedUp; +}; + + +class TestCrashCleanupChild : + public PTestCrashCleanupChild +{ +public: + TestCrashCleanupChild(); + virtual ~TestCrashCleanupChild(); + +protected: + NS_OVERRIDE + virtual bool AnswerDIEDIEDIE(); + + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { + fail("should have 'crashed'!"); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestCrashCleanup_h diff --git a/ipc/ipdl/test/cxx/TestRPCErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestRPCErrorCleanup.cpp index 2b5dcf844943..db4404313c73 100644 --- a/ipc/ipdl/test/cxx/TestRPCErrorCleanup.cpp +++ b/ipc/ipdl/test/cxx/TestRPCErrorCleanup.cpp @@ -16,6 +16,8 @@ namespace _ipdltest { //----------------------------------------------------------------------------- // parent +namespace { + // NB: this test does its own shutdown, rather than going through // QuitParent(), because it's testing degenerate edge cases @@ -57,6 +59,8 @@ void Done() passed(__FILE__); } +} // namespace + TestRPCErrorCleanupParent::TestRPCErrorCleanupParent() { MOZ_COUNT_CTOR(TestRPCErrorCleanupParent); diff --git a/ipc/ipdl/test/cxx/ipdl.mk b/ipc/ipdl/test/cxx/ipdl.mk index e2282b1d139a..8e0ec2e554a9 100644 --- a/ipc/ipdl/test/cxx/ipdl.mk +++ b/ipc/ipdl/test/cxx/ipdl.mk @@ -1,6 +1,7 @@ IPDLSRCS = \ PTestArrays.ipdl \ PTestArraysSub.ipdl \ + PTestCrashCleanup.ipdl \ PTestDesc.ipdl \ PTestDescSub.ipdl \ PTestDescSubsub.ipdl \