Changes

Jump to: navigation, search

IPDL/Five minute example

8,611 bytes removed, 03:45, 18 July 2009
Write a slightly less trivial protocol
fly by eventually. Remember, the first and third messages are printed by the parent process, and the second is printed by the child process.
== Write Exercise: write a slightly less trivial protocol ==
<!--Try writing a system that behaves as follows.* the parent keeps a map of String keys to String values* the parent allows the child to:** synchronously map a key to a new value** asynchronously query the current value of a key** asynchronously query the values of an array of keys
== Write After writing your own protocol, take a look at the code in <code>ipc/test-harness</code> that implements the IPDL specification ==part of this exercise. Depending on your interpretation of the problem statement this code may not look much like yours. But it's worth refreshing your knowledge of the language features the example code employs.
Put this in the file <code>ipc/test-harness/Test.ipdl</code>.  namespace mozilla { namespace test { sync protocol Test { both: sync Ping() returns (int status); parent: GetValue(String key); GetValues(StringArray keys); sync SetValue(String key, String val) returns (bool ok); child: TellValue(String key, String val); TellValues(StringArray keys, StringArray vals); state START: recv Ping goto START; send Ping goto START; recv SetValue goto HAVE_VALUES; state HAVE_VALUES: recv Ping goto HAVE_VALUES; send Ping goto HAVE_VALUES; recv SetValue goto HAVE_VALUES; recv GetValue goto TELLING_VALUE; recv GetValues goto TELLING_VALUES; state TELLING_VALUE: send TellValue goto HAVE_VALUES; state TELLING_VALUES: send TellValues goto HAVE_VALUES; }; } // namespace test } // namespace mozilla == Hook the IPDL file into our build system == * Add <code>ipc/test-harness</code> to the IPDLDIRS variable in <code>ipc/ipdl/Makefile.in</code>* Create the file <code>ipc/test-harness/ipdl.mk</code> and add the following text to it.  IPDLSRCS = \ Test.ipdl \ $(NULL) == Create the C++ implementation stubs == * Run ipc/test-harness$ python ../ipdl.py -d /tmp Test.ipdl* Open <code>/tmp/mozilla/test/TestProtocolParent.h</code>. Look for the text "Skeleton implementation of abstract actor class."** copy the "Header file contents" into the file <code>ipc/test-harness/TestParent.h</code>** make the TestParent constructor and destructor public** copy the "C++ file contents" into <code>ipc/test-harness/TestParent.cpp</code>** globally replace the text <code>ActorImpl</code> with <code>TestParent</code> in both files.** set up namespaces as you wish. The checked-in example puts TestParent in the mozilla::test namespace.* Repeat the above step for <code>TestProtocolChild.h</code> and <code>TestChild.(h, cpp)</code> == Hook the C++ stubs into your build configuration == This is beyond the scope of this guide. See <code>ipc/test-harness/Makefile.in</code> for an example. It's a good idea to check now that everything compiles. == Create the subprocess class == This class is instantiated in the main process, and launches and tracks the actual child process. Create the files <code>ipc/test-harness/TestProcessParent.h</code> and <code>ipc/test-harness/TestProcessParent.cpp</code> with the following content.  // TestProcessParent.h #ifndef mozilla_test_TestProcessParent_h #define mozilla_test_TestProcessParent_h 1 #include "mozilla/ipc/GeckoChildProcessHost.h" namespace mozilla { namespace test { class TestProcessParent : mozilla::ipc::GeckoChildProcessHost { public: TestProcessParent(); ~TestProcessParent(); /** * Asynchronously launch the plugin process. */ // Could override parent Launch, but don't need to here //bool Launch(); private: DISALLOW_EVIL_CONSTRUCTORS(TestProcessParent); }; } // namespace plugins } // namespace mozilla #endif // ifndef mozilla_test_TestProcessParent_h  // TestProcessParent.cpp #include "mozilla/test/TestProcessParent.h" using mozilla::ipc::GeckoChildProcessHost; namespace mozilla { namespace test { TestProcessParent::TestProcessParent() : GeckoChildProcessHost(GeckoChildProcess_TestHarness) { } TestProcessParent::~TestProcessParent() { } } // namespace test } // namespace mozilla Now open <code>nsXULAppAPI.h</code> and add the new value <code>GeckoChildProcess_TestHarness</code> to the enumeration <code>GeckoChildProcessType</code>. (Yes, this sucks. Sorry.) Hook this new code into your build system and verify that everything still compiles. == Create the child process's main thread == The child process's main thread will hook up the TestChild actor to an IPC channel. Create the files <code>ipc/test-harness/TestThreadChild.h</code> and <code>ipc/test-harness/TestThreadChild.cpp</code> with the following content.  // TestThreadChild.h #ifndef mozilla_test_TestThreadChild_h #define mozilla_test_TestThreadChild_h #include "mozilla/ipc/GeckoThread.h" #include "mozilla/test/TestChild.h" namespace mozilla { namespace test { class TestThreadChild : public mozilla::ipc::GeckoThread { public: TestThreadChild(); ~TestThreadChild(); protected: virtual void Init(); virtual void CleanUp(); private: TestChild mChild; }; } // namespace test } // namespace mozilla #endif // ifndef mozilla_test_TestThreadChild_h  // TestThreadChild.cpp #include "mozilla/test/TestThreadChild.h" using mozilla::test::TestThreadChild; using mozilla::ipc::GeckoThread; TestThreadChild::TestThreadChild() : GeckoThread() { } TestThreadChild::~TestThreadChild() { } void TestThreadChild::Init() { GeckoThread::Init(); mChild.Open(channel(), owner_loop()); } void TestThreadChild::CleanUp() { GeckoThread::CleanUp(); mChild.Close(); } Hook these into your build and check that everything still compiles. Open <code>xre/toolkit/nsEmbedFunctions.cpp</code>. See that #include "mozilla/test/TestThreadChild.h" using mozilla::test::TestThreadChild;get added to the file somewhere. Go to the function <code>XRE_InitChildProcess</code>. Add the following case to the switch. case GeckoChildProcess_TestHarness: mainThread = new TestThreadChild(); break;(Yes, this sucks. Sorry) == Get the subprocess to launch == Here we reach a fork in the road of implementation. You can either continue to follow this guide, which will walk through creating a new, dummy, non-Firefox main process, or you can roughly follow the steps here but adapt them so that they end up launching your subprocess class from the existing Firefox process. '''REMEMBER''': these steps create a new, non-Firefox main process. Create the directory <code>ipc/test-harness/app</code> and see that it gets added to the <code>TOOL_DIRS</code> var in your Makefile. Create the file <code>ipc/test-harness/app/TestApp.cpp</code> with the following content. #include "nsXULAppAPI.h" #if defined(XP_WIN) #include <windows.h> #include "nsWindowsWMain.cpp" #endif int main(int argc, char* argv[]) { return XRE_RunIPCTestHarness(argc, argv); } Now re-open <code>xpcom/build/nsXULAppAPI.h</code> and add the declaration XRE_API(int, XRE_RunIPCTestHarness, (int aArgc, char* aArgv[])) Re-open <code>toolkit/xre/nsEmbedFunctions.cpp</code>. See that #include "mozilla/test/TestProcessParent.h" #include "mozilla/test/TestParent.h" using mozilla::test::TestProcessParent; using mozilla::test::TestParent;get added somewhere in the file. Add the following code.  //----------------------------------------------------------------------------- // TestHarness static void IPCTestHarnessMain(TestProcessParent* subprocess) { TestParent parent; parent.Open(subprocess->GetChannel()); parent.DoStuff(); } static void IPCTestHarnessLaunchSubprocess(TestProcessParent* subprocess, MessageLoop* mainLoop) { NS_ASSERTION(subprocess->Launch(), "can't launch subprocess"); mainLoop->PostTask(FROM_HERE, NewRunnableFunction(IPCTestHarnessMain, subprocess)); } static void IPCTestHarnessPostLaunchSubprocessTask(void* data) { TestProcessParent* subprocess = new TestProcessParent(); MessageLoop* ioLoop = BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); ioLoop->PostTask(FROM_HERE, NewRunnableFunction(IPCTestHarnessLaunchSubprocess, subprocess, MessageLoop::current())); } int XRE_RunIPCTestHarness(int aArgc, char* aArgv[]) { nsresult rv = XRE_InitParentProcess( aArgc, aArgv, IPCTestHarnessPostLaunchSubprocessTask, NULL); NS_ENSURE_SUCCESS(rv, 1); return 0; } (Sorry, this code is a little hard to follow.) This code will not compile because <code>TestParent::DoStuff()</code> has not been implemented. Everything else is set up thoughHappy piddling== Make the TestParent/TestChild do stuff == This is up to your imagination. The following is a quick example. Edit the <code>RecvPing()</code> function in <code>ipc/test-harness/TestChild.cpp</code> to be  nsresult TestChild::RecvPing(int* status) { *status = 42; return NS_OK; } and add this code to <code>ipc/test-harness/TestParent.cpp</code>.  void TestParent::DoStuff() { int ping; SendPing(&ping); printf("[TestParent] child replied to ping with status code %d\n", ping); } '''Suggested further exercises''': change <code>ipc/test-harness/Test.ipdl</code>; add new messages, change the state machines, and so on. See how the generated headers change. Edit the TestParent and TestChild to implement your new messages. == Run the test-harness == If you followed the path to creating a standalone binary, you can run the test harness by executing <code>$objdir/dist/bin/ipctestharness</code>. -->
Confirm
699
edits

Navigation menu