首页 > 代码库 > pyston的generator实现

pyston的generator实现

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