IPDL Tutorial

  • Revision slug: IPDL/Tutorial
  • Revision title: IPDL Tutorial
  • Revision id: 69580
  • Created:
  • Creator: Benjamin Smedberg
  • Is current revision? No
  • Comment 489 words added, 1463 words removed

Revision Content

IPDL, short for "Inter-process-communication Protocol Definition Language", is a Mozilla-specific language allowing C++ code to pass messages between processes or threads in an organized and secure way. All messages for multi-process plugins and tabs in Firefox are declared in the IPDL language.

All IPDL messages are sent between parent and a child end points, called actors. An IPDL protocol declares how actors communicate: it declares the possible messages that may be sent between actors, as well as a state machine describing when messages are allowed to be sent.

The parent actor is typically the more permanent side of the conversation:

  Parent Child
Parent/Child Actors
IPC Tabs Chrome process Content process
IPC Plugins Content process Plugin Process

Each protocol is declared in a separate file. The IPDL compiler generates several C++ headers from each IPDL protocol. This generated code manages the details of the underlying communication layer (sockets and pipes), constructing and sending messages, ensuring that all actors adhere to their specifications, and handling many error conditions. The following IPDL code defines a very basic interaction of browser and plugin actors:

protocol PPlugin
{
child:
  Init(nsCString pluginPath);
  Shutdown();

parent:
  Ready()
};

This code declares the PPlugin protocol. Two messages can be sent from the parent to the child, Init() and Shutdown(). One message can be sent from the child to the parent, Ready().

IPDL protocols start with the letter P. The file in which the protocol is declared must have a matching name, PPlugin.ipdl.

Generated C++ Code

When PPlugin.ipdl is compiled, the headers PPluginParent.h, and PPluginChild.h will be generated in ipc/ipdl/_ipdlgen of the build tree. The PPluginParent and PPluginChild classes are abstract classes that must be subclassed. Each outgoing message is a C++ method which can be called. Each incoming message is a pure-virtual C++ method which must be implemented:

class PPluginParent
{
public:
  bool SendInit(const nsCString& pluginPath) {
    // generated code to send an Init() message
  }

  bool SendShutdown() {
    // generated code to send a Shutdown() message
  }

protected:
  /**
   * A subclass of PPluginParent must implement this method to handle the Ready() message.
   */
  bool RecvReady() = 0;
};

class PPluginChild
{
protected:
  bool RecvInit(const nsCString& pluginPath) = 0;
  bool RecvShutdown() = 0;

public:
  bool SendReady() {
    // generated code to send a Ready() message
  }
};

These Parent and Child abstract classes take care of all the "protocol layer" concerns: serializing data, sending and receiving messages, and checking protocol safety. It is the responsibility of the implementor to create subclasses to perform the actual work involved in each message. Here is a dirt-simple example of how a browser implementor might use PPluginParent.

class PluginParent : public PPluginParent
{
public:
  PluginParent(const nsCString& pluginPath) {
    // launch child plugin process
    SendInit(pluginPath);
  }

  ~PluginParent() {
    SendShutdown();
  }

protected:
  bool RecvReady() {
    mObservers.Notify("ready for action");
  }
};

Here's how the PPluginChild might be used by a C++ implementor in the plugin process:

class PluginChild : public PPluginChild 
{
protected:
  void RecvInit(const nsCString& pluginPath) {
    mPluginLibrary = PR_LoadLibrary(pluginPath.get());
    SendReady();
  }
  void RecvShutdown() {
    PR_UnloadLibrary(mPluginLibrary);
  }

private:
  PRLibrary* mPluginLibrary;
};

Launching the subprocess and hooking these protocol actors into our IPC "transport layer" is beyond the scope of this document. See IPDL Processes and Threads for more details.

Because protocol messages are represented as C++ methods, it's easy to forget that they are in fact asynchronous messages: by default the C++ method will return immediately, before the message has been delivered.

Direction

Each message type includes a "direction." The message direction specifies whether the message can be sent from-parent-to-child, from-child-to-parent, or both ways. Three keywords serve as direction specifiers; child was introduced above. The second is parent, which means that the messages declared under the parent label can only be sent from-child-to-parent. The third is both, which means that the declared messages can be sent in both directions. The following artificial example shows how these specifiers are used and how these specifiers change the generated abstract actor classes.

// PDirection.ipdl
protocol PDirection
{
child:
  Foo();  // can be sent from-parent-to-child
parent:
  Bar();  // can be sent from-child-to-parent
both:
  Baz();  // can be sent both ways
};  
// PDirectionParent.h
class PDirectionParent
{
protected:
  virtual void RecvBar() = 0;
  virtual void RecvBaz() = 0;

public:
  void SendFoo() { /* boilerplate */ }
  void SendBaz() { /* boilerplate */ }
};
// PDirectionChild.h
class PDirectionChild
{
protected:
  virtual void RecvFoo() = 0;
  virtual void RecvBaz() = 0;

public:
  void SendBar() { /* boilerplate */ }
  void SendBaz() { /* boilerplate */ }
};

You can use the child, parent, and both labels multiple times in a protocol specification. They behave like public, protected, and private labels in C++.

Parameters

Message declarations allow any number of parameters. Parameters specify data that are sent with the message. Their values are serialized by the sender and deserialized by the receiver. IPDL supports built-in and custom primitive types, as well as unions and arrays.

The built-in simple types include the C++ integer types (bool, char, int, double) and XPCOM string types (nsString, nsCString). IPDL imports these automatically because they are common, and because the base IPC library knows how to serialize and deserialize these types. See ipc/ipdl/ipdl/builtins.py for the most up-to-date list of automatically imported types.

Actors may be passed as parameters. The C++ signature will accept a PProtocolParent* on one side and convert it to a PProtocolChild* on the other.

Custom primitive types. When you need to send data of a type other than one built into IPDL, you can add a using declaration in an IPDL specification.
A custom serializer and deserializer must be provided by your C++ code.

using mozilla::plugins::NPRemoteEvent;

protocol PPluginInstance
{
child:
  HandleEvent(NPRemoteEvent);
};

Unions: IPDL has built-in support for declaring discriminated unions.

using mozilla::ipc::void_t;

union Variant
{
  void_t;
  bool;
  int;
  double;
  nsCString;
  PPluginScriptableObject;
};

Semantics

Note that in all the IPDL message declarations above, the generated C++ methods corresponding to those messages always had the return type void. What if we wanted to return values from message handlers, in addition to sending parameters in the messages? If we thought of IPDL messages as C++ functions, this would be very natural. However, "returning" values from message handlers is much different from returning values from function calls. Please don't get them mixed up!

