diff options
Diffstat (limited to 'ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp')
-rw-r--r-- | ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp new file mode 100644 index 0000000000..21a5bc5924 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp @@ -0,0 +1,155 @@ +#include "TestInterruptErrorCleanup.h" + +#include "base/task.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 = nullptr; + + cvar->Notify(); +} + +void DeleteTheWorld() +{ + delete static_cast<TestInterruptErrorCleanupParent*>(gParentActor); + gParentActor = nullptr; + + // needs to be synchronous to avoid affecting event ordering on + // the main thread + Mutex mutex("TestInterruptErrorCleanup.DeleteTheWorld.mutex"); + CondVar cvar(mutex, "TestInterruptErrorCleanup.DeleteTheWorld.cvar"); + + MutexAutoLock lock(mutex); + + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); + + cvar.Wait(); +} + +void Done() +{ + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID)); + appShell->Exit(); + + passed(__FILE__); +} + +} // namespace <anon> + +TestInterruptErrorCleanupParent::TestInterruptErrorCleanupParent() + : mGotProcessingError(false) +{ + MOZ_COUNT_CTOR(TestInterruptErrorCleanupParent); +} + +TestInterruptErrorCleanupParent::~TestInterruptErrorCleanupParent() +{ + MOZ_COUNT_DTOR(TestInterruptErrorCleanupParent); +} + +void +TestInterruptErrorCleanupParent::Main() +{ + // This test models the following sequence of events + // + // (1) Parent: Interrupt out-call + // (2) Child: crash + // --[Parent-only hereafter]-- + // (3) Interrupt out-call return false + // (4) Close() + // --[event loop]-- + // (5) delete parentActor + // (6) delete childProcess + // --[event loop]-- + // (7) Channel::OnError notification + // --[event loop]-- + // (8) Done, quit + // + // See bug 535298 and friends; this seqeunce of events captures + // three differnent potential errors + // - Close()-after-error (semantic error previously) + // - use-after-free of parentActor + // - use-after-free of channel + // + // Because of legacy constraints related to nsNPAPI* code, we need + // to ensure that this sequence of events can occur without + // errors/crashes. + + MessageLoop::current()->PostTask( + NewRunnableFunction(DeleteTheWorld)); + + // it's a failure if this *succeeds* + if (CallError()) + fail("expected an error!"); + + if (!mGotProcessingError) + fail("expected a ProcessingError() notification"); + + // it's OK to Close() a channel after an error, because nsNPAPI* + // wants to do this + Close(); + + // we know that this event *must* be after the MaybeError + // notification enqueued by AsyncChannel, because that event is + // enqueued within the same mutex that ends up signaling the + // wakeup-on-error of |CallError()| above + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); +} + +void +TestInterruptErrorCleanupParent::ProcessingError(Result aCode, const char* aReason) +{ + if (aCode != MsgDropped) + fail("unexpected processing error"); + mGotProcessingError = true; +} + +//----------------------------------------------------------------------------- +// child + +TestInterruptErrorCleanupChild::TestInterruptErrorCleanupChild() +{ + MOZ_COUNT_CTOR(TestInterruptErrorCleanupChild); +} + +TestInterruptErrorCleanupChild::~TestInterruptErrorCleanupChild() +{ + MOZ_COUNT_DTOR(TestInterruptErrorCleanupChild); +} + +bool +TestInterruptErrorCleanupChild::AnswerError() +{ + _exit(0); + NS_RUNTIMEABORT("unreached"); + return false; +} + + +} // namespace _ipdltest +} // namespace mozilla |