1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,155 @@ 1.4 +#include "TestInterruptErrorCleanup.h" 1.5 + 1.6 +#include "mozilla/CondVar.h" 1.7 +#include "mozilla/Mutex.h" 1.8 + 1.9 +#include "IPDLUnitTests.h" // fail etc. 1.10 +#include "IPDLUnitTestSubprocess.h" 1.11 + 1.12 +using mozilla::CondVar; 1.13 +using mozilla::Mutex; 1.14 +using mozilla::MutexAutoLock; 1.15 + 1.16 +namespace mozilla { 1.17 +namespace _ipdltest { 1.18 + 1.19 +//----------------------------------------------------------------------------- 1.20 +// parent 1.21 + 1.22 +namespace { 1.23 + 1.24 +// NB: this test does its own shutdown, rather than going through 1.25 +// QuitParent(), because it's testing degenerate edge cases 1.26 + 1.27 +void DeleteSubprocess(Mutex* mutex, CondVar* cvar) 1.28 +{ 1.29 + MutexAutoLock lock(*mutex); 1.30 + 1.31 + delete gSubprocess; 1.32 + gSubprocess = nullptr; 1.33 + 1.34 + cvar->Notify(); 1.35 +} 1.36 + 1.37 +void DeleteTheWorld() 1.38 +{ 1.39 + delete static_cast<TestInterruptErrorCleanupParent*>(gParentActor); 1.40 + gParentActor = nullptr; 1.41 + 1.42 + // needs to be synchronous to avoid affecting event ordering on 1.43 + // the main thread 1.44 + Mutex mutex("TestInterruptErrorCleanup.DeleteTheWorld.mutex"); 1.45 + CondVar cvar(mutex, "TestInterruptErrorCleanup.DeleteTheWorld.cvar"); 1.46 + 1.47 + MutexAutoLock lock(mutex); 1.48 + 1.49 + XRE_GetIOMessageLoop()->PostTask( 1.50 + FROM_HERE, 1.51 + NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); 1.52 + 1.53 + cvar.Wait(); 1.54 +} 1.55 + 1.56 +void Done() 1.57 +{ 1.58 + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); 1.59 + nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID)); 1.60 + appShell->Exit(); 1.61 + 1.62 + passed(__FILE__); 1.63 +} 1.64 + 1.65 +} // namespace <anon> 1.66 + 1.67 +TestInterruptErrorCleanupParent::TestInterruptErrorCleanupParent() 1.68 + : mGotProcessingError(false) 1.69 +{ 1.70 + MOZ_COUNT_CTOR(TestInterruptErrorCleanupParent); 1.71 +} 1.72 + 1.73 +TestInterruptErrorCleanupParent::~TestInterruptErrorCleanupParent() 1.74 +{ 1.75 + MOZ_COUNT_DTOR(TestInterruptErrorCleanupParent); 1.76 +} 1.77 + 1.78 +void 1.79 +TestInterruptErrorCleanupParent::Main() 1.80 +{ 1.81 + // This test models the following sequence of events 1.82 + // 1.83 + // (1) Parent: Interrupt out-call 1.84 + // (2) Child: crash 1.85 + // --[Parent-only hereafter]-- 1.86 + // (3) Interrupt out-call return false 1.87 + // (4) Close() 1.88 + // --[event loop]-- 1.89 + // (5) delete parentActor 1.90 + // (6) delete childProcess 1.91 + // --[event loop]-- 1.92 + // (7) Channel::OnError notification 1.93 + // --[event loop]-- 1.94 + // (8) Done, quit 1.95 + // 1.96 + // See bug 535298 and friends; this seqeunce of events captures 1.97 + // three differnent potential errors 1.98 + // - Close()-after-error (semantic error previously) 1.99 + // - use-after-free of parentActor 1.100 + // - use-after-free of channel 1.101 + // 1.102 + // Because of legacy constraints related to nsNPAPI* code, we need 1.103 + // to ensure that this sequence of events can occur without 1.104 + // errors/crashes. 1.105 + 1.106 + MessageLoop::current()->PostTask( 1.107 + FROM_HERE, NewRunnableFunction(DeleteTheWorld)); 1.108 + 1.109 + // it's a failure if this *succeeds* 1.110 + if (CallError()) 1.111 + fail("expected an error!"); 1.112 + 1.113 + if (!mGotProcessingError) 1.114 + fail("expected a ProcessingError() notification"); 1.115 + 1.116 + // it's OK to Close() a channel after an error, because nsNPAPI* 1.117 + // wants to do this 1.118 + Close(); 1.119 + 1.120 + // we know that this event *must* be after the MaybeError 1.121 + // notification enqueued by AsyncChannel, because that event is 1.122 + // enqueued within the same mutex that ends up signaling the 1.123 + // wakeup-on-error of |CallError()| above 1.124 + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(Done)); 1.125 +} 1.126 + 1.127 +void 1.128 +TestInterruptErrorCleanupParent::ProcessingError(Result what) 1.129 +{ 1.130 + if (what != MsgDropped) 1.131 + fail("unexpected processing error"); 1.132 + mGotProcessingError = true; 1.133 +} 1.134 + 1.135 +//----------------------------------------------------------------------------- 1.136 +// child 1.137 + 1.138 +TestInterruptErrorCleanupChild::TestInterruptErrorCleanupChild() 1.139 +{ 1.140 + MOZ_COUNT_CTOR(TestInterruptErrorCleanupChild); 1.141 +} 1.142 + 1.143 +TestInterruptErrorCleanupChild::~TestInterruptErrorCleanupChild() 1.144 +{ 1.145 + MOZ_COUNT_DTOR(TestInterruptErrorCleanupChild); 1.146 +} 1.147 + 1.148 +bool 1.149 +TestInterruptErrorCleanupChild::AnswerError() 1.150 +{ 1.151 + _exit(0); 1.152 + NS_RUNTIMEABORT("unreached"); 1.153 + return false; 1.154 +} 1.155 + 1.156 + 1.157 +} // namespace _ipdltest 1.158 +} // namespace mozilla