In the Plugin protocol example above, we saw the parent actor send the Init() message to the child actor. Under the covers, "sending the Init() message" encompasses code on the parent side creating some Init()-like message object in C++, serializing that message object into a sequence of bytes, and then sending those bytes to the child actor. (All this is hidden from the C++ implementor, PluginParent, above. You don't have to worry about this except to better understand message semantics.) What happens in the parent-side code once it has sent those bytes? It is this behavior that we broadly call message semantics.

In IPDL, the parent-side code has three options for what happens after those serialized bytes are sent:

  1. Continue executing. We call this asynchronous semantics; the parent is not blocked.
  2. Wait until the child acknowledges that it received the message. We call this synchronous semantics, as the parent blocks until the child receives the message and sends back a reply.
  3. The third option is more complicated and will be introduced below, after another example.

Note that when a child actor blocks after sending a synchronous message, it is only that single child actor that is blocked, and not all child actors using the same protocol. Similarly, when a parent actor blocks after sending a synchronous message, it is only that one parent actor that is blocked, and not all parent actors using the same protocol.

In the Plugin protocol example above, we might want the Init() message to be synchronous. It may not make sense for the browser to continue executing until it knows whether the plugin was successfully initialized. (We will discuss which semantics, asynchronous or synchronous, is preferred in a later section.) So for this particular case, let's extend the Plugin protocol's Init() message to be synchronous and return an error code: 0 if the plugin was initialized successfully, non-zero if not. Here's a first attempt at that

protocol Plugin {
child:
  sync Init() returns (int rv);
  Deinit();
};

We added two new keywords to the Plugin protocol, sync and returns. sync marks a message as being sent synchronously; note that the Deinit() message has no specifier. The default semantics is asynchronous. The returns keyword marks the beginning of the list of values that are returned in the reply to the message. (It is a type error to add a returns block to an asynchronous message.)

Message Semantics Strength

The above protocol will fail the IPDL type checker. Why? IPDL protocols also have "semantics specifiers", just like messages. A protocol must be declared to have semantics at least as "strong" as its strongest message semantics. Synchronous semantics is called "stronger than" asynchronous. Like message declarations, the default protocol semantics is asynchronous; however, since the Plugin protocol declares a synchronous message, this type rule is violated. The fixed up Plugin protocol is shown below.

sync protocol Plugin {
child:
  sync Init() returns (int rv);
  Deinit();
};

This new sync message with returns values changes the generated PPluginParent and PPluginChild headers, as follows.

// class PPluginParent { ...
    int SendInit() { /* boilerplate */ }

// class PPluginChild { ...
    virtual int RecvInit() = 0;

To the parent implementor, the new SendInit() method signature means that it receives an int return code back from the child. To the child implementor, the new RecvInit() method signature means that it must return an int back from the handler, signifying whether the plugin was initialized successfully.

Important: To reiterate, after the parent actor's thread calls SendInit(), it will block until the response to this message is received from the child actor. On the child side, the int returned by child implementor from RecvInit() is packed into the response to Init(), then this response is sent back to the parent. Once the parent reads the bytes of the response, it deserializes the int return value and unblocks the parent actor's thread, returning that deserialized int to the caller of SendInit(). It is very important to understand this sequence of events.

Implementation detail: IPDL supports multiple returns values, such as in the following example message declaration

// sync protocol Blah {
child:
    sync Foo(int param1, char param2) returns (long ret1, int64_t ret2);

C++ does not have syntax that allows returning multiple values from functions/methods. Additionally, IPDL needs to allow implementor code to signal error conditions, and IPDL itself needs to notify implementors of errors. To those ends, IPDL generates interface methods with nsresult return types and "outparams" for the IPDL returns values. So in reality, the C++ code generated for the Blah example above would be

// class PBlahParent { ...
    nsresult SendFoo(const int& param1, const int& param2, long* ret1, int64_t* ret2) { /* boilerplate */ }

// class PBlahChild { ...
    virtual nsresult RecvFoo(const int& param1, const int& param2, long* ret1, int64_t* ret2) = 0;

And the actual interface generated for the Plugin protocol is

// class PPluginParent { ...
    nsresult SendInit(int* rv) { /* boilerplate */ }

// class PPluginChild { ...
    virtual nsresult RecvInit(int* rv) = 0;

RPC semantics

"RPC" stands for "remote procedure call," and this third semantics models procedure call semantics. A quick summary of the difference between RPC and sync semantics is that RPC allows "re-entrant" message handlers --- that is, while an actor is blocked waiting for an "answer" to an RPC "call", it can be unblocked to handle a new, incoming RPC call.

In the example protocol below, the child actor offers a "CallMeCallYou()" RPC interface, and the parent offers a "CallYou()" RPC interface. The rpc qualifiers mean that if the parent calls "CallMeCallYou()" on the child actor, then the child actor, while servicing this call, is allowed to call back into the parent actor's "CallYou()" message.

rpc protocol Example {
child:
    rpc CallMeCallYou() returns (int rv);

parent:
    rpc CallYou() returns (int rv);
};

If this were instead a sync protocol, the child actor would not be allowed to call the parent actor's "CallYou()" method while servicing the "CallMeCallYou()" message. (The child actor would be terminated with extreme prejudice.)

TODO sequence diagram if this explanation is unclear

Preferred semantics

Use async semantics whenever possible. Asynchronous messaging in C++ has a bad rap because of the complexity and verbosity of implementing asynchronous "protocols" in C++ with event loops and massive "switch" statements. IPDL is specifically designed to make this easier and more concise.

Blocking on replies to messages is discouraged. If you absolutely need to block on a reply, use sync semantics very carefully. It is possible to get into trouble with careless uses of synchronous messages; while IPDL can (or will eventually) check and/or guarantee that your code does not deadlock, it is easy to cause nasty performance problems by blocking.

Please don't use RPC semantics. RPC inherits (nearly) all the problems of sync messages, while adding more of its own. Every time your code makes an RPC call, it must ensure that its state is "consistent" enough to handle every re-entrant (nested) call allowed by the IPDL state machine. This is hard to get right. (Why then is RPC semantics supported? Basically because of NPAPI plugins, where we can't change their existing code.)

Checkpoint 1

By this point in the guide, you should understand

  • basically what an IPDL protocol is
  • what an IPDL actor is, basically
  • what an IPDL message is, basically
  • message directions
  • message semantics
  • the relationship between IPDL and its generated interface; i.e., what your C++ code must implement

Protocol management

