Proposal: Mojo Synchronous Methods

yzshen@chromium.org

02/02/2016

Overview

Currently there are quite a lot of sync IPC messages in Chrome: A quick search of IPC_SYNC_MESSAGE* in *messages.h returned 239 results. Some messages such as PpapiHostMsg_ResourceSyncCall are used as generic sync wrappers for other messages, so there are even more sync messages.

In order to facilitate conversion of Chrome IPC messages to Mojo interfaces, this document presents the idea of Mojo synchronous methods.

Please note: Sync calls should be avoided whenever possible!

  • Although some sync IPCs are necessary; some might be added just for conveniences. For the latter, in-depth refactoring should be done eventually to convert them to async calls.

  • Sync calls hurt parallelism and therefore hurt performance.

  • Re-entrancy changes message order and produces call stacks that you probably never think about while you are coding. It has always been a huge pain.

  • Sync calls may lead to deadlocks.

  • As you can see below, this feature is similar to the current Chrome sync IPC support, but is less flexible (mostly intentionally). For example, with Chrome IPCs you can configure any async IPC message to “unblock” so it can re-enter ongoing sync calls at the receiving side. Such capability is not supported for Mojo sync calls. Unless later we see strong demand, I personally would like to consider lack-of-support as a feature in this case. :)

Mojom

A new attribute [Sync] (or [Sync=true]) is introduced for methods. For example:

interface Foo {

[Sync]

SomeSyncCall() => (Bar result);

};

It indicates that when SomeSyncCall() is called, the control flow of the calling thread is blocked until the response is received.

It is not allowed to use this attribute with functions that don’t have responses. If you just need to wait until the service side finishes processing the call, you can use an empty response parameter list:

[Sync]

SomeSyncCallWithNoResult() => ();

Message format

A new flag is defined for the flags field of message header:

enum {

kMessageExpectsResponse      = 1 << 0,

kMessageIsResponse           = 1 << 1,

kMessageIsSync               = 1 << 2

};

If kMessageIsSync is set, either kMessageExpectsResponse or kMessageIsResponse must also be set.

Generated bindings (C++)

Response is mapped to output parameters. The boolean return value indicates whether the operation is successful. Returning false usually means a connection error has occurred.

// Client side:

virtual bool SomeSyncCall(BarPtr* result) = 0;

The implementation side implements a different signature:

// Service side:

virtual void SomeSyncCall(const SomeSyncCallCallback& callback) = 0;

The reason to use a signature with callback at the impl side is that the implementation may need to do some async works which the sync method’s result depends on.

There are two ways to organize these signatures:

Putting them in two different interfaces:

class Foo {

public:

class Service {

virtual void SomeSyncCall(const SomeSyncCallCallback& callback) = 0;

...

};

class Client {

virtual bool SomeSyncCall(BarPtr* result) = 0;

...

};

};

Or, put them in a single interface:

class Foo {

public:

virtual void SomeSyncCall(const SomeSyncCallCallback& callback) = 0;

virtual bool SomeSyncCall(BarPtr* result) {

// The service side should implement the other signature.

NOTREACHED();

return false;

}

};

I personally prefer the second approach: it requires less changes to the existing bindings/user code; besides, it allows the client to use both the async and sync way to make the call. (That being said, the first approach is more clear about the capability/responsibility of both sides.)

Re-entrancy behavior

What should happen on the calling thread while waiting for the response of a sync method call? This proposal adopts the following behavior: continue to process incoming sync request messages (i.e., sync method calls); block other messages, including async messages and sync response messages that don’t match the ongoing sync call.

Please note that sync response messages that don’t match the ongoing sync call cannot re-enter. That is because they correspond to sync calls down in the call stack. Therefore, they need to be queued and processed while the stack unwinds.

Please also note that such re-entrancy behavior doesn’t eliminate deadlocks involving async calls. For example:

(If you find that you get into this situation, you probably want to either change async_call_b to a sync call, or use the pattern described in alternative (2) below.)

Alternatives considered (but disfavored):

  1. no re-entrancy: block the thread completely until the response message arrives.

Alternative (1) results in deadlocks, if two or multiple parties can issue sync calls to others and create a cycle. Besides, it is easy to achieve the same purpose using the following pattern:

interface SyncCallWaiter {

Done(Bar result);

};

interface Foo {

SyncCall(SyncCallWaiter);

};

foo->SyncCall(std::move(sync_call_waiter_ptr));

// Block and wait for the service side to call Done().

sync_call_waiter_binding.WaitForIncomingMethodCall();

  1. full re-entrancy: continue to process all messages.

You can achieve the same purpose using the following pattern:

interface Foo {

AsyncCall() => (Bar result);

};

foo.set_connection_error_handler([&run_loop]() { run_loop.Quit(); });

foo->AsyncCall([&run_loop](BarPtr result) { run_loop.Quit(); });

run_loop.Run();

  1. context-based re-entrancy: continue to process incoming sync request messages caused by the ongoing sync call, but not other messages. For example, in the following diagram, sync_call_b is resulted from sync_call_a so it is allowed to re-enter; on the other hand, sync_call_c is not resulted from sync_call_a so it is postponed until sync_call_a completes:

Alternative (3) seems useful to reduce sync call re-entrancy. However, it can lead to deadlocks similar to alternative (1). Also, it is hard (if not impossible) to determine automatically whether a call is “caused by” another call. Consider complicating the example above a little bit: what should we do if in order to serve sync_call_a, app_2 has to make an async call to app_4 which in turn sends sync_call_d to app_1? The bindings probably have to require the user to pass a context ID around explicitly, in order to tell whether a call is caused by another call.

