Mojo Associated Interfaces

yzshen@chromium.org

02/22/2017

Background

Before associated interfaces are introduced, each Mojo interface is always run on a separate message pipe, so there is no ordering guarantee between different interfaces. It could cause difficulties for using a set of semantically related interfaces. For example, consider the following call sequence:

file_ptr->Append(...);

file_system_ptr->GetUsedSpace(...);

Even if the impls of both interfaces live on the same process and thread, the arriving order of Append() and GetUsedSpace() is undefined. Another example:

// Run a callback of the File interface and send an event using the

// corresponding FileClient interface.

file_append_callback.Run(...);

file_client_ptr->NotifyFileSizeChanged(...);

Again, the arriving order of the callback and NotifyFileSizeChanged() is undefined.

This is not only counterintuitive, but also likely to have a negative impact on API design. With lack of ordering, developers are more inclined to define a monolithic interface for a logical grouping of functionality, rather than properly splitting the functionality into multiple interfaces.

Associated interfaces are used to solve this “FIFO” problem. It enables running multiple interfaces over a single message pipe. It also allows the bindings to use a single message pipe from multiple threads.

Overview

When a message pipe is created to run an interface, the interface is called the master interface for the message pipe. Associated interfaces can be set up on the same message pipe. Each message pipe can have an arbitrary number of associated interfaces but always a single master interface. The impl side of associated interfaces can live at either end of the message pipe.

Each interface is assigned an interface ID. The ID is scoped to the message pipe. Messages have an interface ID field so that they can be routed to the corresponding interface.

For the sake of convenience, the document refers to both the impl and client side of an interface as interface endpoints.

The work of setting up an associated interface includes allocating an interface ID; setting up an interface endpoint at one end of the message pipe; passing the ID over the message pipe; and setting up the corresponding interface endpoint there. From a user’s point of view, he/she creates a pair of AssociatedInterfacePtrInfo and AssociatedInterfaceRequest, binds one of them to Associated{InterfacePtr|Binding} locally, passes the other using another interface (either master or associated) to the remote side.

The following figure shows a message pipe running multiple interfaces.

In the figure above, either direction of the message pipe is a FIFO queue and therefore preserves message order:

  • right -> left: master and associated_2 method calls, associated_1 callbacks.

  • left -> right: master and associated_2 callbacks, associated_1 method calls.

Although each interface should be used strictly on a single thread, using master and associated interfaces on different threads is allowed. FIFO-ness is still preserved in this case. For example: Master sends message_1 and then signals assciated_1 (living on another thread) to send message_2. Message_1 and message_2 are guaranteed to arrive at the other end in order.

Because associated interfaces don’t have their own message pipes, there are some important restrictions:

  • At either end of the message pipe, the code for the master and associated interfaces must live in the same process.

  • Associated interface pointers and requests cannot be passed back over the same message pipe or forwarded over a different message pipe.

  • Extracting the underlying message pipe handles for the master interface is allowed, but it closes any existing associated interfaces.

  • It is disallowed to make calls on an associated interface foo_ptr before the associated interface request foo_request is sent. Doing so is considered as a programming error which could lead to crash or message pipe being closed. This restriction is required by the FIFO-ness guarantee: when receiving a message of foo method calls, we want to dispatch it to the foo impl before processing any subsequent messages. If foo_request is in a subsequent message, message dispatching gets into a deadlock.

On the other hand, as soon as foo_request is sent, foo_ptr is usable. There is no need to wait until foo_request is bound with an impl. If a message of foo method calls arrives before foo_request is bound, the message (and any subsequent messages) needs to be queued.

Mojom

A new keyword associated is introduced for interface pointers or requests. For example:

struct Qux {

associated Bar bar3;

};

interface Foo {

SetBar(associated Bar bar1);

GetBar(associated Bar& bar2);

PassQux(Qux qux);

};

bar1/bar2/bar3 are all associated with the message pipe on which they are passed.

Message format

The message header definition is changed to:

struct MessageHeader {

uint32 interface_id;

uint32 type;

uint32 flags;

[MinVersion=1] uint64 request_id;

[MinVersion=2] GenericStruct payload;

[MinVersion=2] array<uint32> payload_interface_ids;

};

The interface_id field indicates which interface the message should be routed to.

If a message transfers associated interface pointers or requests, it needs to use message header struct version 2 (or above). The payload field points to the payload struct. The payload_interface_ids field points to a mojo array of uint32 containing all interface IDs transferred.

In the message payload, an associated interface pointer is encoded as a uint32 index into the payload_interface_ids array + a uint32 version field; an associated interface request is encoded as a uint32 index into the payload_interface_ids array.

Why do we need the payload_interface_ids array? Why don’t we put interface IDs directly in the payload? If a message is discarded, or contains fields that are not understood due to version difference, the router wants to know what interface IDs are abandoned, in order to notify the sender side to raise connection error on the corresponding interface endpoints. Please consider the following example:

(1) The user creates AssociatedInterfacePtr<Foo> foo_ptr and AssociatedInterfaceRequest<Foo> foo_request.

(2) He sends foo_request over the message pipe, using a method call on another interface bar_ptr.

(3) At the other side, bar_binding is closed before it accepts the method call containing foo_request, so the corresponding message is discarded by the router.

(4) The router wants to notify the sending side to raise a connection error on foo_ptr.

Interface ID allocation

  • Interface IDs are scoped to the message pipe they are used on.

  • 0 is the master interface ID;  0xFFFFFFFF is the invalid ID.

  • IDs of associated interface can be generated at both sides of the message pipe. In order to avoid collision, the highest bit is used as namespace bit: at the side where the client-side of the master interface lives, IDs are generated with the namespace bit set to 1; at the opposite side IDs are generated with the namespace bit set to 0.