So far we've seen a protocol that Plugin actors use to communicate. Plugins are "singletons"; there's only one copy of libflash.so open at any time. However, there are many plugin instances. (This is a very general pattern.) IPDL needs to somehow support these "instances", and it does so through "managed" protocols, a.k.a. sub-protocols (the terms will be used interchangeably). A sub-protocol is bound to a "manager" protocol, and actors created speaking that sub-protocol are bound to the lifetime of the "manager" protocol actor that created them. The managing protocol acts like a "factory" for actors of the sub-protocol. In general, IPDL supports hierarchies of protocols, descending from a single top-level protocol.

The following example extends the Plugin protocol to manage a PluginInstance protocol. The Plugin protocol is top-level in this example.

// ----- file Plugin.ipdl
include protocol "PluginInstance.ipdl";

sync protocol Plugin {
  manages PluginInstance;

child:
  sync Init() returns (int rv);
  Deinit();

  sync PluginInstance(String type, StringArray args) returns (int rv);
  ~PluginInstance();
}

We added four new chunks of syntax. The first is a "protocol include," which are different from C++ includes in IPDL. IPDL implicitly defines "include guards" for protocol specifications, and actually reads and parses included protocol files. IPDL also allows C++ headers to be included, but these are completely transparent to IPDL (see PluginInstance.ipdl below).

The second new statement is a "manages statement." Here we declare that the Plugin protocol manages the PluginInstance protocol (which is defined in the included "PluginInstance.ipdl" specification). This means that the Plugin protocol must provide a "constructor" and "destructor" for PluginInstance actors; these are akin to factory methods. The "manages" statement also means that PluginInstance actors are tied to the lifetime of the Plugin actor that creates them --- that is, once the Plugin actor dies, all the PluginInstances become invalid.

The third and fourth new statements are "constructor" and "destructor" message declarations. The syntax was borrowed from C++, but the semantics are different. The first difference is that constructors and destructors have a direction, like other IPDL messages. They also can have arbitrary parameters and return values, and can additionally have any IPDL messaging semantics. Take our word that this is useful; the reason that constructors and destructors behave like other messages will be explained below.

Managed protocols require constructor and destructor declarations. The IPDL compiler ensures this. The constructor declaration for a managed protocol must appear in the managing protocol's IPDL file. Similarly for the destructor declaration.

Constructors and destructors turn into C++ interface methods that are almost the same as other IPDL interface methods generated for message declarations, with one notable exception: the implementing C++ class must provide actual methods for allocating and deallocating the sub-protocol actors. This is shown below.

//class PluginParent { ...
  // NOTE: these two methods are basically the same for PluginParent and PluginChild
  virtual PluginInstanceParent* PluginInstanceConstructor(
          const String& type,
          const StringArray& args,
          int* rv) = 0;
  virtual nsresult PluginInstanceDestructor(PluginInstanceParent* __a) = 0;
  
  // NOTE: PluginChild does not have "Recv*" analogues of these methods
  PluginInstanceParent* SendPluginInstanceConstructor(
          const String& type,
          const StringArray& args,
          int* rv)
  { /* boilerplate code */ }
  nsresult SendPluginInstanceDestructor(PluginInstanceParent* __a)
  { /* boilerplate code */ }
//class PluginChild {...
  // NOTE: these two methods are basically the same for PluginParent and PluginChild
  virtual PluginInstanceChild* PluginInstanceConstructor(
          const String& type,
          const StringArray& args,
          int* rv) = 0;
  virtual nsresult PluginInstanceDestructor(PluginInstanceChild* __a) = 0;

Let's break apart this example quickly. Both the PluginParent and PluginChild have to be able to allocate and deallocate raw PluginInstance's; IPDL requires your C++ code to do this by implementing the PluginInstanceConstructor/PluginInstanceDestructor interface. (Your code must do this, because IPDL has no idea what concrete classes you'll have implementing the PluginInstance abstract classes, and it's best that IPDL does not know this.)

Different from this raw C++ allocation/deallocation is the protocol-level allocation/deallocation, done by actors sending each other constructor/destructor messages. This is the second component of the C++ interface above; note that the interface generated for ctor/dtor messages is basically identical to that generated for other messages. (There's one exception: the PluginChild does not need to implement "RecvPluginInstanceConstructor()" and "RecvPluginInstancecDestructor()" handlers. IPDL knows what this code should do and thus generates it for you.) The real secret "secret sauce" to IPDL ctors/dtors is that the generated code is smart enough to know when to invoke the C++-level allocation/deallocation "factory methods." Your code need not worry about this.

Next, let's take a look at the PluginInstance sub-protocol.

// ----- file PluginInstance.ipdl
include "mozilla/plugins/PluginTypes.h"
using mozilla::plugins::PluginWindow;

sync protocol PluginInstance {
  manager Plugin;

child:
  SetWindow(PluginWindow window);
  Paint();

parent:
  sync GetBrowserValue(String key) returns (String value);
};

This protocol doesn't introduce any wildly new features; three new bits of syntax. First, PluginInstance.ipdl contains a "C++ include." This is transparent to IPDL, and is merely passed through to generated C++ code (i.e., IPDL doesn't even care if the file exists.) The second bit of new syntax is the "using statement," which pulls a non-builtin C++ type into IPDL's type system. This statement is modeled after C++'s using statement and should hopefully be familiar.

The last bit of new syntax is the "manager statement." This tells IPDL that the PluginInstance protocol is managed by the Plugin protocol. (Yes, "manager" is redundant. It exists for a variety of uninteresting reasons.)

At this point, it's worth noting two more facts about protocol management: first, to reiterate, the PluginInstance protocol can manage its own protocols. For example, in real life, PluginInstances manage "PluginScriptObject" and "PluginStream" protocols. The second bit of trivia is an implementation detail: in the current IPDL implementation, there is exactly one top-level protocol per underlying socket. However, this might change.

Protocol state machines

The astute reader might question why IPDL includes the word "protocol" when all that has been introduced so far are unstructured grab-bags of messages. IPDL allows protocol authors to define the order and structure of how messages may be sent/received by defining protocol state machines (finite state machines).

[Note that the state machine portion of the IPDL compiler is not complete as of this writing, 22 October 2009. IPDL code for state machines is accepted by the compiler, but it does not affect the generated C++, yet.]

IPDL parent and child actors communicating via a protocol are paired. Each actor in the pair follows the same state machine. The pair attempts to keep their single collective state synchronized. Though, it is possible that the parent and child actors may be momentarily out of sync while messages are transmitted.

IPDL (arbitrarily) requires state machines to be defined from the perspective of the parent side of the protocol. For example, when you see the send Msg syntax, it means "when the parent actor sends Msg".

The following example shows one such state machine for the Plugin protocol.

include protocol "PluginInstance.ipdl";

