Build Your Own HandlerThread Part 1

MessagePump

对于我们的 ActiveThread 来说,核心仍然是持续不断运行的 message-loop;但是在实现 message-loop 前,我们需要首先来实现它的“引擎”:MessagePump

大体上,message-loop 要维持运转,就要能够源源不断地从某个地方获取消息/事件,而获取消息/事件的这个行为,就是由 message-pump 来完成,正如他的名字一样。

这里可能会产生一个疑问,为什么要单独抽出一个 message-pump,而不是直接把这部分实现做到 message-loop 里呢?

原因是:一个复杂的系统可能会有多个消息/事件来源,每个来源会有专门的获取、分发的方式。

例如:

  • 界面消息可能是从系统的 GUI 消息系统中获得。一个典型的例子是 Win32 GUI 程序所使用的 GetMessage()
  • 网络活动事件通常从 native 系统提供的 I/O multiplexor 中获得。例子包括 POSIX 标准的 select(2), poll(2);Linux 独有的 epoll;以及 Windows 提供的真-异步I/O机制 IO Completion Port。

注:GUI 系统中多使用 messages(消息)作为术语;而网络 I/O 等倾向于使用 events(事件)作为使用术语。因为此系列文章不涉及网络 I/O,因此以后均采用_消息_作为我们的使用术语。

注1:对于 Android 系统,他的 GUI 事件实质上是通过 epoll 监听关联到 device 抽象的 pipe fd 来完成的;Linux 的各类 X-Window 实现大多也是类似做法。

所以为了解耦具体的消息来源,同时让我们的实现看上去更加有逼格,我们采取分离实现 message-pump 和 message-loop。

前面说到可能有多种 pump,所以这里我们的 MessagePump 应该是一个 interface,规定了一个 message-pump 应该具有哪些基本的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.time.Instant;

interface MessagePump {
interface Delegate {
boolean doWork();
boolean doDelayedWork();
Instant nextDelayedWorkExpiration();
}

void run(Delegate delegate);

// Quit the pump.
// This method can only be called on the thread that called run().
void quit();

// Thread-safe
void wakeup();
}

同时我们定义了一个 Delegate interface,message-loop 需要实现这个 interface,以便让 message-pump 可以通过这个 interface 来按照自己的实现来操作 message-loop。

什么都不做的 MessagePumpDefault

我们的 ActiveThread 运行的纯 Java 环境,不需要像 Android 那样能够获取设备输入消息(Frankly,你想做还做不了呢,我哪来的设备信息获取啊),有点类似 worker HandlerThread.

Read More

Build Your Own HandlerThread Part 0

立一个 Flag

HandlerThread 是 Android runtime 提供的一种线程基础设施;和 Java Thread 相比,HandlerThread 最大的优点是自带消息循环(message-loop),这使得用户可以很方便地基于 HandlerThread 实现 CSP (Communicating Sequential Process) 模型,极大降低多线程编程的复杂度。

Read More

Monthly Read Posts in Nov 2018

Algorithms

Shuffling

Read More

Chromium Base MessageLoop Internals (1)

class MessageLoop

Version: r70_3538
File: base/message_loop/message_loop.{h, cc}

A MessageLoop is used to process events for a particular thread, i.e. the core infrastructure for implementing Communicating Sequential Process (CSP) model.

There is at most one MessageLoop instance on a thread.

MessageLoop is a farily complex class, it driveds from multiple base classes:

1
2
3
4
5
class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
public RunLoop::Delegate,
public MessageLoopCurrent {
// omitted...
};

MessageLoop Type

A MessageLoop has a particular type, differred by the set of asynchronous events it is capable of handling.

Read More

在 cmder 里以 mintty 为终端的方式运行 bash 和 wsl

在 cmder 里如果不指定运行的 terminal 则会默认使用 windows cmd,而这在使用 git-bash 或者 wsl 时会经常遇到问题:

  1. 性能底下导致交互出现显著延迟(这个发生在使用 tig 等重型 bash 命令或者使用 wsl中)
  2. 容易因为环境属性问题导致乱码,比如 cmder 里直接运行 git-bash 的 tig 会发现提交历史的分割线都乱码了

Read More

MSVC 对多继承下的 EBO 支持的一个 workaround

首先简单介绍一下 EBO(Empty Base Class Optimization)。

因为 C++ 规定,任何一个 instance 在内存中必须要有唯一的地址,因此一个空的 class/struct 会在编译时被偷偷插入一个外人看不到的 char mem;,于是这个空类的每一个 instance 都可以有一个唯一的地址了。

但是如果将这个空类作为某个类的成员时,这个隐藏的成员会被计入内存布局之中,考虑到 memory padding,有时候会导致类对象体积膨涨一倍。

例如考虑:

1
2
3
4
5
6
7
8
9
class E {};

class A1 {
E e;
int i;
};

assert(sizeof(E) == 1);
assert(sizeof(A1) == 8);

我们会发现每一个 A1 的 instance 都占了 8-byte,比起 4-byte 足足翻了一倍。

占用内存无端变大导致 cache 问题啥的就不讲了,这方面的内容任何讲 computer architecture 的书应该都会有。

Read More

Chromium Base MessageLoop Internals (0)

Our fancy star in this post is class base::MessageLoopCurrent.

MessageLoopCurrent

Version: r70_3538
File: base/message_loop/message_loop_current.{h, cc}

MessageLoopCurernt is a proxy class for interactions with the MessageLoop bound to the current thread.

It is introduced to avoid direct uses of MessageLoop::current(), quoting from original comments:

Why: Historically MessageLoop::current() gave access to the full MessageLoop API, preventing both addition of powerful owner-only APIs as well as making it harder to remove callers of deprecated APIs (that need to stick around for a few owner-only use cases and re-accrue callers after cleanup per remaining publicly available).

Because it is a light-weight proxy, it contains only a single pointer to the MessageLoop bound to the current thread.

Read More