【Python 协程系列】greenlet 源码分析

参考资料

前言

C在过程调用时,使用栈帧来传递参数、存储返回信息、保存寄存器,以及局部存储。
Python 在执行的时,以 PyFrameObject 作为环境,不断加载 PyCodeObject 进行执行。
greenlet 就是利用 C 栈和 PyFrameObject,这两个特性,实现了协程。

C栈帧结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from greenlet import greenlet

def test1():
print(12)
gr2.switch()
print(56)

def test2():
print(34)
gr1.switch()
print("Never")

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

greenlet 实现了在一个系统线程中伪并发的运行多个任务流程。
之所以是伪并发,是因为同时运行的始终只有一个,不同的任务可以不断的切换。
换句换说,greenlet 实现的是一种 micro-threads/coroutines,并且 greenlet 本身并不提供调度功能。

通过 gr.switch() 主动切换到不同协程,需要注意的是仅支持同线程内切换。协程要切换,必然也要组织成链式结构。greenlet 给每个协程都赋予了 parent 参数,执行完成后,自动返回到 parent 继续执行。

Python 用法

Instantiation:

  • greenlet(run=None, parent=None) run is callable
  • greenlet.getcurrent()
  • greenlet.GreenletExit 不传递给 parent,可以用来 kill greenlet
  • 可以通过继承 greenlet ,调用 run 方法。

Switching:

  • x = gr.switch(y) will send the object y to g, 并且将从 任意的协程 接收参数给 x
  • 注意,切换进入的是在,曾经切出的位置
  • 如果没有启动,将从头开始执行

Attributes:

  • g.parent This is writeable, 但不能 循环
  • g.gr_frame The current top frame, or None.
  • g.dead True if g is dead (i.e. it finished its execution).
  • bool(g) True if g is active, False if it is dead or not yet started.
  • g.throw([typ, [val, [tb]]])
    • Switches execution to the greenlet g,defaults greenlet.GreenletExit

PyGreenlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
              |     ^^^       |
| older data |
高地址处 | |
stack_stop . |_______________|
. | |
. | greenlet data |
. | in stack |
. * |_______________| . . _____________ stack_copy + stack_saved
. | | | |
. | data | |greenlet data|
. | unrelated | | saved |
. | to | | in heap |
stack_start . | this | . . |_____________| stack_copy
低地址处 | greenlet |
| |
| newer data |
| vvv |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _greenlet {
PyObject_HEAD
char* stack_start; // 栈顶
char* stack_stop; // 栈底
char* stack_copy; // heap 数据地址
intptr_t stack_saved; // heap 数据大小
struct _greenlet* stack_prev; // gr 链, The main (initial) gr 是最后一个
struct _greenlet* parent; // parent 指针
PyObject* run_info; // 运行信息,PyThreadState_GET()->dict
struct _frame* top_frame;
int recursion_depth; // 栈深度
PyObject* weakreflist;
PyObject* exc_type;
PyObject* exc_value;
PyObject* exc_traceback;
PyObject* dict;
} PyGreenlet;

Data:

  • 部分在堆、部分在栈、都可能为空
  • stack_stop == NULL && stack_start == NULL: did not start yet
  • stack_stop != NULL && stack_start == NULL: already finished
  • stack_stop != NULL && stack_start != NULL: active

初始对象:

  • 栈底,高地址:gmain->stack_stop = (char *) -1
  • 栈顶,低地址:gmain->stack_start = (char *) 1

注意要点:

  1. greenlet 将每个 gr 都保存在堆上,调用链上的 gr 各自都有栈帧。
  2. switch 涉及到 C栈 的备份恢复,以及 Python 环境的备份恢复。
  3. 执行 swtch 时,将 %rbp %rbx %rsp 等寄存器以及当前 gr 的栈帧数据,保存到堆上。
  4. 将 ts_target 的堆数据恢复到栈帧,并且恢复寄存器的值,让程序切出处执行。
  5. Python 环境通过实现备份恢复 PyThreadState_GET() 实现。