sync protocol Plugin {
  manages PluginInstance;

child:
  sync Init() returns (int rv);
  Deinit();

  sync PluginInstance(String type, StringArray args) returns (int rv);
  ~PluginInstance();

// NOTE: state machine follows
state START:
  send Init goto IDLE;

state IDLE:
  send PluginInstance goto ACTIVE;

state ACTIVE:
  send PluginInstance goto ACTIVE;
  send ~PluginInstance goto ACTIVE;
  send Deinit goto DYING;

state DYING:
  send ~PluginInstance goto DYING;
};

There are three new syntactic elements, above. First are "state declarations": the code state FOO: declares a state "FOO". (States are capitalized by convention, not because of syntactic rules.) The first state to be declared is the protocol's "start state"; when an actor is created, its initial state is the "start state."

The second new syntactic element is the trigger. The syntax send MsgDecl defines a trigger for a state transition; in this case, the trigger is sending the async or sync message "MsgDecl." The triggers are:

  1. sending an async or sync message
  2. recving an async or sync message
  3. calling an RPC
  4. answering an RPC

Aside: this is why actor ctors/dtors act like normal messages, with directions etc.: this allows them to be checked against the protocol state machine like any other message.

The third new syntactic element is a state transition. The syntax is: goto NEXT_STATE. When the trigger preceding this transition occurs, the protocol actor's internal state is changed to, in this case, "NEXT_STATE."

Another example state machine, for PluginInstance, follows.

sync protocol PluginInstance {
  manager Plugin;

child:
  SetWindow(PluginWindow window);
  Paint();

parent:
  sync GetBrowserValue(String key) returns (String value);

state START:
  send SetWindow goto SENT_WINDOW;
  recv GetBrowserValue goto START;

state SENT_WINDOW:
  send SetWindow goto SENT_WINDOW;
  send Paint goto SENT_WINDOW;
  recv GetBrowserValue goto SENT_WINDOW;
};


Note:

  • The following points will apply when the IPDL compiler fully supports states. It is incomplete as of this writing, on 22 October 2009.
  • Protocol state machines are optional, but strongly encouraged. Even simple state machines are useful.
  • All actor states, trigger matching, and transitions are managed by IPDL-generated code. Your C++ code need not manage these things.
  • All messages sent and received are checked against the protocol's state machine. If a message violates the state machine semantics defined in the IPDL code, the child process containing the child actor will be terminated with extreme prejudice, and all parent actors will be made invalid! It is up to the developer to make sure that this never happens.
  • Lots of syntactic sugar is possible for state machine definitions. Ping the Electrolysis team if you have a good proposal.

Checkpoint 2

By this point in the guide, you should understand

  • what "protocol management" means, and how manages/manager statements are used
  • sub-protocol actor constructors and destructors
  • the IPDL actor ctor/dtor C++ interface
  • how protocol state machines are used
  • how to define states, triggers, and transitions

Revision Source

<p>IPDL, short for "Inter-process-communication Protocol Definition Language", is a Mozilla-specific language allowing C++ code to pass messages between processes or threads in an organized and secure way. All messages for multi-process plugins and tabs in Firefox are declared in the IPDL language.</p>
<p>All IPDL messages are sent between <strong>parent</strong> and a <strong>child</strong> end points, called <strong>actors</strong>. An IPDL <strong>protocol</strong> declares how actors communicate: it declares the possible <strong>messages</strong> that may be sent between actors, as well as a <strong>state machine</strong> describing when messages are allowed to be sent.</p>
<p>The parent actor is typically the more permanent side of the conversation:</p>
<table border="1" cellpadding="2" cellspacing="0" style="table-layout: fixed;"> <thead> <tr> <th scope="col"> </th> <th scope="col">Parent</th> <th scope="col">Child</th> </tr> </thead> <caption>Parent/Child Actors</caption> <tbody> <tr> <th scope="row">IPC Tabs</th> <td>Chrome process</td> <td>Content process</td> </tr> <tr> <th scope="row">IPC Plugins</th> <td>Content process</td> <td>Plugin Process</td> </tr> </tbody>
</table>
<p>Each protocol is declared in a separate file. The IPDL compiler generates several C++ headers from each IPDL protocol. This generated code manages the details of the underlying communication layer (sockets and pipes), constructing and sending messages, ensuring that all actors adhere to their specifications, and handling many error conditions. The following IPDL code defines a very basic interaction of browser and plugin actors:</p>
<pre>protocol PPlugin
{
child:
  Init(nsCString pluginPath);
  Shutdown();

parent:
  Ready()
};
</pre>
<p>This code declares the <code>PPlugin</code> protocol. Two messages can be sent from the parent to the child, <code>Init()</code> and <code>Shutdown()</code>. One message can be sent from the child to the parent, <code>Ready()</code>.</p>
<div class="note">IPDL protocols start with the letter P. The file in which the protocol is declared must have a matching name, PPlugin.ipdl.</div>
<h3><span class="mw-headline">Generated C++ Code<br>
</span></h3>
<p>When PPlugin.ipdl is compiled, the headers <code>PPluginParent.h</code>, and <code>PPluginChild.h</code> will be generated in ipc/ipdl/_ipdlgen of the build tree. The PPluginParent and PPluginChild classes are abstract classes that must be subclassed. Each outgoing message is a C++ method which can be called. Each incoming message is a pure-virtual C++ method which must be implemented:</p>
<pre>class PPluginParent
{
public:
  bool SendInit(const nsCString&amp; pluginPath) {
    // generated code to send an Init() message
  }

  bool SendShutdown() {
    // generated code to send a Shutdown() message
  }

protected:
  /**
   * A subclass of PPluginParent must implement this method to handle the Ready() message.
   */
  bool RecvReady() = 0;
};

