1. python的generator是啥?
参见
https://wiki.python.org/moin/Generators
http://linuxgazette.net/100/pramode.html
就是一个能够当做iterator使用的function。例如如下常用的玩意
for i in range(10): print i
2. 这东西难不难实现?
如果不允许用户自定义generator函数,就range这种内置的东西而言,不难实现。把range定义成一个类的对象,通过内部状态维护当前的值,在定义诸如next的方法就ok了。但是如果允许用户定义这种函数就有点要命了。例如如下的代码:
def forfun(): yield "hello" yield "world"
def forfun1(n): i = 0 while i < 10 : if i < n: yield i else : yield i * 10 i = i + 1
这下就比较要命了,forfun和forfun1被编译成函数,可是函数是没状态的(static这种就不提了),每次执行不可能接着上次返回之后继续来。在pyston中,其实generator的实现类似与co-route.
3. ucontex这么个东西
参见:http://linux.die.net/man/3/swapcontext
通过getcontex这个函数,我们可以获得一个ucontex_t类型的实例,在通过makecontext函数修改这个实例,makecontext需要提供被调用的函数,参数列表。例如那个页面里面的一段代码:
uctx_func1.uc_stack.ss_sp = func1_stack; uctx_func1.uc_stack.ss_size = sizeof(func1_stack); uctx_func1.uc_link = &uctx_main; makecontext(&uctx_func1, func1, 0);
uctx_func1就是通过getconext获取的,首先我们需要设置它执行函数时使用的栈,栈的大小,uc_link应该就是调用它所服务的那个函数的context。
现在是如何在每个函数之间跳来跳去,还能保证回到原函数时,继续执行之前跳出的代码之后的代码。关键是swapcontext函数,它会把当前的context保存在第一个指针指向的ucontext_t,然后执行第二个指针所保存的文境。
4.generator怎么实现的呢
实现generator表达式或者函数都是BoxedGenerator类型的一个对象,这个类记录了用户定义的generator函数执行的stack,并且包含两个ucontext_t类型的变量context和returncontext。客户端在不断的调用它的next函数时,其实执行的流程如下:
next->generatorNext
-> generatorSend(注意,里面调用了swapcontext(&self->returnContext,&self->context);)->跳至用户定义的函数->用户定义代码中执行yield,调用实现函数yield
->yield(它干啥里,就是调用swapcontext(&self->context,&self->returnContext);)
->这时执行流程又回到了generatorSend,开始执行swapcontext下一条指令。
这里还漏了一点,也就是第一次调用next的时候,context的值从何而来,这个关键在BoxGenerator的够着函数中了,代码如下:
getcontext(&context); context.uc_link = 0; context.uc_stack.ss_sp = stack; context.uc_stack.ss_size = STACK_SIZE; makecontext(&context, (void (*)(void))generatorEntry, 1, this);
而在generatorEntry中就会调用用户定义的代码。然后这个环就链上了,用户定义的代码里面有yield,yield又会回到generatorSend,哈,拿到了需要的结果。
实现文件:https://github.com/dropbox/pyston/blob/28c651ab0918b318ce99852f0ea8edd84527c74a/src/runtime/generator.cpp
pyston的generator实现