其他:

  • gr 的 return 以及报错,都是上抛给 parent
  • gr 的 parent 默认是 ts_current,可以更改
  • gr 的 switch 是通过全局变量 ts_current, ts_target 等实现
  • gr 的 switch 无法发生在不同线程之间

PyMODINIT_FUNC

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
static struct PyModuleDef greenlet_module_def = {
PyModuleDef_HEAD_INIT,
"greenlet",
NULL,
-1,
GreenMethods, // 定义了 getcurrent、settrace、gettrace
};

PyMODINIT_FUNC
PyInit_greenlet(void)
{
PyObject* m = NULL;
char** p = NULL;
PyObject *c_api_object;
static void *_PyGreenlet_API[PyGreenlet_API_pointers]; // [8]

GREENLET_NOINLINE_INIT(); // 宏:阻止编译器对部分函数 inline 优化
// 创建 module
m = PyModule_Create(&greenlet_module_def);
PyType_Ready(&PyGreenlet_Type)

// 创建共享字符串,并赋值全局变量全局变量 static PyObject*
ts_curkey = PyUnicode_InternFromString("__greenlet_ts_curkey");
ts_delkey = PyUnicode_InternFromString("__greenlet_ts_delkey");
ts_tracekey = PyUnicode_InternFromString("__greenlet_ts_tracekey");
ts_event_switch = PyUnicode_InternFromString("switch");
ts_event_throw = PyUnicode_InternFromString("throw");

PyType_Ready(&PyGreenlet_Type)
PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL);
PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit",
PyExc_BaseException, NULL);
// 创建 (),{} 并赋值全局变量
ts_empty_tuple = PyTuple_New(0);
ts_empty_dict = PyDict_New();

// 只要导入模块,就创建了 main 协程
// static PyGreenlet* volatile ts_current = NULL
ts_current = green_create_main();

// 添加 module 属性
PyModule_AddObject(m, "greenlet", (PyObject*) &PyGreenlet_Type);
PyModule_AddObject(m, "error", PyExc_GreenletError);
PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit);
PyModule_AddObject(m, "GREENLET_USE_GC", PyBool_FromLong(GREENLET_USE_GC));
PyModule_AddObject(m, "GREENLET_USE_TRACING",
PyBool_FromLong(GREENLET_USE_TRACING));

/* 同时添加到 PyGreenlet_Type.__dict__ */
for (p=copy_on_greentype; *p; p++) {
PyObject* o = PyObject_GetAttrString(m, *p);
if (!o) continue;
PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o);
Py_DECREF(o);
}

/* Expose C API */
_PyGreenlet_API[PyGreenlet_Type_NUM] = (void *) &PyGreenlet_Type;
_PyGreenlet_API[PyExc_GreenletError_NUM] = (void *) PyExc_GreenletError;
_PyGreenlet_API[PyExc_GreenletExit_NUM] = (void *) PyExc_GreenletExit;
_PyGreenlet_API[PyGreenlet_New_NUM] = (void *) PyGreenlet_New;
_PyGreenlet_API[PyGreenlet_GetCurrent_NUM] =
(void *) PyGreenlet_GetCurrent;
_PyGreenlet_API[PyGreenlet_Throw_NUM] = (void *) PyGreenlet_Throw;
_PyGreenlet_API[PyGreenlet_Switch_NUM] = (void *) PyGreenlet_Switch;
_PyGreenlet_API[PyGreenlet_SetParent_NUM] =
(void *) PyGreenlet_SetParent;

c_api_object = PyCapsule_New((void *) _PyGreenlet_API, "greenlet._C_API", NULL);
PyModule_AddObject(m, "_C_API", c_api_object);
return m;
}

如上,module init,主要是创建属性、暴露API。
从中能看出,greenlet 大量利用 static 全局变量,保存当前状态。
其中很重要的一点是对 ts_current的赋值 green_create_main()

green_create_main

