1IPDL: Inter-Thread and Inter-Process Message Passing 2==================================================== 3 4The Idea 5-------- 6 7**IPDL**, the "Inter-[thread|process] Protocol Definition Language", is the 8Mozilla-specific language that allows code to communicate between system 9threads or processes in a standardized, efficient, safe, secure and 10platform-agnostic way. IPDL communications take place between *parent* and 11*child* objects called *actors*. The architecture is inspired by the `actor 12model <https://en.wikipedia.org/wiki/Actor_model>`_. 13 14.. note:: 15 IPDL actors differ from the actor model in one significant way -- all 16 IPDL communications are *only* between a parent and its only child. 17 18The actors that constitute a parent/child pair are called **peers**. Peer 19actors communicate through an **endpoint**, which is an end of a message pipe. 20An actor is explicitly bound to its endpoint, which in turn is bound to a 21particular thread soon after it is constructed. An actor never changes its 22endpoint and may only send and receive predeclared **messages** from/to that 23endpoint, on that thread. Violations result in runtime errors. A thread may 24be bound to many otherwise unrelated actors but an endpoint supports 25**top-level** actors and any actors they **manage** (see below). 26 27.. note:: 28 More precisely, endpoints can be bound to any ``nsISerialEventTarget``, 29 which are themselves associated with a specific thread. By default, 30 IPDL will bind to the current thread's "main" serial event target, 31 which, if it exists, is retrieved with ``GetCurrentSerialEventTarget``. 32 For the sake of clarity, this document will frequently refer to actors 33 as bound to threads, although the more precise interpretation of serial 34 event targets is also always valid. 35 36.. note:: 37 Internally, we use the "Ports" component of the `Chromium Mojo`_ library 38 to *multiplex* multiple endpoints (and, therefore, multiple top-level 39 actors). This means that the endpoints communicate over the same native 40 pipe, which conserves limited OS resources. The implications of this are 41 discussed in `IPDL Best Practices`_. 42 43Parent and child actors may be bound to threads in different processes, in 44different threads in the same process, or even in the same thread in the same 45process. That last option may seem unreasonable but actors are versatile and 46their layout can be established at run-time so this could theoretically arise 47as the result of run-time choices. One large example of this versatility is 48``PCompositorBridge`` actors, which in different cases connect endpoints in the 49main process and the GPU process (for UI rendering on Windows), in a content 50process and the GPU process (for content rendering on Windows), in the main 51process and the content process (for content rendering on Mac, where there is 52no GPU process), or between threads on the main process (UI rendering on Mac). 53For the most part, this does not require elaborate or redundant coding; it 54just needs endpoints to be bound judiciously at runtime. The example in 55:ref:`Connecting With Other Processes` shows one way this can be done. It 56also shows that, without proper plain-language documentation of *all* of the 57ways endpoints are configured, this can quickly lead to unmaintainable code. 58Be sure to document your endpoint bindings throroughly!!! 59 60.. _Chromium Mojo: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/mojo/core/README.md#Port 61 62The Approach 63------------ 64 65The actor framework will schedule tasks to run on its associated event target, 66in response to messages it receives. Messages are specified in an IPDL 67**protocol** file and the response handler tasks are defined per-message by C++ 68methods. As actors only communicate in pairs, and each is bound to one thread, 69sending is always done sequentially, never concurrently (same for receiving). 70This means that it can, and does, guarantee that an actor will always receive 71messages in the same order they were sent by its related actor -- and that this 72order is well defined since the related actor can only send from one thread. 73 74.. warning:: 75 There are a few (rare) exceptions to the message order guarantee. They 76 include `synchronous nested`_ messages, `interrupt`_ messages, and 77 messages with a ``[Priority]`` or ``[Compress]`` annotation. 78 79An IPDL protocol file specifies the messages that may be sent between parent 80and child actors, as well as the direction and payload of those messages. 81Messages look like function calls but, from the standpoint of their caller, 82they may start and end at any time in the future -- they are *asynchronous*, 83so they won't block their sending actors or any other components that may be 84running in the actor's thread's ``MessageLoop``. 85 86.. note:: 87 Not all IPDL messages are asynchronous. Again, we run into exceptions for 88 messages that are synchronous, `synchronous nested`_ or `interrupt`_. Use 89 of synchronous and nested messages is strongly discouraged but may not 90 always be avoidable. They will be defined later, along with superior 91 alternatives to both that should work in nearly all cases. Interrupt 92 messages were prone to misuse and are deprecated, with removal expected in 93 the near future 94 (`Bug 1729044 <https://bugzilla.mozilla.org/show_bug.cgi?id=1729044>`_). 95 96Protocol files are compiled by the *IPDL compiler* in an early stage of the 97build process. The compiler generates C++ code that reflects the protocol. 98Specifically, it creates one C++ class that represents the parent actor and one 99that represents the child. The generated files are then automatically included 100in the C++ build process. The generated classes contain public methods for 101sending the protocol messages, which client code will use as the entry-point to 102IPC communication. The generated methods are built atop our IPC framework, 103defined in `/ipc <https://searchfox.org/mozilla-central/source/ipc>`_, that 104standardizes the safe and secure use of sockets, pipes, shared memory, etc on 105all supported platforms. See `Using The IPDL compiler`_ for more on 106integration with the build process. 107 108Client code must be written that subclasses these generated classes, in order 109to add handlers for the tasks generated to respond to each message. It must 110also add routines (``ParamTraits``) that define serialization and 111deserialization for any types used in the payload of a message that aren't 112already known to the IPDL system. Primitive types, and a bunch of Mozilla 113types, have predefined ``ParamTraits`` (`here 114<https://searchfox.org/mozilla-central/source/ipc/glue/IPCMessageUtils.h>`__ 115and `here 116<https://searchfox.org/mozilla-central/source/ipc/glue/IPCMessageUtilsSpecializations.h>`__). 117 118.. note:: 119 Among other things, client code that uses the generated code must include 120 ``chromium-config.mozbuild`` in its ``moz.build`` file. See `Using The 121 IPDL compiler`_ for a complete list of required build changes. 122 123.. _interrupt: `The Old Ways`_ 124.. _synchronous nested: `The Rest`_ 125 126The Steps To Making A New Actor 127~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128 129#. Decide what folder you will work in and create: 130 131 #. An IPDL protocol file, named for your actor (e.g. ``PMyActor.ipdl`` -- 132 actor protocols must begin with a ``P``). See `The Protocol Language`_. 133 #. Properly-named source files for your actor's parent and child 134 implementations (e.g. ``MyActorParent.h``, ``MyActorChild.h`` and, 135 optionally, adjacent .cpp files). See `The C++ Interface`_. 136 #. IPDL-specific updates to the ``moz.build`` file. See `Using The IPDL 137 compiler`_. 138#. Write your actor protocol (.ipdl) file: 139 140 #. Decide whether you need a top-level actor or a managed actor. See 141 `Top Level Actors`_. 142 #. Find/write the IPDL and C++ data types you will use in communication. 143 Write ``ParamTraits`` for C++ data types that don't have them. See 144 `Generating IPDL-Aware C++ Data Types: IPDL Structs and Unions`_ for IPDL 145 structures. See `Referencing Externally Defined Data Types: IPDL 146 Includes`_ and `ParamTraits`_ for C++ data types. 147 #. Write your actor and its messages. See `Defining Actors`_. 148#. Write C++ code to create and destroy instances of your actor at runtime. 149 150 * For managed actors, see `Actor Lifetimes in C++`_. 151 * For top-level actors, see `Creating Top Level Actors From Other Actors`_. 152 The first actor in a process is a very special exception -- see `Creating 153 First Top Level Actors`_. 154#. Write handlers for your actor's messages. See `Actors and Messages in 155 C++`_. 156#. Start sending messages through your actors! Again, see `Actors and Messages 157 in C++`_. 158 159The Protocol Language 160--------------------- 161 162This document will follow the integration of two actors into Firefox -- 163``PMyManager`` and ``PMyManaged``. ``PMyManager`` will manage ``PMyManaged``. 164A good place to start is with the IPDL actor definitions. These are files 165that are named for the actor (e.g. ``PMyManager.ipdl``) and that declare the 166messages that a protocol understands. These actors are for demonstration 167purposes and involve quite a bit of functionality. Most actors will use a very 168small fraction of these features. 169 170.. literalinclude:: _static/PMyManager.ipdl 171 :language: c++ 172 :name: PMyManager.ipdl 173 174.. literalinclude:: _static/PMyManaged.ipdl 175 :language: c++ 176 :name: PMyManaged.ipdl 177 178These files reference three additional files. ``MyTypes.ipdlh`` is an "IPDL 179header" that can be included into ``.ipdl`` files as if it were inline, except 180that it also needs to include any external actors and data types it uses: 181 182.. literalinclude:: _static/MyTypes.ipdlh 183 :language: c++ 184 :name: MyTypes.ipdlh 185 186``MyActorUtils.h`` and ``MyDataTypes.h`` are normal C++ header files that 187contain definitions for types passed by these messages, as well as instructions 188for serializing them. They will be covered in `The C++ Interface`_. 189 190Using The IPDL compiler 191~~~~~~~~~~~~~~~~~~~~~~~ 192 193To build IPDL files, list them (alphabetically sorted) in a ``moz.build`` file. 194In this example, the ``.ipdl`` and ``.ipdlh`` files would be alongside a 195``moz.build`` containing: 196 197.. code-block:: c++ 198 199 IPDL_SOURCES += [ 200 "MyTypes.ipdlh", 201 "PMyManaged.ipdl", 202 "PMyManager.ipdl", 203 ] 204 205 UNIFIED_SOURCES += [ 206 "MyManagedChild.cpp", 207 "MyManagedParent.cpp", 208 "MyManagerChild.cpp", 209 "MyManagerParent.cpp", 210 ] 211 212 include("/ipc/chromium/chromium-config.mozbuild") 213 214``chromium-config.mozbuild`` sets up paths so that generated IPDL header files 215are in the proper scope. If it isn't included, the build will fail with 216``#include`` errors in both your actor code and some internal ipc headers. For 217example: 218 219.. code-block:: c++ 220 221 c:/mozilla-src/mozilla-unified/obj-64/dist/include\ipc/IPCMessageUtils.h(13,10): fatal error: 'build/build_config.h' file not found 222 223``.ipdl`` files are compiled to C++ files as one of the earliest post-configure 224build steps. Those files are, in turn, referenced throughout the source code 225and build process. From ``PMyManager.ipdl`` the compiler generates two header 226files added to the build context and exported globally: 227``mozilla/myns/PMyManagerParent.h`` and ``mozilla/myns/PMyManagerChild.h``, as 228discussed in `Namespaces`_ below. These files contain the base classes for the 229actors. It also makes several other files, including C++ source files and 230another header, that are automatically included into the build and should not 231require attention. 232 233C++ definions of the actors are required for IPDL. They define the actions 234that are taken in response to messages -- without this, they would have no 235value. There will be much more on this when we discuss `Actors and Messages in 236C++`_ but note here that C++ header files named for the actor are required by 237the IPDL `compiler`. The example would expect 238``mozilla/myns/MyManagedChild.h``, ``mozilla/myns/MyManagedParent.h``, 239``mozilla/myns/MyManagerChild.h`` and ``mozilla/myns/MyManagerParent.h`` and 240will not build without them. 241 242Referencing Externally Defined Data Types: IPDL Includes 243~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 244 245Let's begin with ``PMyManager.ipdl``. It starts by including types that it 246will need from other places: 247 248.. code-block:: c++ 249 250 include protocol PMyManaged; 251 include MyTypes; // for MyActorPair 252 253 using MyActorEnum from "mozilla/myns/MyActorUtils.h"; 254 using struct mozilla::myns::MyData from "mozilla/MyDataTypes.h"; 255 [MoveOnly] using mozilla::myns::MyOtherData from "mozilla/MyDataTypes.h"; 256 [RefCounted] using class mozilla::myns::MyThirdData from "mozilla/MyDataTypes.h"; 257 258The first line includes a protocol that PMyManager will manage. That protocol 259is defined in its own ``.ipdl`` file. Cyclic references are expected and pose 260no concern. 261 262The second line includes the file ``MyTypes.ipdlh``, which defines types like 263structs and unions, but in IPDL, which means they have behavior that goes 264beyond the similar C++ concepts. Details can be found in `Generating 265IPDL-Aware C++ Data Types: IPDL Structs and Unions`_. 266 267The final lines include types from C++ headers. Additionally, the [RefCounted] 268and [MoveOnly] attributes tell IPDL that the types have special functionality 269that is important to operations. These are the data type attributes currently 270understood by IPDL: 271 272================ ============================================================== 273``[RefCounted]`` Type ``T`` is reference counted (by ``AddRef``/``Release``). 274 As a parameter to a message or as a type in IPDL 275 structs/unions, it is referenced as a ``RefPtr<T>``. 276``[MoveOnly]`` The type ``T`` is treated as uncopyable. When used as a 277 parameter in a message or an IPDL struct/union, it is as an 278 r-value ``T&&``. 279================ ============================================================== 280 281Finally, note that ``using``, ``using class`` and ``using struct`` are all 282valid syntax. The ``class`` and ``struct`` keywords are optional. 283 284Namespaces 285~~~~~~~~~~ 286 287From the IPDL file: 288 289.. code-block:: c++ 290 291 namespace mozilla { 292 namespace myns { 293 294 // ... data type and actor definitions ... 295 296 } // namespace myns 297 } // namespace mozilla 298 299 300Namespaces work similar to the way they do in C++. They also mimic the 301notation, in an attempt to make them comfortable to use. When IPDL actors are 302compiled into C++ actors, the namespace scoping is carried over. As previously 303noted, when C++ types are included into IPDL files, the same is true. The most 304important way in which they differ is that IPDL also uses the namespace to 305establish the path to the generated files. So, the example defines the IPDL 306data type ``mozilla::myns::MyUnion`` and the actors 307``mozilla::myns::PMyManagerParent`` and ``mozilla::myns::PMyManagerChild``, 308which can be included from ``mozilla/myns/PMyManagerParent.h``, 309``mozilla/myns/PMyManagerParent.h`` and ``mozilla/myns/PMyManagerChild.h``, 310respectively. The namespace becomes part of the path. 311 312Generating IPDL-Aware C++ Data Types: IPDL Structs and Unions 313~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 314 315``PMyManager.ipdl`` and ``MyTypes.ipdlh`` define: 316 317.. code-block:: c++ 318 319 [Comparable] union MyUnion { 320 float; 321 MyOtherData; 322 }; 323 324 struct MyActorPair { 325 PMyManaged actor1; 326 nullable PMyManaged actor2; 327 }; 328 329From these descriptions, IPDL generates C++ classes that approximate the 330behavior of C++ structs and unions but that come with pre-defined 331``ParamTraits`` implementations. These objects can also be used as usual 332outside of IPDL, although the lack of control over the generated code means 333they are sometimes poorly suited to use as plain data. See `ParamTraits`_ for 334details. 335 336The ``[Comparable]`` attribute tells IPDL to generate ``operator==`` and 337``operator!=`` for the new type. In order for it to do that, the fields inside 338the new type need to define both of those operators. 339 340Finally, the ``nullable`` keyword indicates that, when serialized, the actor 341may be null. It is intended to help users avoid null-object dereference 342errors. It only applies to actor types and may also be attached to parameters 343in message declarations. 344 345Defining Actors 346~~~~~~~~~~~~~~~ 347 348The real point of any ``.ipdl`` file is that each defines exactly one actor 349protocol. The definition always matches the ``.ipdl`` filename. Repeating the 350one in ``PMyManager.ipdl``: 351 352.. code-block:: c++ 353 354 sync protocol PMyManager { 355 manages PMyManaged; 356 357 async PMyManaged(); 358 // ... more message declarations ... 359 }; 360 361.. important:: 362 A form of reference counting is `always` used internally by IPDL to make 363 sure that it and its clients never address an actor the other component 364 deleted but this becomes fragile, and sometimes fails, when the client code 365 does not respect the reference count. For example, when IPDL detects that 366 a connection died due to a crashed remote process, deleting the actor could 367 leave dangling pointers, so IPDL `cannot` delete it. On the other hand, 368 there are many cases where IPDL is the only entity to have references to 369 some actors (this is very common for one side of a managed actor) so IPDL 370 `must` delete it. If all of those objects were reference counted then 371 there would be no complexity here. Indeed, new actors using 372 ``[ManualDealloc]`` should not be approved without a very compelling 373 reason. New ``[ManualDealloc]`` actors may soon be forbidden. 374 375The ``sync`` keyword tells IPDL that the actor contains messages that block the 376sender using ``sync`` blocking, so the sending thread waits for a response to 377the message. There is more on what it and the other blocking modes mean in 378`IPDL messages`_. For now, just know that this is redundant information whose 379value is primarily in making it easy for other developers to know that there 380are ``sync`` messages defined here. This list gives preliminary definitions of 381the options for the actor-blocking policy of messages: 382 383======================= ======================================================= 384``async`` Actor may contain only asynchronous messages. 385``sync`` Actor has ``async`` capabilities and adds ``sync`` 386 messages. ``sync`` messages 387 can only be sent from the child actor to the parent. 388``intr`` (deprecated) Actor has ``sync`` capabilities and adds ``intr`` 389 messages. Some messages can be received while an actor 390 waits for an ``intr`` response. This type will be 391 removed soon. 392======================= ======================================================= 393 394Beyond these protocol blocking strategies, IPDL supports annotations that 395indicate the actor has messages that may be received in an order other than 396the one they were sent in. These orderings attempt to handle messages in 397"message thread" order (as in e.g. mailing lists). These behaviors can be 398difficult to design for. Their use is discouraged but is sometimes warranted. 399They will be discussed further in `Nested messages`_. 400 401============================== ================================================ 402``[NestedUpTo=inside_sync]`` Actor has high priority messages that can be 403 handled while waiting for a ``sync`` response. 404``[NestedUpTo=inside_cpow]`` Actor has the highest priority messages that 405 can be handled while waiting for a ``sync`` 406 response. 407============================== ================================================ 408 409The ``manages`` clause tells IPDL that ``PMyManager`` manages the 410``PMyManaged`` actor that was previously ``include`` d. As with any managed 411protocol, it must also be the case that ``PMyManaged.ipdl`` includes 412``PMyManager`` and declares that ``PMyManaged`` is ``managed`` by 413``PMyManager``. Recalling the code: 414 415.. code-block:: c++ 416 417 // PMyManaged.ipdl 418 include protocol PMyManager; 419 // ... 420 421 protocol PMyManaged { 422 manager PMyManager; 423 // ... 424 }; 425 426An actor has a ``manager`` (e.g. ``PMyManaged``) or else it is a top-level 427actor (e.g. ``PMyManager``). An actor protocol may be managed by more than one 428actor type. For example, ``PMyManaged`` could have also been managed by some 429``PMyOtherManager`` not shown here. In that case, ``manager`` s are presented 430in a list, separated by ``or`` -- e.g. ``manager PMyManager or 431PMyOtherManager``. Of course, an **instance** of a managed actor type has only 432one manager actor (and is therefore managed by only one of the types of 433manager). The manager of an instance of a managee is always the actor that 434constructed that managee. 435 436Finally, there is the message declaration ``async PMyManaged()``. This message 437is a constructor for ``MyManaged`` actors; unlike C++ classes, it is found in 438``MyManager``. Every manager will need to expose constructors to create its 439managed types. These constructors are the only way to create an actor that is 440managed. They can take parameters and return results, like normal messages. 441The implementation of IPDL constructors are discussed in `Actor Lifetimes in 442C++`_. 443 444We haven't discussed a way to construct new top level actors. This is a more 445advanced topic and is covered separately in `Top Level Actors`_. 446 447.. _IPDL messages: `Declaring IPDL Messages`_ 448 449Declaring IPDL Messages 450~~~~~~~~~~~~~~~~~~~~~~~ 451 452The final part of the actor definition is the declaration of messages: 453 454.. code-block:: c++ 455 456 sync protocol PMyManager { 457 // ... 458 parent: 459 async __delete__(nsString aNote); 460 sync SomeMsg(MyActorPair? aActors, MyData[] aMyData) 461 returns (int32_t x, int32_t y, MyUnion aUnion); 462 async PMyManaged(); 463 both: 464 [Tainted] async AnotherMsg(MyActorEnum aEnum, int32_t aNumber) 465 returns (MyOtherData aOtherData); 466 }; 467 468The messages are grouped into blocks by ``parent:``, ``child:`` and ``both:``. 469These labels work the way ``public:`` and ``private:`` work in C++ -- messages 470after these descriptors are sent/received (only) in the direction specified. 471 472.. note:: 473 As a mnemonic to remember which direction they indicate, remember to put 474 the word "to" in front of them. So, for example, ``parent:`` preceeds 475 ``__delete__``, meaning ``__delete__`` is sent from the child **to** the 476 parent, and ``both:`` states that ``AnotherMsg`` can be sent **to** either 477 endpoint. 478 479IPDL messages support the following annotations: 480 481======================== ====================================================== 482``[Compress]`` Indicates repeated messages of this type will 483 consolidate. 484``[Tainted]`` Parameters are required to be validated before using 485 them. 486``[Priority=Foo]`` Priority of ``MessageTask`` that runs the C++ message 487 handler. ``Foo`` is one of: ``normal``, ``input``, 488 ``vsync``, ``mediumhigh``, or ``control``. 489 See the ``IPC::Message::PriorityValue`` enum. 490``[Nested=inside_sync]`` Indicates that the message can sometimes be handled 491 while a sync message waits for a response. 492``[Nested=inside_cpow]`` Indicates that the message can sometimes be handled 493 while a sync message waits for a response. 494======================== ====================================================== 495 496``[Compress]`` provides crude protection against spamming with a flood of 497messages. When messages of type ``M`` are compressed, the queue of unprocessed 498messages between actors will never contain an ``M`` beside another one; they 499will always be separated by a message of a different type. This is achieved by 500throwing out the older of the two messages if sending the new one would break 501the rule. This has been used to throttle pointer events between the main and 502content processes. 503 504``[Compress=all]`` is similar but applies whether or not the messages are 505adjacent in the message queue. 506 507``[Tainted]`` is a C++ mechanism designed to encourage paying attentiton to 508parameter security. The values of tainted parameters cannot be used until you 509vouch for their safety. They are discussed in `Actors and Messages in C++`_. 510 511The ``Nested`` annotations are deeply related to the message's blocking policy 512that follows it and which was briefly discussed in `Defining Actors`_. See 513`Nested messages`_ for details. 514 515The following is a complete list of the available blocking policies. It 516resembles the list in `Defining Actors`_: 517 518====================== ======================================================== 519``async`` Actor may contain only asynchronous messages. 520``sync`` Actor has ``async`` capabilities and adds ``sync`` 521 messages. ``sync`` messages can only be sent from the 522 child actor to the parent. 523``intr`` (deprecated) Actor has ``sync`` capabilities and adds ``intr`` 524 messages. This type will be removed soon. 525====================== ======================================================== 526 527The policy defines whether an actor will wait for a response when it sends a 528certain type of message. A ``sync`` actor will wait immediately after sending 529a ``sync`` message, stalling its thread, until a response is received. This is 530an easy source of browser stalls. It is rarely required that a message be 531synchronous. New ``sync`` messages are therefore required to get approval from 532an IPC peer. The IPDL compiler will require such messages to be listed in the 533file ``sync-messages.ini``. 534 535The notion that only child actors can send ``sync`` messages was introduced to 536avoid potential deadlocks. It relies on the belief that a cycle (deadlock) of 537sync messages is impossible because they all point in one direction. This is 538no longer the case because any endpoint can be a child `or` parent and some, 539like the main process, sometimes serve as both. This means that sync messages 540should be used with extreme care. 541 542.. note:: 543 The notion of sync messages flowing in one direction is still the main 544 mechanism IPDL uses to avoid deadlock. New actors should avoid violating 545 this rule as the consequences are severe (and complex). Actors that break 546 these rules should not be approved without **extreme** extenuating 547 circumstances. If you think you need this, check with the IPC team on 548 Element first (#ipc). 549 550An ``async`` actor will not wait. An ``async`` response is essentially 551identical to sending another ``async`` message back. It may be handled 552whenever received messages are handled. The value over an ``async`` response 553message comes in the ergonomics -- async responses are usually handled by C++ 554lambda functions that are more like continuations than methods. This makes 555them easier to write and to read. Additionally, they allow a response to 556return message failure, while there would be no such response if we were 557expecting to send a new async message back, and it failed. 558 559Following synchronization is the name of the message and its parameter list. 560The message ``__delete__`` stands out as strange -- indeed, it terminates the 561actor's connection. `It does not delete any actor objects itself!` It severs 562the connections of the actor `and any actors it manages` at both endpoints. An 563actor will never send or receive any messages after it sends or receives a 564``__delete__``. Note that all sends and receives have to happen on a specific 565*worker* thread for any actor tree so the send/receive order is well defined. 566Anything sent after the actor processes ``__delete__`` is ignored (send returns 567an error, messages yet to be received fail their delivery). In other words, 568some future operations may fail but no unexpected behavior is possible. 569 570In our example, the child can break the connection by sending ``__delete__`` to 571the parent. The only thing the parent can do to sever the connection is to 572fail, such as by crashing. This sort of unidirectional control is both common 573and desirable. 574 575``PMyManaged()`` is a managed actor constructor. Note the asymmetry -- an 576actor contains its managed actor's constructors but its own destructor. 577 578The list of parameters to a message is fairly straight-forward. Parameters 579can be any type that has a C++ ``ParamTraits`` specialization and is imported 580by a directive. That said, there are some surprises in the list of messages: 581 582================= ============================================================= 583``int32_t``,... The standard primitive types are included. See `builtin.py`_ 584 for a list. Pointer types are, unsurprisingly, forbidden. 585``?`` When following a type T, the parameter is translated into 586 ``Maybe<T>`` in C++. 587``[]`` When following a type T, the parameter is translated into 588 ``nsTArray<T>`` in C++. 589================= ============================================================= 590 591Finally, the returns list declares the information sent in response, also as a 592tuple of typed parameters. As previously mentioned, even ``async`` messages 593can receive responses. A ``sync`` message will always wait for a response but 594an ``async`` message will not get one unless it has a ``returns`` clause. 595 596This concludes our tour of the IPDL example file. The connection to C++ is 597discussed in the next chapter; messages in particular are covered in `Actors 598and Messages in C++`_. For suggestions on best practices when designing your 599IPDL actor approach, see `IPDL Best Practices`_. 600 601.. _builtin.py: https://searchfox.org/mozilla-central/source/ipc/ipdl/ipdl/builtin.py 602 603IPDL Syntax Quick Reference 604~~~~~~~~~~~~~~~~~~~~~~~~~~~ 605 606The following is a list of the keywords and operators that have been introduced 607for use in IPDL files: 608 609============================= ================================================= 610``include`` Include a C++ header (quoted file name) or 611 ``.ipdlh`` file (unquoted with no file suffix). 612``using (class|struct) from`` Similar to ``include`` but imports only a 613 specific data type. 614``include protocol`` Include another actor for use in management 615 statements, IPDL data types or as parameters to 616 messages. 617``[RefCounted]`` Indicates that the imported C++ data types are 618 reference counted. Refcounted types require a 619 different ``ParamTraits`` interface than 620 non-reference-counted types. 621``[ManualDealloc]`` Indicates that the IPDL interface uses the legacy 622 manual allocation/deallocation interface, rather 623 than modern reference counting. 624``[MoveOnly]`` Indicates that an imported C++ data type should 625 not be copied. IPDL code will move it instead. 626``namespace`` Specifies the namespace for IPDL generated code. 627``union`` An IPDL union definition. 628``struct`` An IPDL struct definition. 629``[Comparable]`` Indicates that IPDL should generate 630 ``operator==`` and ``operator!=`` for the given 631 IPDL struct/union. 632``nullable`` Indicates that an actor reference in an IPDL type 633 may be null when sent over IPC. 634``protocol`` An IPDL protocol (actor) definition. 635``sync/async`` These are used in two cases: (1) to indicate 636 whether a message blocks as it waits for a result 637 and (2) because an actor that contains ``sync`` 638 messages must itself be labeled ``sync`` or 639 ``intr``. 640``[NestedUpTo=inside_sync]`` Indicates that an actor contains 641 [Nested=inside_sync] messages, in addition to 642 normal messages. 643``[NestedUpTo=inside_cpow]`` Indicates that an actor contains 644 [Nested=inside_cpow] messages, in addition to 645 normal messages. 646``intr`` Used to indicate either that (1) an actor 647 contains ``sync``, ``async`` and (deprecated) 648 ``intr`` messages, or (2) a message is ``intr`` 649 type. 650``[Nested=inside_sync]`` Indicates that the message can be handled while 651 waiting for lower-priority, or in-message-thread, 652 sync responses. 653``[Nested=inside_cpow]`` Indicates that the message can be handled while 654 waiting for lower-priority, or in-message-thread, 655 sync responses. Cannot be sent by the parent 656 actor. 657``manager`` Used in a protocol definition to indicate that 658 this actor manages another one. 659``manages`` Used in a protocol definition to indicate that 660 this actor is managed by another one. 661``or`` Used in a ``manager`` clause for actors that have 662 multiple potential managers. 663``parent: / child: / both:`` Indicates direction of subsequent actor messages. 664 As a mnemonic to remember which direction they 665 indicate, put the word "to" in front of them. 666``returns`` Defines return values for messages. All types 667 of message, including ``async``, support 668 returning values. 669``__delete__`` A special message that destroys the related 670 actors at both endpoints when sent. 671 ``Recv__delete__`` and ``ActorDestroy`` are 672 called before destroying the actor at the other 673 endpoint, to allow for cleanup. 674``int32_t``,... The standard primitive types are included. 675``String`` Translated into ``nsString`` in C++. 676``?`` When following a type T in an IPDL data structure 677 or message parameter, 678 the parameter is translated into ``Maybe<T>`` in 679 C++. 680``[]`` When following a type T in an IPDL data structure 681 or message parameter, 682 the parameter is translated into ``nsTArray<T>`` 683 in C++. 684``[Tainted]`` Used to indicate that a message's handler should 685 receive parameters that it is required to 686 manually validate. Parameters of type ``T`` 687 become ``Tainted<T>`` in C++. 688``[Compress]`` Indicates repeated messages of this type will 689 consolidate. When two messages of this type are 690 sent and end up side-by-side in the message queue 691 then the older message is discarded (not sent). 692``[Compress=all]`` Like ``[Compress]`` but discards the older 693 message regardless of whether they are adjacent 694 in the message queue. 695``[Priority=Foo]`` Priority of ``MessageTask`` that runs the C++ 696 message handler. ``Foo`` is one of: ``normal``, 697 ``input``, ``vsync``, ``mediumhigh``, or 698 ``control``. 699``[ChildImpl="RemoteFoo"]`` Indicates that the child side implementation of 700 the actor is a class named ``RemoteFoo``, and the 701 definition is included by one of the 702 ``include "...";`` statements in the file. 703 *New uses of this attribute are discouraged.* 704``[ParentImpl="FooImpl"]`` Indicates that the parent side implementation of 705 the actor is a class named ``FooImpl``, and the 706 definition is included by one of the 707 ``include "...";`` statements in the file. 708 *New uses of this attribute are discouraged.* 709``[ChildImpl=virtual]`` Indicates that the child side implementation of 710 the actor is not exported by a header, so virtual 711 ``Recv`` methods should be used instead of direct 712 function calls. *New uses of this attribute are 713 discouraged.* 714``[ParentImpl=virtual]`` Indicates that the parent side implementation of 715 the actor is not exported by a header, so virtual 716 ``Recv`` methods should be used instead of direct 717 function calls. *New uses of this attribute are 718 discouraged.* 719============================= ================================================= 720 721 722The C++ Interface 723----------------- 724 725ParamTraits 726~~~~~~~~~~~ 727 728Before discussing how C++ represents actors and messages, we look at how IPDL 729connects to the imported C++ data types. In order for any C++ type to be 730(de)serialized, it needs an implementation of the ``ParamTraits`` C++ type 731class. ``ParamTraits`` is how your code tells IPDL what bytes to write to 732serialize your objects for sending, and how to convert those bytes back to 733objects at the other endpoint. Since ``ParamTraits`` need to be reachable by 734IPDL code, they need to be declared in a C++ header and imported by your 735protocol file. Failure to do so will result in a build error. 736 737Most basic types and many essential Mozilla types are always available for use 738without inclusion. An incomplete list includes: C++ primitives, strings 739(``std`` and ``mozilla``), vectors (``std`` and ``mozilla``), ``RefPtr<T>`` 740(for serializable ``T``), ``UniquePtr<T>``, ``nsCOMPtr<T>``, ``nsTArray<T>``, 741``std::unordered_map<T>``, ``nsresult``, etc. See `builtin.py 742<https://searchfox.org/mozilla-central/source/ipc/ipdl/ipdl/builtin.py>`_, 743`ipc_message_utils.h 744<https://searchfox.org/mozilla-central/source/ipc/chromium/src/chrome/common/ipc_message_utils.h>`_ 745and `IPCMessageUtilsSpecializations.h 746<https://searchfox.org/mozilla-central/source/ipc/glue/IPCMessageUtilsSpecializations.h>`_. 747 748``ParamTraits`` typically bootstrap with the ``ParamTraits`` of more basic 749types, until they hit bedrock (e.g. one of the basic types above). In the most 750extreme cases, a ``ParamTraits`` author may have to resort to designing a 751binary data format for a type. Both options are available. 752 753We haven't seen any of this C++ yet. Let's look at the data types included 754from ``MyDataTypes.h``: 755 756.. code-block:: c++ 757 758 // MyDataTypes.h 759 namespace mozilla::myns { 760 struct MyData { 761 nsCString s; 762 uint8_t bytes[17]; 763 MyData(); // IPDL requires the default constructor to be public 764 }; 765 766 struct MoveonlyData { 767 MoveonlyData(); 768 MoveonlyData& operator=(const MoveonlyData&) = delete; 769 770 MoveonlyData(MoveonlyData&& m); 771 MoveonlyData& operator=(MoveonlyData&& m); 772 }; 773 774 typedef MoveonlyData MyOtherData; 775 776 class MyUnusedData { 777 public: 778 NS_INLINE_DECL_REFCOUNTING(MyUnusedData) 779 int x; 780 }; 781 }; 782 783 namespace IPC { 784 // Basic type 785 template<> 786 struct ParamTraits<mozilla::myns::MyData> { 787 typedef mozilla::myns::MyData paramType; 788 static void Write(MessageWriter* m, const paramType& in); 789 static bool Read(MessageReader* m, paramType* out); 790 }; 791 792 // [MoveOnly] type 793 template<> 794 struct ParamTraits<mozilla::myns::MyOtherData> { 795 typedef mozilla::myns::MyOtherData paramType; 796 static void Write(MessageWriter* m, const paramType& in); 797 static bool Read(MessageReader* m, paramType* out); 798 }; 799 800 // [RefCounted] type 801 template<> 802 struct ParamTraits<mozilla::myns::MyUnusedData*> { 803 typedef mozilla::myns::MyUnusedData paramType; 804 static void Write(MessageWriter* m, paramType* in); 805 static bool Read(MessageReader* m, RefPtr<paramType>* out); 806 }; 807 } 808 809MyData is a struct and MyOtherData is a typedef. IPDL is fine with both. 810Additionally, MyOtherData is not copyable, matching its IPDL ``[MoveOnly]`` 811annotation. 812 813``ParamTraits`` are required to be defined in the ``IPC`` namespace. They must 814contain a ``Write`` method with the proper signature that is used for 815serialization and a ``Read`` method, again with the correct signature, for 816deserialization. 817 818Here we have three examples of declarations: one for an unannotated type, one 819for ``[MoveOnly]`` and a ``[RefCounted]`` one. Notice the difference in the 820``[RefCounted]`` type's method signatures. The only difference that may not be 821clear from the function types is that, in the non-reference-counted case, a 822default-constructed object is supplied to ``Read`` but, in the 823reference-counted case, ``Read`` is given an empty ``RefPtr<MyUnusedData>`` and 824should only allocate a ``MyUnusedData`` to return if it so desires. 825 826These are straight-forward implementations of the ``ParamTraits`` methods for 827``MyData``: 828 829.. code-block:: c++ 830 831 /* static */ void IPC::ParamTraits<MyData>::Write(MessageWriter* m, const paramType& in) { 832 WriteParam(m, in.s); 833 m->WriteBytes(in.bytes, sizeof(in.bytes)); 834 } 835 /* static */ bool IPC::ParamTraits<MyData>::Read(MessageReader* m, paramType* out) { 836 return ReadParam(m, &out->s) && 837 m->ReadBytesInto(out->bytes, sizeof(out->bytes)); 838 } 839 840``WriteParam`` and ``ReadParam`` call the ``ParamTraits`` for the data you pass 841them, determined using the type of the object as supplied. ``WriteBytes`` and 842``ReadBytesInto`` work on raw, contiguous bytes as expected. ``MessageWriter`` 843and ``MessageReader`` are IPDL internal objects which hold the incoming/outgoing 844message as a stream of bytes and the current spot in the stream. It is *very* 845rare for client code to need to create or manipulate these obejcts. Their 846advanced use is beyond the scope of this document. 847 848.. important:: 849 Potential failures in ``Read`` include everyday C++ failures like 850 out-of-memory conditions, which can be handled as usual. But ``Read`` can 851 also fail due to things like data validation errors. ``ParamTraits`` read 852 data that is considered insecure. It is important that they catch 853 corruption and properly handle it. Returning false from ``Read`` will 854 usually result in crashing the process (everywhere except in the main 855 process). This is the right behavior as the browser would be in an 856 unexpected state, even if the serialization failure was not malicious 857 (since it cannot process the message). Other responses, such as failing 858 with a crashing assertion, are inferior. IPDL fuzzing relies on 859 ``ParamTraits`` not crashing due to corruption failures. 860 Occasionally, validation will require access to state that ``ParamTraits`` 861 can't easily reach. (Only) in those cases, validation can be reasonably 862 done in the message handler. Such cases are a good use of the ``Tainted`` 863 annotation. See `Actors and Messages in C++`_ for more. 864 865.. note:: 866 In the past, it was required to specialize ``mozilla::ipc::IPDLParamTraits<T>`` 867 instead of ``IPC::ParamTraits<T>`` if you needed the actor object itself during 868 serialization or deserialization. These days the actor can be fetched using 869 ``IPC::Message{Reader,Writer}::GetActor()`` in ``IPC::ParamTraits``, so that 870 trait should be used for all new serializations. 871 872A special case worth mentioning is that of enums. Enums are a common source of 873security holes since code is rarely safe with enum values that are not valid. 874Since data obtained through IPDL messages should be considered tainted, enums 875are of principal concern. ``ContiguousEnumSerializer`` and 876``ContiguousEnumSerializerInclusive`` safely implement ``ParamTraits`` for 877enums that are only valid for a contiguous set of values, which is most of 878them. The generated ``ParamTraits`` confirm that the enum is in valid range; 879``Read`` will return false otherwise. As an example, here is the 880``MyActorEnum`` included from ``MyActorUtils.h``: 881 882.. code-block:: c++ 883 884 enum MyActorEnum { e1, e2, e3, e4, e5 }; 885 886 template<> 887 struct ParamTraits<MyActorEnum> 888 : public ContiguousEnumSerializerInclusive<MyActorEnum, MyActorEnum::e1, MyActorEnum::e5> {}; 889 890IPDL Structs and Unions in C++ 891~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 892 893IPDL structs and unions become C++ classes that provide interfaces that are 894fairly self-explanatory. Recalling ``MyUnion`` and ``MyActorPair`` from 895`IPDL Structs and Unions`_ : 896 897.. code-block:: c++ 898 899 union MyUnion { 900 float; 901 MyOtherData; 902 }; 903 904 struct MyActorPair { 905 PMyManaged actor1; 906 nullable PMyManaged actor2; 907 }; 908 909These compile to: 910 911.. code-block:: c++ 912 913 class MyUnion { 914 enum Type { Tfloat, TMyOtherData }; 915 Type type(); 916 MyUnion(float f); 917 MyUnion(MyOtherData&& aOD); 918 MyUnion& operator=(float f); 919 MyUnion& operator=(MyOtherData&& aOD); 920 operator float&(); 921 operator MyOtherData&(); 922 }; 923 924 class MyActorPair { 925 MyActorPair(PMyManagedParent* actor1Parent, PMyManagedChild* actor1Child, 926 PMyManagedParent* actor2Parent, PMyManagedChild* actor2Child); 927 // Exactly one of { actor1Parent(), actor1Child() } must be non-null. 928 PMyManagedParent*& actor1Parent(); 929 PMyManagedChild*& actor1Child(); 930 // As nullable, zero or one of { actor2Parent(), actor2Child() } will be non-null. 931 PMyManagedParent*& actor2Parent(); 932 PMyManagedChild*& actor2Child(); 933 } 934 935The generated ``ParamTraits`` use the ``ParamTraits`` for the types referenced 936by the IPDL struct or union. Fields respect any annotations for their type 937(see `IPDL Includes`_). For example, a ``[RefCounted]`` type ``T`` generates 938``RefPtr<T>`` fields. 939 940Note that actor members result in members of both the parent and child actor 941types, as seen in ``MyActorPair``. When actors are used to bridge processes, 942only one of those could ever be used at a given endpoint. IPDL makes sure 943that, when you send one type (say, ``PMyManagedChild``), the adjacent actor of 944the other type (``PMyManagedParent``) is received. This is not only true for 945message parameters and IPDL structs/unions but also for custom ``ParamTraits`` 946implementations. If you ``Write`` a ``PFooParent*`` then you must ``Read`` a 947``PFooChild*``. This is hard to confuse in message handlers since they are 948members of a class named for the side they operate on, but this cannot be 949enforced by the compiler. If you are writing 950``MyManagerParent::RecvSomeMsg(Maybe<MyActorPair>&& aActors, nsTArray<MyData>&& aMyData)`` 951then the ``actor1Child`` and ``actor2Child`` fields cannot be valid since the 952child (usually) exists in another process. 953 954.. _IPDL Structs and Unions: `Generating IPDL-Aware C++ Data Types: IPDL Structs and Unions`_ 955.. _IPDL Includes: `Referencing Externally Defined Data Types: IPDL Includes`_ 956 957Actors and Messages in C++ 958~~~~~~~~~~~~~~~~~~~~~~~~~~ 959 960As mentioned in `Using The IPDL compiler`_, the IPDL compiler generates two 961header files for the protocol ``PMyManager``: ``PMyManagerParent.h`` and 962``PMyManagerChild.h``, which declare the actor's base classes. There, we 963discussed how the headers are visible to C++ components that include 964``chromium-config.mozbuild``. We, in turn, always need to define two files 965that declare our actor implementation subclasses (``MyManagerParent.h`` and 966``MyManagerChild.h``). The IPDL file looked like this: 967 968.. literalinclude:: _static/PMyManager.ipdl 969 :language: c++ 970 :name: PMyManager.ipdl 971 972So ``MyManagerParent.h`` looks like this: 973 974.. code-block:: c++ 975 976 #include "PMyManagerParent.h" 977 978 namespace mozilla { 979 namespace myns { 980 981 class MyManagerParent : public PMyManagerParent { 982 NS_INLINE_DECL_REFCOUNTING(MyManagerParent, override) 983 protected: 984 IPCResult Recv__delete__(const nsString& aNote); 985 IPCResult RecvSomeMsg(const Maybe<MyActorPair>& aActors, const nsTArray<MyData>& aMyData, 986 int32_t* x, int32_t* y, MyUnion* aUnion); 987 IPCResult RecvAnotherMsg(const Tainted<MyActorEnum>& aEnum, const Tainted<int32_t>& aNumber, 988 AnotherMsgResolver&& aResolver); 989 990 already_AddRefed<PMyManagerParent> AllocPMyManagedParent(); 991 IPCResult RecvPMyManagedConstructor(PMyManagedConstructor* aActor); 992 993 // ... etc ... 994 }; 995 996 } // namespace myns 997 } // namespace mozilla 998 999All messages that can be sent to the actor must be handled by ``Recv`` methods 1000in the proper actor subclass. They should return ``IPC_OK()`` on success and 1001``IPC_FAIL(actor, reason)`` if an error occurred (where ``actor`` is ``this`` 1002and ``reason`` is a human text explanation) that should be considered a failure 1003to process the message. The handling of such a failure is specific to the 1004process type. 1005 1006``Recv`` methods are called by IPDL by enqueueing a task to run them on the 1007``MessageLoop`` for the thread on which they are bound. This thread is the 1008actor's *worker thread*. All actors in a managed actor tree have the same 1009worker thread -- in other words, actors inherit the worker thread from their 1010managers. Top level actors establish their worker thread when they are 1011*bound*. More information on threads can be found in `Top Level Actors`_. For 1012the most part, client code will never engage with an IPDL actor outside of its 1013worker thread. 1014 1015Received parameters become stack variables that are ``std::move``-d into the 1016``Recv`` method. They can be received as a const l-value reference, 1017rvalue-reference, or by value (type-permitting). ``[MoveOnly]`` types should 1018not be received as const l-values. Return values for sync messages are 1019assigned by writing to non-const (pointer) parameters. Return values for async 1020messages are handled differently -- they are passed to a resolver function. In 1021our example, ``AnotherMsgResolver`` would be a ``std::function<>`` and 1022``aResolver`` would be given the value to return by passing it a reference to a 1023``MyOtherData`` object. 1024 1025``MyManagerParent`` is also capable of ``sending`` an async message that 1026returns a value: ``AnotherMsg``. This is done with ``SendAnotherMsg``, which 1027is defined automatically by IPDL in the base class ``PMyManagerParent``. There 1028are two signatures for ``Send`` and they look like this: 1029 1030.. code-block:: c++ 1031 1032 // Return a Promise that IPDL will resolve with the response or reject. 1033 RefPtr<MozPromise<MyOtherData, ResponseRejectReason, true>> 1034 SendAnotherMsg(const MyActorEnum& aEnum, int32_t aNumber); 1035 1036 // Provide callbacks to process response / reject. The callbacks are just 1037 // std::functions. 1038 void SendAnotherMsg(const MyActorEnum& aEnum, int32_t aNumber, 1039 ResolveCallback<MyOtherData>&& aResolve, RejectCallback&& aReject); 1040 1041The response is usually handled by lambda functions defined at the site of the 1042``Send`` call, either by attaching them to the returned promise with e.g. 1043``MozPromise::Then``, or by passing them as callback parameters. See docs on 1044``MozPromise`` for more on its use. The promise itself is either resolved or 1045rejected by IPDL when a valid reply is received or when the endpoint determines 1046that the communication failed. ``ResponseRejectReason`` is an enum IPDL 1047provides to explain failures. 1048 1049Additionally, the ``AnotherMsg`` handler has ``Tainted`` parameters, as a 1050result of the [Tainted] annotation in the protocol file. Recall that 1051``Tainted`` is used to force explicit validation of parameters in the message 1052handler before their values can be used (as opposed to validation in 1053``ParamTraits``). They therefore have access to any state that the message 1054handler does. Their APIs, along with a list of macros that are used to 1055validate them, are detailed `here 1056<https://searchfox.org/mozilla-central/source/mfbt/Tainting.h>`__. 1057 1058Send methods that are not for async messages with return values follow a 1059simpler form; they return a ``bool`` indicating success or failure and return 1060response values in non-const parameters, as the ``Recv`` methods do. For 1061example, ``PMyManagerChild`` defines this to send the sync message ``SomeMsg``: 1062 1063.. code-block:: c++ 1064 1065 // generated in PMyManagerChild 1066 bool SendSomeMsg(const Maybe<MyActorPair>& aActors, const nsTArray<MyData>& aMyData, 1067 int32_t& x, int32_t& y, MyUnion& aUnion); 1068 1069Since it is sync, this method will not return to its caller until the response 1070is received or an error is detected. 1071 1072All calls to ``Send`` methods, like all messages handler ``Recv`` methods, must 1073only be called on the worker thread for the actor. 1074 1075Constructors, like the one for ``MyManaged``, are clearly an exception to these 1076rules. They are discussed in the next section. 1077 1078.. _Actor Lifetimes in C++: 1079 1080Actor Lifetimes in C++ 1081~~~~~~~~~~~~~~~~~~~~~~ 1082 1083The constructor message for ``MyManaged`` becomes *two* methods at the 1084receiving end. ``AllocPMyManagedParent`` constructs the managed actor, then 1085``RecvPMyManagedConstructor`` is called to update the new actor. The following 1086diagram shows the construction of the ``MyManaged`` actor pair: 1087 1088.. mermaid:: 1089 :align: center 1090 :caption: A ``MyManaged`` actor pair being created by some ``Driver`` 1091 object. Internal IPC objects in the parent and child processes 1092 are combined for compactness. Connected **par** blocks run 1093 concurrently. This shows that messages can be safely sent while 1094 the parent is still being constructed. 1095 1096 %%{init: {'sequence': {'boxMargin': 4, 'actorMargin': 10} }}%% 1097 sequenceDiagram 1098 participant d as Driver 1099 participant mgdc as MyManagedChild 1100 participant mgrc as MyManagerChild 1101 participant ipc as IPC Child/Parent 1102 participant mgrp as MyManagerParent 1103 participant mgdp as MyManagedParent 1104 d->>mgdc: new 1105 mgdc->>d: [mgd_child] 1106 d->>mgrc: SendPMyManagedConstructor<br/>[mgd_child, params] 1107 mgrc->>ipc: Form actor pair<br/>[mgd_child, params] 1108 par 1109 mgdc->>ipc: early PMyManaged messages 1110 and 1111 ipc->>mgrp: AllocPMyManagedParent<br/>[params] 1112 mgrp->>mgdp: new 1113 mgdp->>mgrp: [mgd_parent] 1114 ipc->>mgrp: RecvPMyManagedConstructor<br/>[mgd_parent, params] 1115 mgrp->>mgdp: initialization 1116 ipc->>mgdp: early PMyManaged messages 1117 end 1118 Note over mgdc,mgdp: Bi-directional sending and receiving will now happen concurrently. 1119 1120The next diagram shows the destruction of the ``MyManaged`` actor pair, as 1121initiated by a call to ``Send__delete__``. ``__delete__`` is sent from the 1122child process because that is the only side that can call it, as declared in 1123the IPDL protocol file. 1124 1125.. mermaid:: 1126 :align: center 1127 :caption: A ``MyManaged`` actor pair being disconnected due to some 1128 ``Driver`` object in the child process sending ``__delete__``. 1129 1130 %%{init: {'sequence': {'boxMargin': 4, 'actorMargin': 10} }}%% 1131 sequenceDiagram 1132 participant d as Driver 1133 participant mgdc as MyManagedChild 1134 participant ipc as IPC Child/Parent 1135 participant mgdp as MyManagedParent 1136 d->>mgdc: Send__delete__ 1137 mgdc->>ipc: Disconnect<br/>actor pair 1138 par 1139 ipc->>mgdc: ActorDestroy 1140 ipc->>mgdc: Release 1141 and 1142 ipc->>mgdp: Recv__delete__ 1143 ipc->>mgdp: ActorDestroy 1144 ipc->>mgdp: Release 1145 end 1146 1147Finally, let's take a look at the behavior of an actor whose peer has been lost 1148(e.g. due to a crashed process). 1149 1150.. mermaid:: 1151 :align: center 1152 :caption: A ``MyManaged`` actor pair being disconnected when its peer is 1153 lost due to a fatal error. Note that ``Recv__delete__`` is not 1154 called. 1155 1156 %%{init: {'sequence': {'boxMargin': 4, 'actorMargin': 10} }}%% 1157 sequenceDiagram 1158 participant mgdc as MyManagedChild 1159 participant ipc as IPC Child/Parent 1160 participant mgdp as MyManagedParent 1161 Note over mgdc: CRASH!!! 1162 ipc->>ipc: Notice fatal error. 1163 ipc->>mgdp: ActorDestroy 1164 ipc->>mgdp: Release 1165 1166The ``Alloc`` and ``Recv...Constructor`` methods are somewhat mirrored by 1167``Recv__delete__`` and ``ActorDestroy`` but there are a few differences. 1168First, the ``Alloc`` method really does create the actor but the 1169``ActorDestroy`` method does not delete it. Additionally, ``ActorDestroy`` 1170is run at *both* endpoints, during ``Send__delete__`` or after 1171``Recv__delete__``. Finally and most importantly, ``Recv__delete__`` is only 1172called if the ``__delete__`` message is received but it may not be if, for 1173example, the remote process crashes. ``ActorDestroy``, on the other hand, is 1174guaranteed to run for *every* actor unless the process terminates uncleanly. 1175For this reason, ``ActorDestroy`` is the right place for most actor shutdown 1176code. ``Recv__delete__`` is rarely useful, although it is occasionally 1177beneficial to have it receive some final data. 1178 1179The relevant part of the parent class looks like this: 1180 1181.. code-block:: c++ 1182 1183 class MyManagerParent : public PMyManagerParent { 1184 already_AddRefed<PMyManagerParent> AllocPMyManagedParent(); 1185 IPCResult RecvPMyManagedConstructor(PMyManagedConstructor* aActor); 1186 1187 IPCResult Recv__delete__(const nsString& aNote); 1188 void ActorDestroy(ActorDestroyReason why); 1189 1190 // ... etc ... 1191 }; 1192 1193The ``Alloc`` method is required for managed actors that are constructed by 1194IPDL receiving a ``Send`` message. It is not required for the actor at the 1195endpoint that calls ``Send``. The ``Recv...Constructor`` message is not 1196required -- it has a base implementation that does nothing. 1197 1198If the constructor message has parameters, they are sent to both methods. 1199Parameters are given to the ``Alloc`` method by const reference but are moved 1200into the ``Recv`` method. They differ in that messages can be sent from the 1201``Recv`` method but, in ``Alloc``, the newly created actor is not yet 1202operational. 1203 1204The ``Send`` method for a constructor is similarly different from other 1205``Send`` methods. In the child actor, ours looks like this: 1206 1207.. code-block:: c++ 1208 1209 IPCResult SendPMyManagedConstructor(PMyManagedChild* aActor); 1210 1211The method expects a ``PMyManagedChild`` that the caller will have constructed, 1212presumably using ``new`` (this is why it does not require an ``Alloc`` method). 1213Once ``Send...Constructor`` is called, the actor can be used to send and 1214receive messages. It does not matter that the remote actor may not have been 1215created yet due to asynchronicity. 1216 1217The destruction of actors is as unusual as their construction. Unlike 1218construction, it is the same for managed and top-level actors. Avoiding 1219``[ManualDealloc]`` actors removes a lot of the complexity but there is still 1220a process to understand. Actor destruction begins when an ``__delete__`` 1221message is sent. In ``PMyManager``, this message is declared from child to 1222parent. The actor calling ``Send__delete__`` is no longer connected to 1223anything when the method returns. Future calls to ``Send`` return an error 1224and no future messages will be received. This is also the case for an actor 1225that has run ``Recv__delete__``; it is no longer connected to the other 1226endpoint. 1227 1228.. note:: 1229 Since ``Send__delete__`` may release the final reference to itself, it 1230 cannot safely be a class instance method. Instead, unlike other ``Send`` 1231 methods, it's a ``static`` class method and takes the actor as a parameter: 1232 1233 .. code-block:: c++ 1234 1235 static IPCResult Send__delete__(PMyManagerChild* aToDelete); 1236 1237 Additionally, the ``__delete__`` message tells IPDL to disconnect both the 1238 given actor *and all of its managed actors*. So it is really deleting the 1239 actor subtree, although ``Recv__delete__`` is only called for the actor it 1240 was sent to. 1241 1242During the call to ``Send__delete__``, or after the call to ``Recv__delete__``, 1243the actor's ``ActorDestroy`` method is called. This method gives client code a 1244chance to do any teardown that must happen in `all` circumstances were it is 1245possible -- both expected and unexpected. This means that ``ActorDestroy`` 1246will also be called when, for example, IPDL detects that the other endpoint has 1247terminated unexpectedly, so it is releasing its reference to the actor, or 1248because an ancestral manager (manager or manager's manager...) received a 1249``__delete__``. The only way for an actor to avoid ``ActorDestroy`` is for its 1250process to crash first. ``ActorDestroy`` is always run after its actor is 1251disconnected so it is pointless to try to send messages from it. 1252 1253Why use ``ActorDestroy`` instead of the actor's destructor? ``ActorDestroy`` 1254gives a chance to clean up things that are only used for communication and 1255therefore don't need to live for however long the actor's (reference-counted) 1256object does. For example, you might have references to shared memory (Shmems) 1257that are no longer valid. Or perhaps the actor can now release a cache of data 1258that was only needed for processing messages. It is cleaner to deal with 1259communication-related objects in ``ActorDestroy``, where they become invalid, 1260than to leave them in limbo until the destructor is run. 1261 1262Consider actors to be like normal reference-counted objects, but where IPDL 1263holds a reference while the connection will or does exist. One common 1264architecture has IPDL holding the `only` reference to an actor. This is common 1265with actors created by sending construtor messages but the idea is available to 1266any actor. That only reference is then released when the ``__delete__`` 1267message is sent or received. 1268 1269The dual of IPDL holding the only reference is to have client code hold the 1270only reference. A common pattern to achieve this has been to override the 1271actor's ``AddRef`` to have it send ``__delete__`` only when it's count is down 1272to one reference (which must be IPDL if ``actor.CanSend()`` is true). A better 1273approach would be to create a reference-counted delegate for your actor that 1274can send ``__delete__`` from its destructor. IPDL does not guarantee that it 1275will not hold more than one reference to your actor. 1276 1277.. _Top Level Actors: 1278 1279Top Level Actors 1280---------------- 1281 1282Recall that top level actors are actors that have no manager. They are at the 1283root of every actor tree. There are two settings in which we use top-level 1284actors that differ pretty dramatically. The first type are top-level actors 1285that are created and maintained in a way that resembles managed actors, but 1286with some important differences we will cover in this section. The second type 1287of top-level actors are the very first actors in a new process -- these actors 1288are created through different means and closing them (usually) terminates the 1289process. The `new process example 1290<https://phabricator.services.mozilla.com/D119038>`_ demonstrates both of 1291these. It is discussed in detail in :ref:`Adding a New Type of Process`. 1292 1293Value of Top Level Actors 1294~~~~~~~~~~~~~~~~~~~~~~~~~ 1295 1296Top-level actors are harder to create and destroy than normal actors. They 1297used to be more heavyweight than managed actors but this has recently been 1298dramatically reduced. 1299 1300.. note:: 1301 Top-level actors previously required a dedicated *message channel*, which 1302 are limited OS resources. This is no longer the case -- message channels 1303 are now shared by actors that connect the same two processes. This 1304 *message interleaving* can affect message delivery latency but profiling 1305 suggests that the change was basically inconsequential. 1306 1307So why use a new top level actor? 1308 1309* The most dramatic property distinguishing top-level actors is the ability to 1310 *bind* to whatever ``EventTarget`` they choose. This means that any thread 1311 that runs a ``MessageLoop`` can use the event target for that loop as the 1312 place to send incoming messages. In other words, ``Recv`` methods would be 1313 run by that message loop, on that thread. The IPDL apparatus will 1314 asynchronously dispatch messages to these event targets, meaning that 1315 multiple threads can be handling incoming messages at the same time. The 1316 `PBackground`_ approach was born of a desire to make it easier to exploit 1317 this, although it has some complications, detailed in that section, that 1318 limit its value. 1319* Top level actors suggest modularity. Actor protocols are tough to debug, as 1320 is just about anything that spans process boundaries. Modularity can give 1321 other developers a clue as to what they need to know (and what they don't) 1322 when reading an actor's code. The alternative is proverbial *dumpster 1323 classes* that are as critical to operations (because they do so much) as they 1324 are difficult to learn (because they do so much). 1325* Top level actors are required to connect two processes, regardless of whether 1326 the actors are the first in the process or not. As said above, the first 1327 actor is created through special means but other actors are created through 1328 messages. In Gecko, apart from the launcher and main processes, all new 1329 processes X are created with their first actor being between X and the main 1330 process. To create a connection between X and, say, a content process, the 1331 main process has to send connected ``Endpoints`` to X and to the content 1332 process, which in turn use those endpoints to create new top level actors 1333 that form an actor pair. This is discussed at length in :ref:`Connecting 1334 With Other Processes`. 1335 1336Top-level actors are not as frictionless as desired but they are probably 1337under-utilized relative to their power. In cases where it is supported, 1338``PBackground`` is sometimes a simpler alternative to achieve the same goals. 1339 1340Creating Top Level Actors From Other Actors 1341~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1342 1343The most common way to create new top level actors is by creating a pair of 1344connected Endpoints and sending one to the other actor. This is done exactly 1345the way it sounds. For example: 1346 1347.. code-block:: c++ 1348 1349 bool MyPreexistingActorParent::MakeMyActor() { 1350 Endpoint<PMyActorParent> parentEnd; 1351 Endpoint<PMyActorChild> childEnd; 1352 if (NS_WARN_IF(NS_FAILED(PMyActor::CreateEndpoints( 1353 base::GetCurrentProcId(), OtherPid(), &parentEnd, &childEnd)))) { 1354 // ... handle failure ... 1355 return false; 1356 } 1357 RefPtr<MyActorParent> parent = new MyActorParent; 1358 if (!parentEnd.Bind(parent)) { 1359 // ... handle failure ... 1360 delete parent; 1361 return false; 1362 } 1363 // Do this second so we skip child if parent failed to connect properly. 1364 if (!SendCreateMyActorChild(std::move(childEnd))) { 1365 // ... assume an IPDL error will destroy parent. Handle failure beyond that ... 1366 return false; 1367 } 1368 return true; 1369 } 1370 1371Here ``MyPreexistingActorParent`` is used to send a child endpoint for the new 1372top level actor to ``MyPreexistingActorChild``, after it hooks up the parent 1373end. In this example, we bind our new actor to the same thread we are running 1374on -- which must be the same thread ``MyPreexistingActorParent`` is bound to 1375since we are sending ``CreateMyActorChild`` from it. We could have bound on a 1376different thread. 1377 1378At this point, messages can be sent on the parent. Eventually, it will start 1379receiving them as well. 1380 1381``MyPreexistingActorChild`` still has to receive the create message. The code 1382for that handler is pretty similar: 1383 1384.. code-block:: c++ 1385 1386 IPCResult MyPreexistingActorChild::RecvCreateMyActorChild(Endpoint<PMyActorChild>&& childEnd) { 1387 RefPtr<MyActorChild> child = new MyActorChild; 1388 if (!childEnd.Bind(child)) { 1389 // ... handle failure and return ok, assuming a related IPDL error will alert the other side to failure ... 1390 return IPC_OK(); 1391 } 1392 return IPC_OK(); 1393 } 1394 1395Like the parent, the child is ready to send as soon as ``Bind`` is complete. 1396It will start receiving messages soon afterward on the event target for the 1397thread on which it is bound. 1398 1399Creating First Top Level Actors 1400~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1401 1402The first actor in a process is an advanced topic that is covered in 1403:ref:`the documentation for adding a new process<Adding a New Type of Process>`. 1404 1405PBackground 1406----------- 1407 1408Developed as a convenient alternative to top level actors, ``PBackground`` is 1409an IPDL protocol whose managees choose their worker threads in the child 1410process and share a thread dedicated solely to them in the parent process. 1411When an actor (parent or child) should run without hogging the main thread, 1412making that actor a managee of ``PBackground`` (aka a *background actor*) is an 1413option. 1414 1415.. warning:: 1416 Background actors can be difficult to use correctly, as spelled out in this 1417 section. It is recommended that other options -- namely, top-level actors 1418 -- be adopted instead. 1419 1420Background actors can only be used in limited circumstances: 1421 1422* ``PBackground`` only supports the following process connections (where 1423ordering is parent <-> child): main <-> main, main <-> content, main <-> socket 1424and socket <-> content. 1425 1426.. important:: 1427 1428 Socket process ``PBackground`` actor support was added after the other 1429 options. It has some rough edges that aren't easy to anticipate. In the 1430 future, their support may be broken out into a different actor or removed 1431 altogether. You are strongly encouraged to use new `Top Level Actors`_ 1432 instead of ``PBackground`` actor when communicating with socket process 1433 worker threads. 1434 1435* Background actor creation is always initiated by the child. Of course, a 1436request to create one can be sent to the child by any other means. 1437* All parent background actors run in the same thread. This thread is 1438dedicated to serving as the worker for parent background actors. While it has 1439no other functions, it should remain responsive to all connected background 1440actors. For this reason, it is a bad idea to conduct long operations in parent 1441background actors. For such cases, create a top level actor and an independent 1442thread on the parent side instead. 1443* Background actors are currently *not* reference-counted. IPDL's ownership 1444has to be carefully respected and the (de-)allocators for the new actors have 1445to be defined. See `The Old Ways`_ for details. 1446 1447A hypothetical layout of ``PBackground`` threads, demonstrating some of the 1448process-type limitations, is shown in the diagram below. 1449 1450.. mermaid:: 1451 :align: center 1452 :caption: Hypothetical ``PBackground`` thread setup. Arrow direction 1453 indicates child-to-parent ``PBackground``-managee relationships. 1454 Parents always share a thread and may be connected to multiple 1455 processes. Child threads can be any thread, including main. 1456 1457 flowchart LR 1458 subgraph content #1 1459 direction TB 1460 c1tm[main] 1461 c1t1[worker #1] 1462 c1t2[worker #2] 1463 c1t3[worker #3] 1464 end 1465 subgraph content #2 1466 direction TB 1467 c2tm[main] 1468 c2t1[worker #1] 1469 c2t2[worker #2] 1470 end 1471 subgraph socket 1472 direction TB 1473 stm[main] 1474 st1[background parent /\nworker #1] 1475 st2[worker #2] 1476 end 1477 subgraph main 1478 direction TB 1479 mtm[main] 1480 mt1[background parent] 1481 end 1482 1483 %% PBackground connections 1484 c1tm --> mt1 1485 c1t1 --> mt1 1486 c1t2 --> mt1 1487 1488 c1t3 --> mt1 1489 c1t3 --> st1 1490 1491 c2t1 --> st1 1492 c2t1 --> mt1 1493 1494 c2t2 --> mt1 1495 1496 c2tm --> st1 1497 1498 stm --> mt1 1499 st1 --> mt1 1500 st2 --> mt1 1501 1502Creating background actors is done a bit differently than normal managees. The 1503new managed type and constructor are still added to ``PBackground.ipdl`` as 1504with normal managees but, instead of ``new``-ing the child actor and then 1505passing it in a ``SendFooConstructor`` call, background actors issue the send 1506call to the ``BackgroundChild`` manager, which returns the new child: 1507 1508.. code-block:: c++ 1509 1510 // Bind our new PMyBackgroundActorChild to the current thread. 1511 PBackgroundChild* bc = BackgroundChild::GetOrCreateForCurrentThread(); 1512 if (!bc) { 1513 return false; 1514 } 1515 PMyBackgroundActorChild* pmyBac = bac->SendMyBackgroundActor(constructorParameters); 1516 if (!pmyBac) { 1517 return false; 1518 } 1519 auto myBac = static_cast<MyBackgroundActorChild*>(pmyBac); 1520 1521.. note:: 1522 ``PBackgroundParent`` still needs a ``RecvMyBackgroundActorConstructor`` 1523 handler, as usual. This must be done in the ``ParentImpl`` class. 1524 ``ParentImpl`` is the non-standard name used for the implementation of 1525 ``PBackgroundParent``. 1526 1527To summarize, ``PBackground`` attempts to simplify a common desire in Gecko: 1528to run tasks that communicate between the main and content processes but avoid 1529having much to do with the main thread of either. Unfortunately, it can be 1530complicated to use correctly and has missed on some favorable IPDL 1531improvements, like reference counting. While top level actors are always a 1532complete option for independent jobs that need a lot of resources, 1533``PBackground`` offers a compromise for some cases. 1534 1535IPDL Best Practices 1536------------------- 1537 1538IPC performance is affected by a lot of factors. Many of them are out of our 1539control, like the influence of the system thread scheduler on latency or 1540messages whose travel internally requires multiple legs for security reasons. 1541On the other hand, some things we can and should control for: 1542 1543* Messages incur inherent performance overhead for a number of reasons: IPDL 1544 internal thread latency (e.g. the I/O thread), parameter (de-)serialization, 1545 etc. While not usually dramatic, this cost can add up. What's more, each 1546 message generates a fair amound of C++ code. For these reasons, it is wise 1547 to reduce the number of messages being sent as far as is reasonable. This 1548 can be as simple as consolidating two asynchronous messages that are always 1549 in succession. Or it can be more complex, like consolidating two 1550 somewhat-overlapping messages by merging their parameter lists and marking 1551 parameters that may not be needed as optional. It is easy to go too far down 1552 this path but careful message optimization can show big gains. 1553* Even ``[moveonly]`` parameters are "copied" in the sense that they are 1554 serialized. The pipes that transmit the data are limited in size and require 1555 allocation. So understand that the performance of your transmission will be 1556 inversely proportional to the size of your content. Filter out data you 1557 won't need. For complex reasons related to Linux pipe write atomicity, it is 1558 highly desirable to keep message sizes below 4K (including a small amount for 1559 message metadata). 1560* On the flip side, very large messages are not permitted by IPDL and will 1561 result in a runtime error. The limit is currently 256M but message failures 1562 frequently arise even with slightly smaller messages. 1563* Parameters to messages are C++ types and therefore can be very complex in the 1564 sense that they generally represent a tree (or graph) of objects. If this 1565 tree has a lot of objects in it, and each of them is serialized by 1566 ``ParamTraits``, then we will find that serialization is allocating and 1567 constructing a lot of objects, which will stress the allocator and cause 1568 memory fragmentation. Avoid this by using larger objects or by sharing this 1569 kind of data through careful use of shared memory. 1570* As it is with everything, concurrency is critical to the performance of IPDL. 1571 For actors, this mostly manifests in the choice of bound thread. While 1572 adding a managed actor to an existing actor tree may be a quick 1573 implementation, this new actor will be bound to the same thread as the old 1574 one. This contention may be undesirable. Other times it may be necessary 1575 since message handlers may need to use data that isn't thread safe or may 1576 need a guarantee that the two actors' messages are received in order. Plan 1577 up front for your actor hierarchy and its thread model. Recognize when you 1578 are better off with a new top level actor or ``PBackground`` managee that 1579 facilitates processing messages simultaneously. 1580* Remember that latency will slow your entire thread, including any other 1581 actors/messages on that thread. If you have messages that will need a long 1582 time to be processed but can run concurrently then they should use actors 1583 that run on a separate thread. 1584* Top-level actors decide a lot of properties for their managees. Probably the 1585 most important are the process layout of the actor (including which process 1586 is "Parent" and which is "Child") and the thread. Every top-level actor 1587 should clearly document this, ideally in their .ipdl file. 1588 1589The Old Ways 1590------------ 1591 1592TODO: 1593 1594The FUD 1595------- 1596 1597TODO: 1598 1599The Rest 1600-------- 1601 1602Nested messages 1603~~~~~~~~~~~~~~~ 1604 1605The ``Nested`` message annotations indicate the nesting type of the message. 1606They attempt to process messages in the nested order of the "conversation 1607thread", as found in e.g. a mailing-list client. This is an advanced concept 1608that should be considered to be discouraged, legacy functionality. 1609Essentially, ``Nested`` messages can make other ``sync`` messages break the 1610policy of blocking their thread -- nested messages are allowed to be received 1611while a sync messagee is waiting for a response. The rules for when a nested 1612message can be handled are somewhat complex but they try to safely allow a 1613``sync`` message ``M`` to handle and respond to some special (nested) messages 1614that may be needed for the other endpoint to finish processing ``M``. There is 1615a `comment in MessageChannel`_ with info on how the decision to handle nested 1616messages is made. For sync nested messages, note that this implies a relay 1617between the endpoints, which could dramatically affect their throughput. 1618 1619Declaring messages to nest requires an annotation on the actor and one on the 1620message itself. The nesting annotations were listed in `Defining Actors`_ and 1621`Declaring IPDL Messages`_. We repeat them here. The actor annotations 1622specify the maximum priority level of messages in the actor. It is validated 1623by the IPDL compiler. The annotations are: 1624 1625============================== ================================================ 1626``[NestedUpTo=inside_sync]`` Indicates that an actor contains messages of 1627 priority [Nested=inside_sync] or lower. 1628``[NestedUpTo=inside_cpow]`` Indicates that an actor contains messages of 1629 priority [Nested=inside_cpow] or lower. 1630============================== ================================================ 1631 1632.. note:: 1633 1634 The order of the nesting priorities is: 1635 (no nesting priority) < ``inside_sync`` < ``inside_cpow``. 1636 1637The message annotations are: 1638 1639========================== ==================================================== 1640``[Nested=inside_sync]`` Indicates that the message can be handled while 1641 waiting for lower-priority, or in-message-thread, 1642 sync responses. 1643``[Nested=inside_cpow]`` Indicates that the message can be handled while 1644 waiting for lower-priority, or in-message-thread, 1645 sync responses. Cannot be sent by the parent actor. 1646========================== ==================================================== 1647 1648.. note:: 1649 1650 ``[Nested=inside_sync]`` messages must be sync (this is enforced by the 1651 IPDL compiler) but ``[Nested=inside_cpow]`` may be async. 1652 1653Nested messages are obviously only interesting when sent to an actor that is 1654performing a synchronous wait. Therefore, we will assume we are in such a 1655state. Say ``actorX`` is waiting for a sync reply from ``actorY`` for message 1656``m1`` when ``actorY`` sends ``actorX`` a message ``m2``. We distinguish two 1657cases here: (1) when ``m2`` is sent while processing ``m1`` (so ``m2`` is sent 1658by the ``RecvM1()`` method -- this is what we mean when we say "nested") and 1659(2) when ``m2`` is unrelated to ``m1``. Case (2) is easy; ``m2`` is only 1660dispatched while ``m1`` waits if 1661``priority(m2) > priority(m1) > (no priority)`` and the message is being 1662received by the parent, or if ``priority(m2) >= priority(m1) > (no priority)`` 1663and the message is being received by the child. Case (1) is less 1664straightforward. 1665 1666To analyze case (1), we again distinguish the two possible ways we can end up 1667in the nested case: (A) ``m1`` is sent by the parent to the child and ``m2`` 1668is sent by the child to the parent, or (B) where the directions are reversed. 1669The following tables explain what happens in all cases: 1670 1671.. |strike| raw:: html 1672 1673 <strike> 1674 1675.. |endstrike| raw:: html 1676 1677 </strike> 1678 1679.. |br| raw:: html 1680 1681 <br/> 1682 1683.. table :: Case (A): Child sends message to a parent that is awaiting a sync response 1684 :align: center 1685 1686 ============================== ======================== ======================================================== 1687 sync ``m1`` type (from parent) ``m2`` type (from child) ``m2`` handled or rejected 1688 ============================== ======================== ======================================================== 1689 sync (no priority) \* IPDL compiler error: parent cannot send sync (no priority) 1690 sync inside_sync async (no priority) |strike| ``m2`` delayed until after ``m1`` completes |endstrike| |br| 1691 Currently ``m2`` is handled during the sync wait (bug?) 1692 sync inside_sync sync (no priority) |strike| ``m2`` send fails: lower priority than ``m1`` |endstrike| |br| 1693 Currently ``m2`` is handled during the sync wait (bug?) 1694 sync inside_sync sync inside_sync ``m2`` handled during ``m1`` sync wait: same message thread and same priority 1695 sync inside_sync async inside_cpow ``m2`` handled during ``m1`` sync wait: higher priority 1696 sync inside_sync sync inside_cpow ``m2`` handled during ``m1`` sync wait: higher priority 1697 sync inside_cpow \* IPDL compiler error: parent cannot use inside_cpow priority 1698 ============================== ======================== ======================================================== 1699 1700.. table :: Case (B): Parent sends message to a child that is awaiting a sync response 1701 :align: center 1702 1703 ============================= ========================= ======================================================== 1704 sync ``m1`` type (from child) ``m2`` type (from parent) ``m2`` handled or rejected 1705 ============================= ========================= ======================================================== 1706 \* async (no priority) ``m2`` delayed until after ``m1`` completes 1707 \* sync (no priority) IPDL compiler error: parent cannot send sync (no priority) 1708 sync (no priority) sync inside_sync ``m2`` send fails: no-priority sync messages cannot handle 1709 incoming messages during wait 1710 sync inside_sync sync inside_sync ``m2`` handled during ``m1`` sync wait: same message thread and same priority 1711 sync inside_cpow sync inside_sync ``m2`` send fails: lower priority than ``m1`` 1712 \* async inside_cpow IPDL compiler error: parent cannot use inside_cpow priority 1713 \* sync inside_cpow IPDL compiler error: parent cannot use inside_cpow priority 1714 ============================= ========================= ======================================================== 1715 1716We haven't seen rule #2 from the `comment in MessageChannel`_ in action but, as 1717the comment mentions, it is needed to break deadlocks in cases where both the 1718parent and child are initiating message-threads simultaneously. It 1719accomplishes this by favoring the parent's sent messages over the child's when 1720deciding which message-thread to pursue first (and blocks the other until the 1721first completes). Since this distinction is entirely thread-timing based, 1722client code needs only to be aware that IPDL internals will not deadlock 1723because of this type of race, and that this protection is limited to a single 1724actor tree -- the parent/child messages are only well-ordered when under the 1725same top-level actor so simultaneous sync messages across trees are still 1726capable of deadlock. 1727 1728Clearly, tight control over these types of protocols is required to predict how 1729they will coordinate within themselves and with the rest of the application 1730objects. Control flow, and hence state, can be very difficult to predict and 1731are just as hard to maintain. This is one of the key reasons why we have 1732stressed that message priorities should be avoided whenever possible. 1733 1734.. _comment in MessageChannel: https://searchfox.org/mozilla-central/rev/077501b34cca91763ae04f4633a42fddd919fdbd/ipc/glue/MessageChannel.cpp#54-118 1735 1736.. _Message Logging: 1737 1738Message Logging 1739~~~~~~~~~~~~~~~ 1740 1741The environment variable ``MOZ_IPC_MESSAGE_LOG`` controls the logging of IPC 1742messages. It logs details about the transmission and reception of messages. 1743This isn't controlled by ``MOZ_LOG`` -- it is a separate system. Set this 1744variable to ``1`` to log information on all IPDL messages, or specify a 1745comma-separated list of **top-level** protocols to log (e.g. 1746``MOZ_IPC_MESSAGE_LOG="PMyManagerChild,PMyManagedParent,PMyManagedChild"``). 1747:ref:`Debugging with IPDL Logging` has an example where IPDL logging is useful 1748in tracking down a bug. 1749 1750.. important:: 1751 The preceeding ``P`` and the ``Parent`` or ``Child`` suffix are required 1752 when listing individual protocols in ``MOZ_IPC_MESSAGE_LOG``. 1753