Control messages

Because closing the client/impl side of an associated interface doesn’t result in message pipe disconnection, we need control messages to notify its peer. There are two control messages:

NotifyPeerEndpointClosed: Notifies that an interface endpoint set up at the message sender side has been closed. It carries an interface ID as payload, as well as an optional disconnect reason.

NotifyPeerEndpointClosed is pretty straightforward. Please consider the following example:

(1) The user creates AssociatedInterfacePtr<Foo> foo_ptr and AssociatedInterfaceRequest<Foo> foo_request. Assume the interface ID is foo_id.

(2) He sends foo_request over the message pipe, which is then bound to foo_binding.

(3) foo_ptr is closed.

(4) foo_binding is closed.

The following is the message sequence and the bookkeeping inside the message routers at both sides:

Performance considerations

When using associated interfaces on threads different than the master thread (where the master interface lives):

  • Sending messages: send happens directly on the calling thread. So there shouldn’t be performance penalty except the locking operation in router/connector.

  • Receiving messages: the router listens on the master thread, therefore there will be one extra thread hop. This cannot be overcomed with the current mojo system API. If later we decide to remove this extra hop, we will need to invent new system api or push this feature downwards into the system layer.

Therefore, performance-wise associated interfaces are better suited for scenarios where message receiving happens on the master thread:

Bindings (C++)

This document shows how to use associated interfaces in C++.

Older versions of design doc

Mojo Associated Interfaces的更多相关文章

  1. Mojo C++ Bindings API

    This document is a subset of the Mojo documentation. Contents Overview Getting Started Interfaces Ba ...

  2. [Chromium文档转载,第002章]Mojo C++ Bindings API

    Mojo C++ Bindings API This document is a subset of the Mojo documentation. Contents Overview Getting ...

  3. Mojom IDL and Bindings Generator

    Mojom IDL and Bindings Generator This document is a subset of the Mojo documentation. Contents Overv ...

  4. Calling Mojo from Blink

    Variants Let's assume we have a mojom file such as this:   module example.mojom;   interface Foo {   ...

  5. Converting Legacy Chrome IPC To Mojo

    Converting Legacy Chrome IPC To Mojo Looking for Mojo Documentation? Contents Overview Deciding What ...

  6. Mojo For Chromium Developers

    Overview This document contains the minimum amount of information needed for a developer to start us ...

  7. Mojo C++ System API

    This document is a subset of the Mojo documentation. Contents Overview Scoped, Typed Handles Message ...

  8. Mojo For Chromium Developers1

    Mojo For Chromium Developers Overview This document contains the minimum amount of information neede ...

  9. [Chromium文档转载,第006章]Chrome IPC To Mojo IPC Cheat Sheet

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Chrome IPC To Mojo IPC Cheat Sheet 目录 1 O ...

随机推荐

  1. android studio 、 as 如何导入eclipse项目

    安卓项目有两种,一种是eclipse开发的,一种的android studio开发的.有些在github开源的安卓项目,下载下来之后不知道该如何处理了. 这个是Eclipse安卓项目的目录结构. 这个 ...

  2. FireEye APT检测——APT业务占比过重,缺乏其他安全系统的查杀和修复功能

    摘自:https://zhidao.baidu.com/question/1694626564301467468.html火眼,APT威胁下快速成长 FireEye的兴起开始于2012年,这时段正好迎 ...

  3. Linux下DNS服务器搭建详解

    Linux下DNS服务器搭建详解 DNS  即Domain Name System(域名系统)的缩写,它是一种将ip地址转换成对应的主机名或将主机名转换成与之相对应ip地址的一种机制.其中通过域名解析 ...

  4. Z 字形变换 C++实现 java实现 leetcode系列(六)

    Z 字形变换  java实现 C++实现  将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 ...

  5. Java数据库连接——jdbc-odbc桥连接方式及汉字乱码问题

    jdbc-odbc桥连接方式操作数据库SU(Course),其中Course属性有Cno,Cname,Cpno,Ccredit. 步骤: 1.配置数据源 控制面板下搜索管理工具->ODBC数据源 ...

  6. hiho160周 - 字符串压缩,经典dp

    题目链接 小Hi希望压缩一个只包含大写字母'A'-'Z'的字符串.他使用的方法是:如果某个子串 S 连续出现了 X 次,就用'X(S)'来表示.例如AAAAAAAAAABABABCCD可以用10(A) ...

  7. Java框架之spring—jdbcTemplate

    JdbcTemplate 今天我们利用 springIOC 写一个 JdbcTemplate 来实现一个表的简单的增删改查 步骤如下: 首先创建数据库,创建一个学生表 student (id,name ...

  8. 系统出现0x0000006B蓝屏修复,系统文件损坏 bootcat.cache、driver.stl

    系统蓝屏,无论如何都不能进入系统,所以你需要一个U盘启动器,就是能绕过电脑的系统进入电脑,可以用U盘做一个U盘启动器,或者其他方法均可以,只要能进入到你的电脑访问C盘即可 2 下载链接内的文件解压后放 ...

  9. adb如何连接mumu模拟器并修改Android ID

    adb工具下载安装 https://dl.google.com/android/repository/platform-tools-latest-windows.zip 参考:https://blog ...

  10. /etc/rc.d/rc.sysinit

    [root@web02 ~]# ls /etc/rc.d/rc.sysinit /etc/rc.d/rc.sysinit [root@web02 ~]# [root@web02 ~]# ls /etc ...