1
2
3
4
5
6
7
8
9
10
11
12
13
static PyGreenlet* green_create_main(void)
{
PyGreenlet* gmain;
PyObject* dict = PyThreadState_GetDict();

/* create the main greenlet for this thread */
gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreenlet_Type, 0);
gmain->stack_start = (char*) 1; // 栈顶为 1
gmain->stack_stop = (char*) -1; // 栈底为 -1,即最大地址处
gmain->run_info = dict; // 当前线程状态信息
Py_INCREF(dict);
return gmain;
}

如上,初始创建 gr 对象 gmain,并且对栈地址、run_info 进行初始化。

实例化

green_new

1
2
3
4
5
6
7
8
9
10
11
12
13
PyTypeObject PyGreenlet_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"greenlet.greenlet", /* tp_name */
(destructor)green_dealloc, /* tp_dealloc */
&green_as_number, /* tp_as _number*/
green_methods, /* tp_methods */
(initproc)green_init, /* tp_init */
GREENLET_tp_alloc, /* tp_alloc */
green_new, /* tp_new */
GREENLET_tp_free, /* tp_free */
(inquiry)GREENLET_tp_is_gc, /* tp_is_gc */
...
}

初始化完成后greenlet.greenlet对应 PyGreenlet_Type。
当执行greenlet(test1),就是在调用green_new方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define STATE_OK    (ts_current->run_info == PyThreadState_GET()->dict \
|| !green_updatecurrent())

static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject* o = PyBaseObject_Type.tp_new(type, ts_empty_tuple, ts_empty_dict);
if (o != NULL) {
if (!STATE_OK) { // 确保同一线程
Py_DECREF(o);
return NULL;
}
Py_INCREF(ts_current);
((PyGreenlet*) o)->parent = ts_current;
}
return o;
}

创建对象,将 gr->parent 指向当前 ts_current。
可见,parent 默认是与 gr 的创建位置有关,与启动位置无关。
接着自然是执行 tp_init。

green_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs)
{
PyObject *run = NULL;
PyObject* nparent = NULL;
static char *kwlist[] = {"run", "parent", 0};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:green", kwlist,
&run, &nparent))
return -1;

if (run != NULL) {
// self->run_info = nrun;
if (green_setrun(self, run, NULL))
return -1;
}
if (nparent != NULL && nparent != Py_None)
// assert nparent != NULL && PyGreenlet_Check(nparent)
// 不能产生循环链(self != nparent->parent->... )
// assert self->run_info == run_info
// 最终 self->parent = nparent
return green_setparent(self, nparent, NULL);
return 0;
}

main greenlet 的 run_info 初始化为 线程状态信息。
而实例化后的 gr,run_info 被赋值为 callable 对象。
至此,生成的 gr,依然保持未启动状态,stack_stop==stack_start==NULL。

切换

1
2
3
4
5
6
7
8
9
10
11
12
def test1():
print(12)
gr2.switch()
print(56)

def test2():
print(34)
gr1.switch()
print("Never")

gr1 = greenlet(test1)
gr2 = greenlet(test2)

上面的 Python 代码,切换链可以简化为:

1
main -> gr1 -> gr2 -> gr1  ---return----> to main

