boost 协程接口变化

介绍

boost-1.61 版本之后协程库 context 的接口改变了,汇编代码里的参数、返回值和语义都发生了改变,另外还增加了新的接口和汇编实现 ontop_fcontext。

旧版本接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace boost {
namespace context {

/**
* @biref 执行环境上下文
*/
typedef void* fcontext_t;

/**
* @biref 初始化执行环境上下文
* @param sp 栈空间地址
* @param size 栈空间的大小
* @param fn 入口函数
* @return 返回初始化完成后的执行环境上下文
*/
extern "C" BOOST_CONTEXT_DECL
fcontext_t BOOST_CONTEXT_CALLDECL make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) );

/**
* @biref 跳转到目标上下文
* @param ofc 当前的上下文会保存到ofc中
* @param nfc 跳转到的目标上下文
* @param vp 如果是第一次跳转,作为函数参数传入,如果是调回到jump_fcontext,这个是返回值
* @param preserve_fpu 是否复制FPU(浮点数寄存器)数据
* @return 如果调回时的透传参数
*/
extern "C" BOOST_CONTEXT_DECL
intptr_t BOOST_CONTEXT_CALLDECL jump_fcontext( fcontext_t * ofc, fcontext_t nfc,
intptr_t vp, bool preserve_fpu = false);

}}

新版本接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
namespace boost {
namespace context {
namespace detail {

/**
* @biref 执行环境上下文
*/
typedef void* fcontext_t;

/**
* @biref 事件参数包装
*/
struct transfer_t {
fcontext_t fctx; /** 相关的的执行环境上下文-不同的API里含义不一样 **/
void * data; /** 自定义数据 **/
};

/**
* @biref 初始化执行环境上下文
* @param sp 栈空间地址
* @param size 栈空间的大小
* @param fn 入口函数
* @return 返回初始化完成后的执行环境上下文
*/
extern "C" BOOST_CONTEXT_DECL
fcontext_t BOOST_CONTEXT_CALLDECL make_fcontext( void * sp, std::size_t size, void (* fn)( transfer_t) );

/**
* @biref 跳转到目标上下文
* @param to 当前的上下文会保存到ofc中
* @param vp 跳转到的目标上下文的附加参数,会设置为transfer_t里的data成员
* @return 跳转来源
*/
extern "C" BOOST_CONTEXT_DECL
transfer_t BOOST_CONTEXT_CALLDECL jump_fcontext( fcontext_t const to, void * vp);

/**
* @biref 顶层跳转
* @param to 当前的上下文会保存到ofc中
* @param vp 跳转到的目标上下文的附加参数,会设置为transfer_t里的data成员
* @param fn 入口函数,参数是跳转来源
* @return 跳转来源
*/
// based on an idea of Giovanni Derreta
extern "C" BOOST_CONTEXT_DECL
transfer_t BOOST_CONTEXT_CALLDECL ontop_fcontext( fcontext_t const to, void * vp, transfer_t (* fn)( transfer_t) );

}}}

修改总结

命名空间从 boost 变成了 boost::detail

首先重要的是多一个 transfer_t,这个里面的有两个对象,第一个 fctx 是来源的执行上下文,第二个 data 是各种接口传入的自定义的指针 (上面接口里的 vp)。 来源的上下文指的是从什么位置跳转过来的。无论在回调参数还是各项返回值中都是这个含义。

对于 make_fcontext 这个接口,原先的入口函数是 void (* fn)( intptr_t),参数是透传自定义指针。现在是 void (* fn)( transfer_t),里面包含了来源执行栈的上下文和透传的自定义指针。

对于 jump_fcontext 这个接口,原先需要传入把当前执行上下文保存到哪里,跳转目标的新的上下文,自定义数据和是否复制 FPU。 现在的版本不再需要指定是否需要复制 FPU 了,同时也去除了自动保存当前上下文的功能,并且改成了跳到新的上下文后,新的上下文可以知道自己是从哪跳转过来的。

另外,这次的 boost.context 新增了一个比较有意思的接口,transfer_t ontop_fcontext (fcontext_t const to, void * vp, transfer_t ( fn)( transfer_t) )。 这个接口的功能是在跳转目标 (to 指向的上下文) 上模拟函数调用,并且返回值作为 jump_fcontext * 的返回值,相当于可以给执行上下文接口打 hook。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Step 1. 当前处于执行上下文-fctx1
transfer_t jump_res = jump_fcontext(fctx2, NULL);

// ...
// Step 2. 当前处于执行上下文-fctx2
// 跳入ontop_callback函数
ontop_fcontext(fctx1, 0x01, ontop_callback);

transfer_t ontop_callback( transfer_t from) {
// 这时候 from.fctx == fctx2, from.data == 0x01
// Step 3. 可以改变这些数据
from.data = 0x02;
return from;
}

// 这时候返回Step 1的
transfer_t jump_res = jump_fcontext(fctx2, NULL);
// Step 4. 这时候,jump_res.fctx == fctx2, from.data == 0x02
// continue other ...