Message pumping and scheduling

Basic case

Obviously, when an interface pointer (let’s call it calling_ptr) is used to make a sync call, we need to watch calling_ptr’s message pipe handle for response.

In addition, we also need to watch all bindings that serve sync calls on the same thread. A thread-local registry is necessary to keep track of all those bindings.

While waiting for sync response on calling_ptr, those bindings being watched may receive async requests; calling_ptr may receive async responses and non-matching sync responses (for previous sync calls down in the call stack). They all need to be queued and processed later.

More complex case: associated interfaces involved

It becomes more complex when associated interfaces are involved. Because master interface endpoints (no matter they are bindings or interface pointers) serve as routers for all associated interfaces running on the same pipe. Those associated interfaces may live on different threads. Also, associated interfaces may contain sync calls. It means we also need to watch:

  • All interface ptrs and bindings that serve as master endpoints. Even if there are only async messages, the destination associated endpoints may live on a different thread. It is undesirable to block them.

  • Associated bindings that serve sync calls. Because associated bindings don’t own a message pipe handle, we need to set up a control message pipe between an associated binding and its corresponding master endpoint to signal about sync message arrival.

Combine all the cases above, while waiting for a sync response on calling_ptr, we will need to watch:

Calling_ptr could be an associated interface pointer, too. We also need to use a control message pipe between it and its corresponding master endpoint. But the rest is the same.

In some cases, users may want to enforce that certain threads shouldn’t make any sync calls. It is straightforward to set such policy at the thread-local registry and enforce it when calling_ptr queries what handles should be watched.

[Chromium文档转载,第003章]Proposal: Mojo Synchronous Methods的更多相关文章

  1. [Chromium文档转载,第004章]Mojo Synchronous Calls

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Synchronous Calls Think carefully before ...

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

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

  3. [Chromium文档转载,第001章] Mojo Migration Guide

        For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Mojo Migration Guide 目录 1 Summary 2 H ...

  4. [Chromium文档转载,第007章]JNI on Chromium for Android

    Overview JNI (Java Native Interface) is the mechanism that enables Java code to call native function ...

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

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

  6. [Chromium文档转载,第005章]Calling Mojo from Blink

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Calling Mojo from Blink Variants Let's as ...

  7. 用R创建Word和PowerPoint文档--转载

    https://www.jianshu.com/p/7df62865c3ed Rapp --简书 Microsoft的Office软件在办公软件领域占有绝对的主导地位,几乎每个职场人士都必须掌握Wor ...

  8. java实现支付宝接口--文档..转载

    //实现java支付宝很简单,只要从支付宝官方下载   http://help.alipay.com/support/index_sh.htm下载程序,配置一下参数就OK了:   1.先到http:/ ...

  9. iOS开发主要参考文档(转载)

    Objective-C,语言的系统详细资料.这是做iOS开发的前题与基础.https://developer.apple.com/library/ios/#documentation/Cocoa/Co ...

随机推荐

  1. Java基础学习总结(8)——super关键字

    一.super关键字 在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象.怎么去引 ...

  2. 洛谷 P1275 魔板

    P1275 魔板 题目描述 有这样一种魔板:它是一个长方形的面板,被划分成n行m列的n*m个方格.每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗).我们可以通过若干操作使魔板从一个状态改变为另一个状 ...

  3. shuoj1936-D序列—最长上升子序列

    Description 已知两个长度为N的数组A和B.下标从0标号至N-1. 如今定义一种D序列 (如果长度为L).这样的序列满足下列条件: 1. 0 <= D[i] <= N-1 2.  ...

  4. 有关Java基础的一些笔试题总结

    针对近期腾讯.京东.网易等公司的笔试.遇到一些有关Java基础的问题,在此总结.希望能通过这几道经典问题题发散,举一反三.借此打牢基础! 自己总结,望提出宝贵意见! 一.关于null的一道小题 先开开 ...

  5. PhpStorm Live Template加PHP短语法Short Open Tags打造原生模板

    关于Php要不要使用模板一直被大家讨论,支持的说使用模板更简洁,易与前端project师交流.反对的说Php本身就支持内嵌语法,不是必需再用个模板,减少性能. 事实上使用Php的短语法.直接嵌入也不是 ...

  6. Innosetup

    卸载的同时删除日志,卸载的时候判断程序是否正在运行,regsvr32 1.卸载程序的时候如何判断程序是否正在运行 http://bbs.csdn.net/topics/370097914 2.强制删除 ...

  7. tomcat:Could not publish to the server. java.lang.IndexOutOfBoundsException

    1.将工程加入到tomcat,报上述错误 2. run--maven build 报jar包错误: invalid LOC header (bad signature) 3.根据提示找到上述jar包, ...

  8. 线程1—Thread

    随便选择两个城市作为预选旅游目标.实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市.分别用Runnable接口和Thread类实 ...

  9. 以下三种下载方式有什么不同?如何用python模拟下载器下载?

    问题始于一个链接https://i1.pixiv.net/img-zip-...这个链接在浏览器打开,会直接下载一个不完整的zip文件 但是,使用下载器下载却是完整文件 而当我尝试使用python下载 ...

  10. iOS开发下对MVVM的理解

    最近看到新浪微博上以及iOS开发的论坛里面谈到MVVM设计模式,所谓MVVM就是Model-View-ViewModel的缩写,关于MVVM的概念,这里我不想过多的介绍,有很多介绍的很详细的博文,这里 ...