class PPluginChild
{
protected:
  bool RecvInit(const nsCString&amp; pluginPath) = 0;
  bool RecvShutdown() = 0;

public:
  bool SendReady() {
    // generated code to send a Ready() message
  }
};
</pre>
<p>These Parent and Child abstract classes take care of all the "protocol layer" concerns: serializing data, sending and receiving messages, and checking protocol safety. It is the responsibility of the implementor to create subclasses to perform the actual work involved in each message. Here is a dirt-simple example of how a browser implementor might use PPluginParent.</p>
<pre>class PluginParent : public PPluginParent
{
public:
  PluginParent(const nsCString&amp; pluginPath) {
    // launch child plugin process
    SendInit(pluginPath);
  }

  ~PluginParent() {
    SendShutdown();
  }

protected:
  bool RecvReady() {
    mObservers.Notify("ready for action");
  }
};
</pre>
<p>Here's how the PPluginChild might be used by a C++ implementor in the plugin process:</p>
<pre>class PluginChild : public PPluginChild 
{
protected:
  void RecvInit(const nsCString&amp; pluginPath) {
    mPluginLibrary = PR_LoadLibrary(pluginPath.get());
    SendReady();
  }
  void RecvShutdown() {
    PR_UnloadLibrary(mPluginLibrary);
  }

private:
  PRLibrary* mPluginLibrary;
};
</pre>
<p>Launching the subprocess and hooking these protocol actors into our IPC "transport layer" is beyond the scope of this document. See <a href="/en/IPDL/Processes_and_Threads" title="en/IPDL/Processes and Threads">IPDL Processes and Threads</a> for more details.</p>
<p>Because protocol messages are represented as C++ methods, it's easy to forget that they are in fact asynchronous messages: by default the C++ method will return immediately, before the message has been delivered.</p>
<h3><span class="mw-headline"> Direction </span></h3>
<p>Each message type includes a "direction." The message direction specifies whether the message can be sent from-parent-to-child, from-child-to-parent, or both ways. Three keywords serve as direction specifiers; <strong>child</strong> was introduced above. The second is <strong>parent</strong>, which means that the messages declared under the <strong>parent</strong> label can only be sent from-child-to-parent. The third is <strong>both</strong>, which means that the declared messages can be sent in both directions. The following artificial example shows how these specifiers are used and how these specifiers change the generated abstract actor classes.</p>
<pre>// PDirection.ipdl
protocol PDirection
{
child:
  Foo();  // can be sent from-parent-to-child
parent:
  Bar();  // can be sent from-child-to-parent
both:
  Baz();  // can be sent both ways
};  
</pre>
<pre>// PDirectionParent.h
class PDirectionParent
{
protected:
  virtual void RecvBar() = 0;
  virtual void RecvBaz() = 0;

public:
  void SendFoo() { /* boilerplate */ }
  void SendBaz() { /* boilerplate */ }
};
</pre>
<pre>// PDirectionChild.h
class PDirectionChild
{
protected:
  virtual void RecvFoo() = 0;
  virtual void RecvBaz() = 0;

public:
  void SendBar() { /* boilerplate */ }
  void SendBaz() { /* boilerplate */ }
};
</pre>
<p>You can use the <code>child</code>, <code>parent</code>, and <code>both <span style="font-family: Verdana,Tahoma,sans-serif;">labels </span></code>multiple times in a protocol specification. They behave like <code>public</code>, <code>protected</code>, and <code>private </code>labels in C++.</p>
<h3><span class="mw-headline"> Parameters </span></h3>
<p>Message declarations allow any number of <strong>parameters</strong>. Parameters specify data that are sent with the message. Their values are serialized by the sender and deserialized by the receiver. IPDL supports built-in and custom primitive types, as well as unions and arrays.</p>
<div>
<div>
<div class="f">
<p>The built-in simple types include the C++ integer types (bool, char, int, double) and XPCOM string types (<code>nsString</code>, <code>nsCString</code>). IPDL imports these automatically because they are common, and because the base IPC library knows how to serialize and deserialize these types. See <code>ipc/ipdl/ipdl/builtins.py</code> for the most up-to-date list of automatically imported types.</p>
<p>Actors may be passed as parameters. The C++ signature will accept a PProtocolParent* on one side and convert it to a PProtocolChild* on the other.</p>
<p>Custom primitive types. When you need to send data of a type other than one built into IPDL, you can add a <code>using</code> declaration in an IPDL specification.<br>
A custom serializer and deserializer must be provided by your C++ code.</p>
<pre>using mozilla::plugins::NPRemoteEvent;

protocol PPluginInstance
{
child:
  HandleEvent(NPRemoteEvent);
};
</pre>
<p>Unions: IPDL has built-in support for declaring discriminated unions.</p>
<pre>using mozilla::ipc::void_t;

