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