根据源码可以看出,在 Python 中调用gr.switch(),将执行g_switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 	green_methods,                          /* tp_methods */
static PyMethodDef green_methods[] = {
{"switch", (PyCFunction)green_switch,
...
}

static PyObject*
green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
{
Py_INCREF(args);
Py_XINCREF(kwargs);
// result = item if result ==(item,) else result
return single_result(g_switch(self, args, kwargs));
}

g_switch

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static PyObject *
g_switch(PyGreenlet* target, PyObject* args, PyObject* kwargs)
{
int err = 0;
PyObject* run_info;

// 1.1 确保 ts_current->run_info 为线程状态信息
if (!STATE_OK) {
Py_XDECREF(args);
Py_XDECREF(kwargs);
return NULL;
}
// 1.2.1 遍历 g->parent chain,直到找到 STARTED(g) 的 run_info
run_info = green_statedict(target);
// 1.2.2 确保 paretn->run_info 跟当前线程一样
if (run_info == NULL || run_info != ts_current->run_info) {
Py_XDECREF(args);
Py_XDECREF(kwargs);
PyErr_SetString(PyExc_GreenletError, run_info
? "cannot switch to a different thread"
: "cannot switch to a garbage collected greenlet");
return NULL;
}
// 2. 改变全局变量,传递参数
ts_passaround_args = args;
ts_passaround_kwargs = kwargs;

// 3. 执行切换
while (target) {
// gr 已经 启动
// gr2 -> gr1 会进入到这部分
if (PyGreenlet_ACTIVE(target)) {
ts_target = target;
err = g_switchstack();
break;
}
// 启动 gr
// main-> gr1 -> gr2 都将进入到这部分
if (!PyGreenlet_STARTED(target)) {
void* dummymarker;
ts_target = target;
err = g_initialstub(&dummymarker);
if (err == 1) {
continue; /* retry the switch */
}
break;
}
target = target->parent;
}

// 4. 将切换回来的 全局变量 保存起来
args = ts_passaround_args;
ts_passaround_args = NULL;
kwargs = ts_passaround_kwargs;
ts_passaround_kwargs = NULL;

PyGreenlet *origin;
origin = ts_origin;
ts_origin = NULL;
Py_DECREF(origin);

// 5. 处理返回值
/*
if kwargs is None or kwargs is {}:
return args
elif args is ():
return kwargs
else:
return (args, kwargs)
*/
}

如上:

  • 首先,确保 ts_current/ts_target 线程状态 run_info 跟当前线程状态一致
  • 其次,改变全局变量,传递参数
  • 然后,g_switchstack 到已经启动的 gr,或者 g_initialstub 初始化 gr
  • 最后,从全局变量,获取 switch() 得到的返回值,并处理后返回到调用位置

main->g1->g2切换对象都是未启动的,必然是走第二条分支g_initialstub。若返回值为为0,会进入return流程。

g2->g1,切换对象 g1 已经启动过,那么就直接进入g_switchstack分支。不管返回值如何,都将进入到return流程。

g_initialstub

1
2
3
4
5
6
7
8
9
if (!PyGreenlet_STARTED(target)) {
void* dummymarker; // 注意这个东西,一个临时变量指针
ts_target = target; // 同时注意这个 全局变量
err = g_initialstub(&dummymarker);
if (err == 1) {
continue; /* retry the switch */
}
break;
}

运行中申请变量,dummymarker其实就是当前的 C栈 指针位置!

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static int GREENLET_NOINLINE(g_initialstub)(void* mark)
{
int err;
PyGreenlet* self = ts_target; // 注意,self 其实是要初始化的 gr
PyObject* args = ts_passaround_args; // 全局变量,switch() 时,传递的参数
PyObject* kwargs = ts_passaround_kwargs;

/* save exception in case getattr clears it */
/* self.run is the object to call in the new greenlet */
/* restore saved exception */
/* recheck the state in case getattr caused thread switches */
/* recheck run_info in case greenlet reparented anywhere above */

/* start the greenlet */
self->stack_start = NULL; // 栈顶为 NULL,暂时 finished
self->stack_stop = (char*) mark; // 栈底为 &dummymarker,表明 actice

// 构建 gr 链
if (ts_current->stack_start == NULL) {
/* ts_current is dying */
self->stack_prev = ts_current->stack_prev;
}
else {
self->stack_prev = ts_current;
}

self->top_frame = NULL;
green_clear_exc(self);
self->recursion_depth = PyThreadState_GET()->recursion_depth;

/* restore arguments in case they are clobbered */
ts_target = self;
ts_passaround_args = args;
ts_passaround_kwargs = kwargs;

/* perform the initial switch
还记得 g_switch()中,遇到已经启动的 gr,会直接调用 g_switchstack
那么,此处同样调用,就意味着,gr 已经初始化完毕
注意,当前的全局变量参数
*/
err = g_switchstack(); // 切换栈,等待返回

/* returns twice!
The 1st time with err=1: we are in the new greenlet
The 2nd time with err=0: back in the caller's greenlet
*/
if (err == 1) {
/* in the new greenlet */
self->stack_start = (char*) 1; /* running */

/* now use run_info to store the statedict */
self->run_info = green_statedict(self->parent);

/* call g.run(*args, **kwargs) */
run = PyObject_GetAttrString((PyObject*) self, "run");
result = PyEval_CallObjectWithKeywords(
run, args, kwargs);
result = g_handle_exit(result);

/* jump back to parent */
self->stack_start = NULL; /* dead */
for (parent = self->parent; parent != NULL; parent = parent->parent) {
// 注意,这里 switch 目标是 parent
// 并且把返回值作为了参数 args
result = g_switch(parent, result, NULL);
assert(result == NULL);
}
}
if (err < 0) { ... } // 切换出错
return err;
}

代码很长,绝大部分都在进行容错处理

  • run 不能为 NULL
  • 确保同一线程
  • 若已经被启动,则返回

真正执行部分,对 self 进行赋值,然后再次恢复全局变量,调用g_switchstack(),切换栈。
注释中也提到,g_switchstack()执行完成后,会返回两次。
一次是在 new gr 中,一次是从任意 gr 返回到 caller 中。

err=1,进入到 new gr 的代码执行阶段,调用PyEval_CallObjectWithKeywords开始执行用户指定的 python callable。
注意,执行完成后,跳转到的是 parent,而并非自身的 caller。

g_switchstack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Perform a stack switch according to some global variables
that must be set before:
- ts_current: current greenlet (holds a reference)
- ts_target: greenlet to switch to (weak reference)
- ts_passaround_args: NULL if PyErr_Occurred(),
else a tuple of args sent to ts_target (holds a reference)
- ts_passaround_kwargs: switch kwargs (holds a reference)
On return results are passed via global variables as well:
- ts_origin: originating greenlet (holds a reference)
- ts_current: current greenlet (holds a reference)
- ts_passaround_args: NULL if PyErr_Occurred(),
else a tuple of args sent to ts_current (holds a reference)
- ts_passaround_kwargs: switch kwargs (holds a reference)
It is very important that stack switch is 'atomic', i.e. no
calls into other Python code allowed (except very few that
are safe), because global variables are very fragile.
*/

先来看段注释,显然,栈切换是通过全局变量 ts_* 实现。那么,执行代码前的全局变量是什么样子的?
执行main -> gr1,会从 g_switch -> g_initialstub -> g_switchstack

在 Module init 中:

  • ts_current = main

在 g_initialstub 中:

  • ts_target = self = ts_target = gr.switch() 的 gr
  • ts_passaround_args = args = g_switch 参数
  • ts_passaround_kwargs = kwargs = g_switch 参数

同时还有一个神奇变量 dummymarker

  • g_switch中创建,void* dummymarker
  • 作为参数 mark 传入,g_initialstub(&dummymarker)
  • g_initialstub中,赋值给栈底,self->stack_stop = (char*) mark
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
static int g_switchstack(void){
int err;
{ /* 保存 当前信息 */
PyGreenlet* current = ts_current;
PyThreadState* tstate = PyThreadState_GET();
current->recursion_depth = tstate->recursion_depth;
current->top_frame = tstate->frame;
current->exc_type = tstate->exc_type;
current->exc_value = tstate->exc_value;
current->exc_traceback = tstate->exc_traceback;
}
err = slp_switch();
// 执行切换
// ts_target 没有 active: err=1
// ts_target 已经 active:从 heap 恢复数据,恢复寄存器,err=0
if (err < 0) {
... // 错误处理
} else {
PyGreenlet* target = ts_target;
PyGreenlet* origin = ts_current;
PyThreadState* tstate = PyThreadState_GET();
tstate->recursion_depth = target->recursion_depth;
tstate->frame = target->top_frame;
target->top_frame = NULL;

tstate->exc_type = target->exc_type;
tstate->exc_value = target->exc_value;
tstate->exc_traceback = target->exc_traceback;

green_clear_exc(target);

assert(ts_origin == NULL);
Py_INCREF(target);
ts_current = target;
ts_origin = origin;
ts_target = NULL;
}
return err;
}

g_switchstack中,首先将当前线程的的状态信息,frame/exc 保存到 main 里。
然后调用slp_switch(),这个函数,是一个区分平台的函数,利用内嵌汇编完成真正的切换。

如果调用链是some_gr.switch() -> g_switch -> g_initialstub -> g_switchstack -> slp_switch

  • 新 gr 返回值err=1
  • g_initialstub中匹配到if(err==1): EvalCode...
  • 直接就开始执行target.run()
  • 执行完成后,切换到target.parent,而并非回到some_gr

slp_switch

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
49
50
51
// 我们只看`gcc on X86`。
// 使用内联汇编代码,
static int slp_switch(void) {
int err;
void *ebp, *ebx;
unsigned short cw;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : "esi", "edi");
__asm__ volatile ("fstcw %0" : "=m" (cw));
// 被调用者保存寄存器 ebx/ebp
// 将寄存器 %ebp 的值保存到内存位置 ebp 处
__asm__ volatile ("movl %%ebp, %0" : "=m" (ebp));
__asm__ volatile ("movl %%ebx, %0" : "=m" (ebx));
// 将 栈指针 保存到寄存器 stackref 处
__asm__ ("movl %%esp, %0" : "=g" (stackref));
{
// 调整栈之间的偏移
stackref += STACK_MAGIC;

// 将 target gr 之前的所有栈都保存到 heap 中
// 常返回0,错误返回 -1
if (slp_save_state((char*)stackref))
return -1;
// 注意这句!!如果 ts_target 没有 active,return 1
if (!PyGreenlet_ACTIVE(ts_target))
return 1; // 第一次切换,就直接 返回 1

/* -------- 如果 ts_target 已经启动过,才执行下面的语句 ----------- */

// 获取 ts_target 切换之前的 栈指针
stsizediff = ts_target->stack_start - (char*)stackref
// 修改寄存器,将指针指向 ts_target 当时的位置
__asm__ volatile (
"addl %0, %%esp\n"
"addl %0, %%ebp\n"
:
: "r" (stsizediff)
);
/* -------- 现在 已经是在 ts_target 中执行 ----------- */
// 从 heap 恢复数据到栈上
slp_restore_state()
// 异或清零 err=0
__asm__ volatile ("xorl %%eax, %%eax" : "=a" (err));
}
// 恢复之前保存的寄存器
__asm__ volatile ("movl %0, %%ebx" : : "m" (ebx));
__asm__ volatile ("movl %0, %%ebp" : : "m" (ebp));
__asm__ volatile ("fldcw %0" : : "m" (cw));
__asm__ volatile ("" : : : "esi", "edi");
return err;
}