union Variant
{
  void_t;
  bool;
  int;
  double;
  nsCString;
  PPluginScriptableObject;
};
</pre>
</div>
</div>
</div>
<h3><span class="mw-headline"> Semantics </span></h3>
<p>Note that in all the IPDL message declarations above, the generated C++ methods corresponding to those messages always had the return type <code>void</code>. What if we wanted to <em>return</em> values from message handlers, in addition to sending <em>parameters</em> in the messages? If we thought of IPDL messages as C++ functions, this would be very natural. However, "returning" values from message handlers is much different from returning values from function calls. Please don't get them mixed up!</p>
<p>In the Plugin protocol example above, we saw the parent actor send the Init() message to the child actor. Under the covers, "sending the Init() message" encompasses code on the parent side creating some Init()-like message object in C++, serializing that message object into a sequence of bytes, and then sending those bytes to the child actor. (All this is hidden from the C++ implementor, PluginParent, above. You don't have to worry about this except to better understand message semantics.) What happens in the parent-side code once it has sent those bytes? It is this behavior that we broadly call <em>message semantics</em>.</p>
<p>In IPDL, the parent-side code has three options for what happens after those serialized bytes are sent:</p>
<ol> <li>Continue executing. We call this <strong>asynchronous</strong> semantics; the parent is not blocked.</li> <li>Wait until the child acknowledges that it received the message. We call this <strong>synchronous</strong> semantics, as the parent blocks until the child receives the message and sends back a reply.</li> <li>The third option is more complicated and will be introduced below, after another example.</li>
</ol>
<p>Note that when a child actor blocks after sending a synchronous message, it is only that single child actor that is blocked, and not all child actors using the same protocol. Similarly, when a parent actor blocks after sending a synchronous message, it is only that one parent actor that is blocked, and not all parent actors using the same protocol.</p>
<p>In the Plugin protocol example above, we might want the Init() message to be synchronous. It may not make sense for the browser to continue executing until it knows whether the plugin was successfully initialized. (We will discuss which semantics, asynchronous or synchronous, is preferred in a later section.) So for this particular case, let's extend the Plugin protocol's Init() message to be synchronous and return an error code: 0 if the plugin was initialized successfully, non-zero if not. Here's a first attempt at that</p>
<pre><strong>protocol</strong> Plugin {
<strong>child</strong>:
  <strong>sync</strong> Init() <strong>returns</strong> (int rv);
  Deinit();
};
</pre>
<p>We added two new keywords to the Plugin protocol, <strong>sync</strong> and <strong>returns</strong>. <strong>sync</strong> marks a message as being sent synchronously; note that the Deinit() message has no specifier. The default semantics is asynchronous. The <strong>returns</strong> keyword marks the beginning of the list of values that are returned in the reply to the message. (It is a type error to add a <strong>returns</strong> block to an asynchronous message.)</p>
<p><a id="Message_Semantics_Strength" name="Message_Semantics_Strength"></a></p><a id="Message_Semantics_Strength" name="Message_Semantics_Strength">
<h4><span class="mw-headline"> Message Semantics Strength </span></h4>
<p>The above protocol will fail the IPDL type checker. Why? IPDL protocols also have "semantics specifiers", just like messages. A protocol must be declared to have semantics at least as "strong" as its strongest message semantics. Synchronous semantics is called "stronger than" asynchronous. Like message declarations, the default protocol semantics is asynchronous; however, since the Plugin protocol declares a synchronous message, this type rule is violated. The fixed up Plugin protocol is shown below.</p>
<pre><strong>sync</strong> <strong>protocol</strong> Plugin {
<strong>child</strong>:
  <strong>sync</strong> Init() <strong>returns</strong> (int rv);
  Deinit();
};
</pre>
<p>This new <strong>sync</strong> message with <strong>returns</strong> values changes the generated PPluginParent and PPluginChild headers, as follows.</p>
<pre>// class PPluginParent { ...
    <strong>int</strong> SendInit() { /* boilerplate */ }

// class PPluginChild { ...
    <strong>virtual</strong> <strong>int</strong> RecvInit() = 0;
</pre>
<p>To the parent implementor, the new SendInit() method signature means that it receives an int return code back from the child. To the child implementor, the new RecvInit() method signature means that it must return an int back from the handler, signifying whether the plugin was initialized successfully.</p>
<p><strong>Important</strong>: To reiterate, after the parent actor's thread calls SendInit(), it will block until the response to this message is received from the child actor. On the child side, the int returned by child implementor from RecvInit() is packed into the response to Init(), then this response is sent back to the parent. Once the parent reads the bytes of the response, it deserializes the int return value and unblocks the parent actor's thread, returning that deserialized int to the caller of SendInit(). It is very important to understand this sequence of events.</p>
<p><strong>Implementation detail</strong>: IPDL supports multiple <strong>returns</strong> values, such as in the following example message declaration</p>
<pre>// sync protocol Blah {
<strong>child</strong>:
    <strong>sync</strong> Foo(int param1, char param2) <strong>returns</strong> (long ret1, int64_t ret2);
</pre>
<p>C++ does not have syntax that allows returning multiple values from functions/methods. Additionally, IPDL needs to allow implementor code to signal error conditions, and IPDL itself needs to notify implementors of errors. To those ends, IPDL generates interface methods with nsresult return types and "outparams" for the IPDL <strong>returns</strong> values. So in reality, the C++ code generated for the Blah example above would be</p>
<pre>// class PBlahParent { ...
    nsresult SendFoo(<strong>const</strong> <strong>int</strong>&amp; param1, <strong>const</strong> <strong>int</strong>&amp; param2, <strong>long</strong>* ret1, int64_t* ret2) { /* boilerplate */ }

// class PBlahChild { ...
    <strong>virtual</strong> nsresult RecvFoo(<strong>const</strong> <strong>int</strong>&amp; param1, <strong>const</strong> <strong>int</strong>&amp; param2, <strong>long</strong>* ret1, int64_t* ret2) = 0;
</pre>
<p>And the actual interface generated for the Plugin protocol is</p>
<pre>// class PPluginParent { ...
    nsresult SendInit(<strong>int</strong>* rv) { /* boilerplate */ }

// class PPluginChild { ...
    <strong>virtual</strong> nsresult RecvInit(<strong>int</strong>* rv) = 0;
</pre>
</a><p><a id="Message_Semantics_Strength" name="Message_Semantics_Strength"></a><a id="RPC_semantics" name="RPC_semantics"></a></p><a id="RPC_semantics" name="RPC_semantics">
<h3><span class="mw-headline"> RPC semantics </span></h3>
<p>"RPC" stands for "remote procedure call," and this third semantics models procedure call semantics. A quick summary of the difference between RPC and sync semantics is that RPC allows "re-entrant" message handlers --- that is, while an actor is blocked waiting for an "answer" to an RPC "call", it can be <em>unblocked</em> to handle a new, incoming RPC <em>call</em>.</p>
<p>In the example protocol below, the child actor offers a "CallMeCallYou()" RPC interface, and the parent offers a "CallYou()" RPC interface. The <code><strong>rpc</strong></code> qualifiers mean that if the parent calls "CallMeCallYou()" on the child actor, then the child actor, while servicing this call, is allowed to call back into the parent actor's "CallYou()" message.</p>
<pre><strong>rpc</strong> <strong>protocol</strong> Example {
<strong>child</strong>:
    <strong>rpc</strong> CallMeCallYou() <strong>returns</strong> (int rv);

<strong>parent</strong>:
    <strong>rpc</strong> CallYou() <strong>returns</strong> (int rv);
};
</pre>
<p>If this were instead a sync protocol, the child actor would not be allowed to call the parent actor's "CallYou()" method while servicing the "CallMeCallYou()" message. (The child actor would be terminated with extreme prejudice.)</p>
<p><strong>TODO</strong> sequence diagram if this explanation is unclear</p>
</a><p><a id="RPC_semantics" name="RPC_semantics"></a><a id="Preferred_semantics" name="Preferred_semantics"></a></p><a id="Preferred_semantics" name="Preferred_semantics">
<h3><span class="mw-headline"> Preferred semantics </span></h3>
<p>Use <strong>async</strong> semantics whenever possible. Asynchronous messaging in C++ has a bad rap because of the complexity and verbosity of implementing asynchronous "protocols" in C++ with event loops and massive "switch" statements. IPDL is specifically designed to make this easier and more concise.</p>
<p>Blocking on replies to messages is discouraged. If you absolutely need to block on a reply, use <strong>sync</strong> semantics <em>very carefully</em>. It is possible to get into trouble with careless uses of synchronous messages; while IPDL can (or will eventually) check and/or guarantee that your code does not deadlock, it is easy to cause nasty performance problems by blocking.</p>
<p>Please don't use RPC semantics. RPC inherits (nearly) all the problems of sync messages, while adding more of its own. Every time your code makes an RPC call, it must ensure that its state is "consistent" enough to handle every re-entrant (nested) call allowed by the IPDL state machine. This is hard to get right. (Why then is RPC semantics supported? Basically because of NPAPI plugins, where we can't change their existing code.)</p>
</a><p><a id="Preferred_semantics" name="Preferred_semantics"></a><a id="Checkpoint_1" name="Checkpoint_1"></a></p><a id="Checkpoint_1" name="Checkpoint_1">
<h2><span class="mw-headline"> Checkpoint 1 </span></h2>
<p>By this point in the guide, you should understand</p>
<ul> <li>basically what an IPDL protocol is</li> <li>what an IPDL actor is, basically</li> <li>what an IPDL message is, basically</li> <li>message directions</li> <li>message semantics</li> <li>the relationship between IPDL and its generated interface; i.e., what your C++ code must implement</li>
</ul>
</a><p><a id="Checkpoint_1" name="Checkpoint_1"></a><a id="Protocol_management" name="Protocol_management"></a></p><a id="Protocol_management" name="Protocol_management">
<h2><span class="mw-headline"> Protocol management </span></h2>
<p>So far we've seen a protocol that Plugin actors use to communicate. Plugins are "singletons"; there's only one copy of libflash.so open at any time. However, there are many plugin <em>instances</em>. (This is a very general pattern.) IPDL needs to somehow support these "instances", and it does so through "managed" protocols, a.k.a. sub-protocols (the terms will be used interchangeably). A sub-protocol is bound to a "manager" protocol, and actors created speaking that sub-protocol are bound to the lifetime of the "manager" protocol actor that created them. The managing protocol acts like a "factory" for actors of the sub-protocol. In general, IPDL supports <em>hierarchies</em> of protocols, descending from a single <em>top-level</em> protocol.</p>
<p>The following example extends the Plugin protocol to manage a PluginInstance protocol. The Plugin protocol is top-level in this example.</p>
<pre>// ----- file Plugin.ipdl
<strong>include</strong> <strong>protocol</strong> "PluginInstance.ipdl";

