michael@0: #include "base/thread.h" michael@0: michael@0: #include "TestOpens.h" michael@0: michael@0: #include "IPDLUnitTests.h" // fail etc. michael@0: michael@0: template<> michael@0: struct RunnableMethodTraits michael@0: { michael@0: static void RetainCallee(mozilla::_ipdltest::TestOpensChild* obj) { } michael@0: static void ReleaseCallee(mozilla::_ipdltest::TestOpensChild* obj) { } michael@0: }; michael@0: michael@0: template<> michael@0: struct RunnableMethodTraits michael@0: { michael@0: static void RetainCallee(mozilla::_ipdltest2::TestOpensOpenedChild* obj) { } michael@0: static void ReleaseCallee(mozilla::_ipdltest2::TestOpensOpenedChild* obj) { } michael@0: }; michael@0: michael@0: using namespace base; michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: // NB: this is generally bad style, but I am lazy. michael@0: using namespace _ipdltest; michael@0: using namespace _ipdltest2; michael@0: michael@0: static MessageLoop* gMainThread; michael@0: michael@0: static void michael@0: AssertNotMainThread() michael@0: { michael@0: if (!gMainThread) michael@0: fail("gMainThread is not initialized"); michael@0: if (MessageLoop::current() == gMainThread) michael@0: fail("unexpectedly called on the main thread"); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // parent michael@0: michael@0: // Thread on which TestOpensOpenedParent runs michael@0: static Thread* gParentThread; michael@0: michael@0: void michael@0: TestOpensParent::Main() michael@0: { michael@0: if (!SendStart()) michael@0: fail("sending Start"); michael@0: } michael@0: michael@0: static void michael@0: OpenParent(TestOpensOpenedParent* aParent, michael@0: Transport* aTransport, ProcessHandle aOtherProcess) michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: // Open the actor on the off-main thread to park it there. michael@0: // Messages will be delivered to this thread's message loop michael@0: // instead of the main thread's. michael@0: if (!aParent->Open(aTransport, aOtherProcess, michael@0: XRE_GetIOMessageLoop(), ipc::ParentSide)) michael@0: fail("opening Parent"); michael@0: } michael@0: michael@0: PTestOpensOpenedParent* michael@0: TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, michael@0: ProcessId otherProcess) michael@0: { michael@0: gMainThread = MessageLoop::current(); michael@0: michael@0: ProcessHandle h; michael@0: if (!base::OpenProcessHandle(otherProcess, &h)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: gParentThread = new Thread("ParentThread"); michael@0: if (!gParentThread->Start()) michael@0: fail("starting parent thread"); michael@0: michael@0: TestOpensOpenedParent* a = new TestOpensOpenedParent(transport); michael@0: gParentThread->message_loop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(OpenParent, a, transport, h)); michael@0: michael@0: return a; michael@0: } michael@0: michael@0: void michael@0: TestOpensParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: // Stops the thread and joins it michael@0: delete gParentThread; michael@0: michael@0: if (NormalShutdown != why) michael@0: fail("unexpected destruction!"); michael@0: passed("ok"); michael@0: QuitParent(); michael@0: } michael@0: michael@0: bool michael@0: TestOpensOpenedParent::RecvHello() michael@0: { michael@0: AssertNotMainThread(); michael@0: return SendHi(); michael@0: } michael@0: michael@0: bool michael@0: TestOpensOpenedParent::RecvHelloSync() michael@0: { michael@0: AssertNotMainThread(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestOpensOpenedParent::AnswerHelloRpc() michael@0: { michael@0: AssertNotMainThread(); michael@0: return CallHiRpc(); michael@0: } michael@0: michael@0: void michael@0: TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: if (NormalShutdown != why) michael@0: fail("unexpected destruction!"); michael@0: michael@0: // ActorDestroy() is just a callback from IPDL-generated code, michael@0: // which needs the top-level actor (this) to stay alive a little michael@0: // longer so other things can be cleaned up. michael@0: MessageLoop::current()->PostTask( michael@0: FROM_HERE, michael@0: new DeleteTask(this)); michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: new DeleteTask(mTransport)); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // child michael@0: michael@0: static TestOpensChild* gOpensChild; michael@0: // Thread on which TestOpensOpenedChild runs michael@0: static Thread* gChildThread; michael@0: michael@0: TestOpensChild::TestOpensChild() michael@0: { michael@0: gOpensChild = this; michael@0: } michael@0: michael@0: bool michael@0: TestOpensChild::RecvStart() michael@0: { michael@0: if (!PTestOpensOpened::Open(this)) michael@0: fail("opening PTestOpensOpened"); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: OpenChild(TestOpensOpenedChild* aChild, michael@0: Transport* aTransport, ProcessHandle aOtherProcess) michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: // Open the actor on the off-main thread to park it there. michael@0: // Messages will be delivered to this thread's message loop michael@0: // instead of the main thread's. michael@0: if (!aChild->Open(aTransport, aOtherProcess, michael@0: XRE_GetIOMessageLoop(), ipc::ChildSide)) michael@0: fail("opening Child"); michael@0: michael@0: // Kick off the unit tests michael@0: if (!aChild->SendHello()) michael@0: fail("sending Hello"); michael@0: } michael@0: michael@0: PTestOpensOpenedChild* michael@0: TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, michael@0: ProcessId otherProcess) michael@0: { michael@0: gMainThread = MessageLoop::current(); michael@0: michael@0: ProcessHandle h; michael@0: if (!base::OpenProcessHandle(otherProcess, &h)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: gChildThread = new Thread("ChildThread"); michael@0: if (!gChildThread->Start()) michael@0: fail("starting child thread"); michael@0: michael@0: TestOpensOpenedChild* a = new TestOpensOpenedChild(transport); michael@0: gChildThread->message_loop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(OpenChild, a, transport, h)); michael@0: michael@0: return a; michael@0: } michael@0: michael@0: void michael@0: TestOpensChild::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: // Stops the thread and joins it michael@0: delete gChildThread; michael@0: michael@0: if (NormalShutdown != why) michael@0: fail("unexpected destruction!"); michael@0: QuitChild(); michael@0: } michael@0: michael@0: bool michael@0: TestOpensOpenedChild::RecvHi() michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: if (!SendHelloSync()) michael@0: fail("sending HelloSync"); michael@0: if (!CallHelloRpc()) michael@0: fail("calling HelloRpc"); michael@0: if (!mGotHi) michael@0: fail("didn't answer HiRpc"); michael@0: michael@0: // Need to close the channel without message-processing frames on michael@0: // the C++ stack michael@0: MessageLoop::current()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableMethod(this, &TestOpensOpenedChild::Close)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestOpensOpenedChild::AnswerHiRpc() michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: mGotHi = true; // d00d michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child, michael@0: Transport* transport) michael@0: { michael@0: delete child; michael@0: michael@0: // Now delete the transport, which has to happen after the michael@0: // top-level actor is deleted. michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: new DeleteTask(transport)); michael@0: michael@0: // Kick off main-thread shutdown. michael@0: gMainThread->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableMethod(gOpensChild, &TestOpensChild::Close)); michael@0: } michael@0: michael@0: void michael@0: TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: AssertNotMainThread(); michael@0: michael@0: if (NormalShutdown != why) michael@0: fail("unexpected destruction!"); michael@0: michael@0: // ActorDestroy() is just a callback from IPDL-generated code, michael@0: // which needs the top-level actor (this) to stay alive a little michael@0: // longer so other things can be cleaned up. Defer shutdown to michael@0: // let cleanup finish. michael@0: MessageLoop::current()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(ShutdownTestOpensOpenedChild, michael@0: this, mTransport)); michael@0: } michael@0: michael@0: } // namespace mozilla