slp_save_state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 将 target gr 之前的所有栈都保存到 heap 中
static int GREENLET_NOINLINE(slp_save_state)(char* stackref)
{
/* must free all the C stack up to target_stop */
char* target_stop = ts_target->stack_stop; // 获取到 目标栈底
PyGreenlet* owner = ts_current; // 当前 gr
assert(owner->stack_saved == 0); // 当前 gr 并未保存数据
if (owner->stack_start == NULL) // already finished, main=1
owner = owner->stack_prev; /* not saved if dying */
else
owner->stack_start = stackref; // 更新当前 gr 最终栈指针

// 循环,将目标栈下方的,所有 gr 全保存到 heap 中
while (owner->stack_stop < target_stop) {
if (g_save(owner, owner->stack_stop))
return -1; /* XXX */
owner = owner->stack_prev;
}
if (owner != ts_target) {
if (g_save(owner, target_stop))
return -1; /* XXX */
}
return 0;
}

g_save

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
// 真正完成将栈中数据保存到 heap
static int g_save(PyGreenlet* g, char* stop) {
/* Save more of g's stack into the heap -- at least up to 'stop'

高 g->stack_stop |________|
| |
| __ stop . . . . .
| | ==> . .
|________| _______
| | | |
| | | |
低 g->stack_start | | |_______| g->stack_copy
*/
intptr_t sz1 = g->stack_saved; // 已经在 heap 上的大小
intptr_t sz2 = stop - g->stack_start; // 实际栈大小
assert(g->stack_start != NULL); // g 并未结束
if (sz2 > sz1) { // 栈地址 增大了
// 栈增大,需要扩充空间,从 stack_copy -> sz2
char* c = (char*)PyMem_Realloc(g->stack_copy, sz2);

memcpy(c+sz1, g->stack_start+sz1, sz2-sz1);
g->stack_copy = c; // 更新 heap 地址
g->stack_saved = sz2; // 更新 heap 大小
}
return 0;
}

