#include "base/process_util.h" #include "TestHangs.h" #include "IPDLUnitTests.h" // fail etc. using base::KillProcess; namespace mozilla { namespace _ipdltest { //----------------------------------------------------------------------------- // parent TestHangsParent::TestHangsParent() : mDetectedHang(false), mNumAnswerStackFrame(0) { MOZ_COUNT_CTOR(TestHangsParent); } TestHangsParent::~TestHangsParent() { MOZ_COUNT_DTOR(TestHangsParent); } void TestHangsParent::Main() { // Here we try to set things up to test the following sequence of events: // // - subprocess causes an OnMaybeDequeueOne() task to be posted to // this thread // // - subprocess hangs just long enough for the hang timer to expire // // - hang-kill code in the parent starts running // // - subprocess replies to message while hang code runs // // - reply is processed in OnMaybeDequeueOne() before Close() has // been called or the channel error notification has been posted // this tells the subprocess to send us Nonce() if (!SendStart()) fail("sending Start"); // now we sleep here for a while awaiting the Nonce() message from // the child. since we're not blocked on anything, the IO thread // will enqueue an OnMaybeDequeueOne() task to process that // message // // NB: PR_Sleep is exactly what we want, only the current thread // sleeping PR_Sleep(5000); // when we call into this, we'll pull the Nonce() message out of // the mPending queue, but that doesn't matter ... the // OnMaybeDequeueOne() event will remain if (CallStackFrame() && mDetectedHang) fail("should have timed out!"); // the Close() task in the queue will shut us down } bool TestHangsParent::ShouldContinueFromReplyTimeout() { mDetectedHang = true; // so we've detected a timeout after 2 ms ... now we cheat and // sleep for a long time, to allow the subprocess's reply to come // in PR_Sleep(5000); // reply should be here; we'll post a task to shut things down. // This must be after OnMaybeDequeueOne() in the event queue. MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( "_ipdltest::TestHangsParent::CleanUp", this, &TestHangsParent::CleanUp)); GetIPCChannel()->CloseWithTimeout(); return false; } mozilla::ipc::IPCResult TestHangsParent::AnswerStackFrame() { ++mNumAnswerStackFrame; if (mNumAnswerStackFrame == 1) { if (CallStackFrame()) { fail("should have timed out!"); } } else if (mNumAnswerStackFrame == 2) { // minimum possible, 2 ms. We want to detecting a hang to race // with the reply coming in, as reliably as possible SetReplyTimeoutMs(2); if (CallHang()) fail("should have timed out!"); } else { fail("unexpected state"); } return IPC_OK(); } void TestHangsParent::CleanUp() { ipc::ScopedProcessHandle otherProcessHandle; if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) { fail("couldn't open child process"); } else { if (!KillProcess(otherProcessHandle, 0, false)) { fail("terminating child process"); } } Close(); } //----------------------------------------------------------------------------- // child TestHangsChild::TestHangsChild() { MOZ_COUNT_CTOR(TestHangsChild); } TestHangsChild::~TestHangsChild() { MOZ_COUNT_DTOR(TestHangsChild); } mozilla::ipc::IPCResult TestHangsChild::AnswerHang() { puts(" (child process is 'hanging' now)"); // just sleep until we're reasonably confident the 1ms hang // detector fired in the parent process and it's sleeping in // ShouldContinueFromReplyTimeout() PR_Sleep(1000); return IPC_OK(); } } // namespace _ipdltest } // namespace mozilla