IPDL/Getting started: Difference between revisions

No edit summary
Line 284: Line 284:


== Protocol management ==
== 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 very different.  First of all, 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.  For now, you'll have to take our word that this is useful.  The reason for constructors and destructors behaving like other messages will be explained below.
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.
Confirmed users
699

edits