The future is the value, the promise is the function that sets the value – essentially the return value (future) of an asynchronous function (promise). Setting the value of a future is also called resolving, fulfilling, or binding it. —— from Wikipedia

1 Preface

Seastar (f-p-c) 属于Reactive编程的一个子集。
Seastar的性能源自 sharded,cooperative,non-blocking的微任务调度设计, 而 f-p-c 是将 task 送入调度程序的一种更友好的方式。

Seastar 引入了 future/promise, 把面向 callback 编程变成了面向future/promise编程, 将以前的回调代码包装在 lambda_task 中,然后交给future调度。

  • future 是一个 value ;
  • promise 是一个函数,本质是返回 value 的异步函数;
  • promise 写入 value 的过程又称为解决、履行或绑定它

具体来说,当区分用法时,future 是变量的只读占位符视图,而 promise 是可写的单一赋值设置未来 value 的容器。

为了满足复杂的异步编程需要,Seastar重新实现了一些有别于标准库中的数据结构,因为像 thread safe这样的要求是无需考虑的。这里主要讨论的是Seastar实现的 future/promise。

(注: 下文将使用 f/p 作为 future/promise的简称)

2 future & promise

future 和 promise 都有一个 _state 指针成员,都指向同一个future_state<T>堆对象;
future_state 堆对象作为一个桥梁,为 future 和 promise 建立了联系;
同时 future_state 可以携带一个task。如下图所示:

future-promise-v1

3 f/p 类图

详细的类图如下,需要注意一点, promise有一个get_future() 方法,
是调用 future 的 private 构造函数构造的。

f-p-class-diagram

4 f/p 调用时序图

下面来看看 Seastar f/p 调用的时序图:
f-p-timing-diagram

Continuation在这里可以理解为:一个 task 的单链表,一个task内链着一个task; f-p-each-loop f/p是成对使用的,相同颜色的f/p表示一对,因为他们操作同一个future_state;
future.then() 可以看成一个中间人,他会定义一个新的 promiseA,并产生 futureA, 把 this,promiseA 转移到一个 lambda_task 内,最后返回futureA。

5 对象占用的空间

我们可以看到 future/promise 对象一般都位于栈上,都只有1个指针成员,所以大小 8 bytes;
而且这两个对象在定义之后,是需要被 move 到不同的lambda_task之中的,可谓是非常高效的设计。

但是future_state对象位于堆上,而且占用的字节会比较多。

6 future & promise 改进

后面为了减少堆空间占用,作者不再使用 future_state 作为桥梁,而使 f/p 直接建立相互关系, 并且 future_state 只用于存储 task 和 task 的执行结果 tuple。

v2-smaller-heap

7 对象生命周期

一般来说, promise 对象在产生后,会被move到一个 lambda_task 里面,
lambda_task 执行完使命也就完成了,析构lambda_task时,promise 对象生命周期随之结束。
而 future 对象此时还在一个待执行的 task 里面。

8 总结

(1) promise 对象会构造一个future对象,同时 future_state 的所有权也会转交给future对象;
(2) promise 会先于 future 对象消亡,满足严格的偏序关系。
(3) future.then() 会将传入的lambda + 当前的future自身 + 局部定义的promise, 这3个对象的所有权都转移到一个更大的 lambda_W 之中, 最后将这个还未ready的 lambda_W 放入 future_state 的task成员上,然后等待时机被调度。

QA

最后问个问题:future_state之为啥要用future前缀命名?
因为future_state 对象的生命周期由future对象管理。

Reference

《全文完》

文末尾给出了《Seastar 源码之美》系列文章的目录,希望你能在这个列表里找到自己感兴趣的内容。