slp_restore_state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 剪切 heap 数据到 C 栈,找到还在栈中的 prev gr
static void GREENLET_NOINLINE(slp_restore_state)(void) {
PyGreenlet* g = ts_target;
PyGreenlet* owner = ts_current;

/* Restore the heap copy back into the C stack */
if (g->stack_saved != 0) {
memcpy(g->stack_start, g->stack_copy, g->stack_saved);
PyMem_Free(g->stack_copy);
g->stack_copy = NULL;
g->stack_saved = 0;
}
if (owner->stack_start == NULL)
owner = owner->stack_prev; /* greenlet is dying, skip it */
while (owner && owner->stack_stop <= g->stack_stop)
// 比 stack_stop 小的 都已经被保存到 heap 上了
// 只有找到在目标上方的,作为 prev
owner = owner->stack_prev;
g->stack_prev = owner;
}

小结

1
main -> gr1 -> gr2 -> gr1  ---return----> to main
  1. 切换是调用 g_switch
  2. g_switch中有个 while 循环,会根据目标的走不同的分支

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    while (target) {
    if (PyGreenlet_ACTIVE(target)) {
    err = g_switchstack();
    break;
    }
    if (!PyGreenlet_STARTED(target)) {
    err = g_initialstub(&dummymarker);
    if (err == 1) {
    continue;
    }
    break;
    }
    target = target->parent;
    }
  3. main -> gr1 -> gr2,目标都是未启动的,都走g_initialstub分支

  4. g_initialstub分支中,如果是未启动的,会直接开始执行 Python 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    err = g_switchstack();  // 切换栈,等待返回

    if (err == 1) {
    result = PyEval_CallObjectWithKeywords(
    run, args, kwargs);

    g_switch(parent, result, NULL);
    }
    return err;
  5. 所以main -> gr1 -> gr2会在PyEval中一直嵌套,直到 gr2 中执行到gr1.swtich()

  6. 注意,此时 gr1 的数据保存在 heap 上,C调用链处于g_switch -> g_initialstub-> g_switchstack -> slp_switch状态
  7. 此时,gr1 已经 Active。产生调用链gr2.run() -> g_switch(gr1) -> g_switchstack -> slp_switch
  8. 在 slp_switch 中,因为 gr1.Active,会开始恢复 gr1 的数据,恢复到 gr1 的执行序列中

    1
    2
    3
    4
    5
    if (!PyGreenlet_ACTIVE(ts_target))
    return 1; # 第一次切换,就直接 返回 1
    slp_restore_state() # 恢复 栈
    __asm__ volatile ... # 恢复 寄存器
    retrun 0;
  9. 根据第 6 步,会产生返回链slp_switch.return 0 -> g_switchstack -> g_initialstub -> g_switch

  10. g_switch中因为 err=0,break 循环,处理结果并返回,完成整个流程