<strong>sync</strong> <strong>protocol</strong> Plugin {
  <strong>manages</strong> PluginInstance;

<strong>child</strong>:
  <strong>sync</strong> Init() <strong>returns</strong> (int rv);
  Deinit();

  <strong>sync</strong> PluginInstance(String type, StringArray args) <strong>returns</strong> (int rv);
  ~PluginInstance();
}
</pre>
<p>We added four new chunks of syntax. The first is a "protocol include," which are different from C++ includes in IPDL. IPDL implicitly defines "include guards" for protocol specifications, and actually reads and parses included protocol files. IPDL also allows C++ headers to be included, but these are completely transparent to IPDL (see PluginInstance.ipdl below).</p>
<p>The second new statement is a "manages statement." Here we declare that the Plugin protocol manages the PluginInstance protocol (which is defined in the included "PluginInstance.ipdl" specification). This means that the Plugin protocol must provide a "constructor" and "destructor" for PluginInstance actors; these are akin to factory methods. The "manages" statement also means that PluginInstance actors are tied to the lifetime of the Plugin actor that creates them --- that is, once the Plugin actor dies, all the PluginInstances become invalid.</p>
<p>The third and fourth new statements are "constructor" and "destructor" message declarations. The syntax was borrowed from C++, but the semantics are different. The first difference is that constructors and destructors have a direction, like other IPDL messages. They also can have arbitrary parameters and return values, and can additionally have any IPDL messaging semantics. Take our word that this is useful; the reason that constructors and destructors behave like other messages will be explained below.</p>
<p>Managed protocols require constructor and destructor declarations. The IPDL compiler ensures this. The constructor declaration for a managed protocol must appear in the managing protocol's IPDL file. Similarly for the destructor declaration.</p>
<p>Constructors and destructors turn into C++ interface methods that are almost the same as other IPDL interface methods generated for message declarations, with one notable exception: the implementing C++ class must provide actual methods for allocating and deallocating the sub-protocol actors. This is shown below.</p>
<pre>//class PluginParent { ...
  // NOTE: these two methods are basically the same for PluginParent and PluginChild
  <strong>virtual</strong> PluginInstanceParent* PluginInstanceConstructor(
          <strong>const</strong> String&amp; type,
          <strong>const</strong> StringArray&amp; args,
          <strong>int</strong>* rv) = 0;
  <strong>virtual</strong> nsresult PluginInstanceDestructor(PluginInstanceParent* __a) = 0;
  
  // NOTE: PluginChild does not have "Recv*" analogues of these methods
  PluginInstanceParent* SendPluginInstanceConstructor(
          <strong>const</strong> String&amp; type,
          <strong>const</strong> StringArray&amp; args,
          <strong>int</strong>* rv)
  { /* boilerplate code */ }
  nsresult SendPluginInstanceDestructor(PluginInstanceParent* __a)
  { /* boilerplate code */ }
</pre>
<pre>//class PluginChild {...
  // NOTE: these two methods are basically the same for PluginParent and PluginChild
  <strong>virtual</strong> PluginInstanceChild* PluginInstanceConstructor(
          <strong>const</strong> String&amp; type,
          <strong>const</strong> StringArray&amp; args,
          <strong>int</strong>* rv) = 0;
  <strong>virtual</strong> nsresult PluginInstanceDestructor(PluginInstanceChild* __a) = 0;
</pre>
<p>Let's break apart this example quickly. Both the PluginParent and PluginChild have to be able to allocate and deallocate raw PluginInstance's; IPDL requires your C++ code to do this by implementing the PluginInstanceConstructor/PluginInstanceDestructor interface. (Your code must do this, because IPDL has no idea what concrete classes you'll have implementing the PluginInstance abstract classes, and it's best that IPDL does not know this.)</p>
<p>Different from this <em>raw</em> C++ allocation/deallocation is the <em>protocol-level</em> allocation/deallocation, done by actors sending each other constructor/destructor messages. This is the second component of the C++ interface above; note that the interface generated for ctor/dtor messages is basically identical to that generated for other messages. (There's one exception: the PluginChild does not need to implement "RecvPluginInstanceConstructor()" and "RecvPluginInstancecDestructor()" handlers. IPDL knows what this code should do and thus generates it for you.) The real secret "secret sauce" to IPDL ctors/dtors is that the generated code is smart enough to know when to invoke the C++-level allocation/deallocation "factory methods." Your code need not worry about this.</p>
<p>Next, let's take a look at the PluginInstance sub-protocol.</p>
<pre>// ----- file PluginInstance.ipdl
<strong>include</strong> "mozilla/plugins/PluginTypes.h"
<strong>using</strong> mozilla::plugins::PluginWindow;

<strong>sync</strong> <strong>protocol</strong> PluginInstance {
  <strong>manager</strong> Plugin;

<strong>child</strong>:
  SetWindow(PluginWindow window);
  Paint();

<strong>parent</strong>:
  <strong>sync</strong> GetBrowserValue(String key) <strong>returns</strong> (String value);
};
</pre>
<p>This protocol doesn't introduce any wildly new features; three new bits of syntax. First, PluginInstance.ipdl contains a "C++ include." This is transparent to IPDL, and is merely passed through to generated C++ code (i.e., IPDL doesn't even care if the file exists.) The second bit of new syntax is the "using statement," which pulls a non-builtin C++ type into IPDL's type system. This statement is modeled after C++'s using statement and should hopefully be familiar.</p>
<p>The last bit of new syntax is the "manager statement." This tells IPDL that the PluginInstance protocol is managed by the Plugin protocol. (Yes, "manager" is redundant. It exists for a variety of uninteresting reasons.)</p>
<p>At this point, it's worth noting two more facts about protocol management: first, to reiterate, the PluginInstance protocol can manage <em>its own</em> protocols. For example, in real life, PluginInstances manage "PluginScriptObject" and "PluginStream" protocols. The second bit of trivia is an implementation detail: in the current IPDL implementation, there is exactly one top-level protocol per underlying socket. However, this might change.</p>
</a><p><a id="Protocol_management" name="Protocol_management"></a><a id="Protocol_state_machines" name="Protocol_state_machines"></a></p><a id="Protocol_state_machines" name="Protocol_state_machines">
<h2><span class="mw-headline"> Protocol state machines </span></h2>
<p>The astute reader might question why IPDL includes the word "protocol" when all that has been introduced so far are unstructured grab-bags of messages. IPDL allows protocol authors to define the order and structure of how messages may be sent/received by defining protocol <em>state machines</em> (finite state machines).</p>
<p>[Note that the state machine portion of the IPDL compiler is not complete as of this writing, 22 October 2009. IPDL code for state machines is accepted by the compiler, but it does not affect the generated C++, yet.]</p>
<p>IPDL parent and child actors communicating via a protocol are paired. Each actor in the pair follows the same state machine. The pair attempts to keep their single collective state synchronized. Though, it is possible that the parent and child actors may be momentarily out of sync while messages are transmitted.</p>
<p>IPDL (arbitrarily) requires state machines to be defined from the perspective of the <strong>parent</strong> side of the protocol. For example, when you see the <code><strong>send</strong> Msg</code> syntax, it means "when the parent actor sends Msg".</p>
<p>The following example shows one such state machine for the Plugin protocol.</p>
<pre><strong>include</strong> <strong>protocol</strong> "PluginInstance.ipdl";

<strong>sync</strong> <strong>protocol</strong> Plugin {
  <strong>manages</strong> PluginInstance;

<strong>child</strong>:
  <strong>sync</strong> Init() <strong>returns</strong> (int rv);
  Deinit();

  <strong>sync</strong> PluginInstance(String type, StringArray args) <strong>returns</strong> (int rv);
  ~PluginInstance();

// NOTE: state machine follows
<strong>state</strong> START:
  <strong>send</strong> Init <strong>goto</strong> IDLE;

<strong>state</strong> IDLE:
  <strong>send</strong> PluginInstance <strong>goto</strong> ACTIVE;

<strong>state</strong> ACTIVE:
  <strong>send</strong> PluginInstance <strong>goto</strong> ACTIVE;
  <strong>send</strong> ~PluginInstance <strong>goto</strong> ACTIVE;
  <strong>send</strong> Deinit <strong>goto</strong> DYING;

<strong>state</strong> DYING:
  <strong>send</strong> ~PluginInstance <strong>goto</strong> DYING;
};
</pre>
<p>There are three new syntactic elements, above. First are "state declarations": the code <code><strong>state</strong> FOO:</code> declares a state "FOO". (States are capitalized by convention, not because of syntactic rules.) The first state to be declared is the protocol's "start state"; when an actor is created, its initial state is the "start state."</p>
<p>The second new syntactic element is the <em>trigger</em>. The syntax <code><strong>send</strong> MsgDecl</code> defines a trigger for a state <em>transition</em>; in this case, the trigger is <code><strong>send</strong></code>ing the async or sync message "MsgDecl." The triggers are:</p>
<ol> <li><code><strong>send</strong></code>ing an async or sync message</li> <li><code><strong>recv</strong></code>ing an async or sync message</li> <li><code><strong>call</strong></code>ing an RPC</li> <li><code><strong>answer</strong></code>ing an RPC</li>
</ol>
<p><strong>Aside</strong>: this is why actor ctors/dtors act like normal messages, with directions etc.: this allows them to be checked against the protocol state machine like any other message.</p>
<p>The third new syntactic element is a state <em>transition</em>. The syntax is: <code><strong>goto</strong> NEXT_STATE</code>. When the trigger preceding this transition occurs, the protocol actor's internal state is changed to, in this case, "NEXT_STATE."</p>
<p>Another example state machine, for PluginInstance, follows.</p>
<pre><strong>sync</strong> <strong>protocol</strong> PluginInstance {
  <strong>manager</strong> Plugin;

<strong>child</strong>:
  SetWindow(PluginWindow window);
  Paint();

<strong>parent</strong>:
  <strong>sync</strong> GetBrowserValue(String key) <strong>returns</strong> (String value);

<strong>state</strong> START:
  <strong>send</strong> SetWindow <strong>goto</strong> SENT_WINDOW;
  <strong>recv</strong> GetBrowserValue <strong>goto</strong> START;

<strong>state</strong> SENT_WINDOW:
  <strong>send</strong> SetWindow <strong>goto</strong> SENT_WINDOW;
  <strong>send</strong> Paint <strong>goto</strong> SENT_WINDOW;
  <strong>recv</strong> GetBrowserValue <strong>goto</strong> SENT_WINDOW;
};
</pre>
<p><br>
Note:</p>
<ul> <li>The following points will apply when the IPDL compiler fully supports states. It is incomplete as of this writing, on 22 October 2009.</li> <li>Protocol state machines are optional, but strongly encouraged. Even simple state machines are useful.</li> <li>All actor states, trigger matching, and transitions are managed by IPDL-generated code. Your C++ code need not manage these things.</li> <li>All messages sent and received are checked against the protocol's state machine. <em>If a message violates the state machine semantics defined in the IPDL code, the child process containing the child actor will be terminated with extreme prejudice, and all parent actors will be made invalid!</em> It is up to the developer to make sure that this never happens.</li> <li>Lots of syntactic sugar is possible for state machine definitions. Ping the Electrolysis team if you have a good proposal.</li>
</ul>
</a><p><a id="Protocol_state_machines" name="Protocol_state_machines"></a><a id="Checkpoint_2" name="Checkpoint_2"></a></p><a id="Checkpoint_2" name="Checkpoint_2">
<h2><span class="mw-headline"> Checkpoint 2 </span></h2>
<p>By this point in the guide, you should understand</p>
<ul> <li>what "protocol management" means, and how manages/manager statements are used</li> <li>sub-protocol actor constructors and destructors</li> <li>the IPDL actor ctor/dtor C++ interface</li> <li>how protocol state machines are used</li> <li>how to define states, triggers, and transitions</li>
</ul>
</a>
Revert to this revision