>

自由软件精神——“自由、开放、分享”。自由软件自诞生之日起,就秉承了学术自由的思想,信奉科学无国界,知识应该全人类共享。

ubuntu精神——人道待人,天下共享连接人人的信念。具有 ubuntu 精神的人心胸开阔,乐于助人,见贤思齐而不忌妒贤能......

tornado coroutine源码分析

 

def coroutine(func, replace_callback=True):
    """Decorator for asynchronous generators.
    """
    return _make_coroutine_wrapper(func, replace_callback=True)


def _make_coroutine_wrapper(func, replace_callback):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        future = TracebackFuture()

        # 如果传入callback,func不传入,而加入到future的callback
        if replace_callback and 'callback' in kwargs:
            callback = kwargs.pop('callback')
            IOLoop.current().add_future(
                future, lambda future: callback(future.result()))

        try:
            # 执行func,如果func是个generator,即里面有yield,那么result就是一个generator
            result = func(*args, **kwargs)
        # Return指gen.Return返回的结果,这里StopIteration有点搞不懂
        except (Return, StopIteration) as e:
            result = getattr(e, 'value', None)
        except Exception:
            future.set_exc_info(sys.exc_info())
            return future
        else:
            # 确定result是个generator,如果不是加个coroutine也没关系,后面会处理
            if isinstance(result, types.GeneratorType):
                # Inline the first iteration of Runner.run.  This lets us
                # avoid the cost of creating a Runner when the coroutine
                # never actually yields, which in turn allows us to
                # use "optional" coroutines in critical path code without
                # performance penalty for the synchronous case.
                try:
                    # 一大堆stack_contexts的处理可以无视
                    orig_stack_contexts = stack_context._state.contexts
                    # 执行到第一个yield
                    yielded = next(result)
                    if stack_context._state.contexts is not orig_stack_contexts:
                        yielded = TracebackFuture()
                        yielded.set_exception(
                            stack_context.StackContextInconsistentError(
                                'stack_context inconsistency (probably caused '
                                'by yield within a "with StackContext" block)'))
                # 这里我也有点搞不懂
                except (StopIteration, Return) as e:
                    future.set_result(getattr(e, 'value', None))
                except Exception:
                    future.set_exc_info(sys.exc_info())
                else:
                    # 交由Runner处理,并把结果填充future
                    Runner(result, future, yielded)
                try:
                    return future
                finally:
                    # Subtle memory optimization: if next() raised an exception,
                    # the future's exc_info contains a traceback which
                    # includes this stack frame.  This creates a cycle,
                    # which will be collected at the next full GC but has
                    # been shown to greatly increase memory usage of
                    # benchmarks (relative to the refcount-based scheme
                    # used in the absence of cycles).  We can avoid the
                    # cycle by clearing the local variable after we return it.
                    future = None
        # 不是generator的情况也会在此处理,作为futurn返回
        future.set_result(result)
        return future
    return wrapper

接下来看Runner

class Runner(object):
    """Internal implementation of `tornado.gen.engine`.

    Maintains information about pending callbacks and their results.

    The results of the generator are stored in ``result_future`` (a
    `.TracebackFuture`)
    """
    def __init__(self, gen, result_future, first_yielded):
        self.gen = gen
        self.result_future = result_future
        self.future = _null_future
        self.yield_point = None
        self.pending_callbacks = None
        self.results = None
        self.running = False
        self.finished = False
        self.had_exception = False
        self.io_loop = IOLoop.current()
        self.stack_context_deactivate = None
        # handle_yield会处理单独的、dict或list的yield,并把第一个yield后的函数的结果(如 a = yield b(c, d)中的b(c, d)结果)写到self.fulture,里面也涉及到context很乱,来不及整理
        if self.handle_yield(first_yielded):
            # 一般会直接run()
            self.run()

    # 其他函数略
    def run(self):
        """Starts or resumes the generator, running until it reaches a
        yield point that is not ready.
        """
        if self.running or self.finished:
            return
        try:
            self.running = True
            # 这也是个循环!
            while True:
                future = self.future
                if not future.done():
                    return
                self.future = None
                try:
                    orig_stack_contexts = stack_context._state.contexts
                    try:
                        # 获取第一个yield后的函数结果
                        value = future.result()
                    except Exception:
                        self.had_exception = True
                        yielded = self.gen.throw(*sys.exc_info())
                    else:
                       # 将结果返回给 a = yield b(c, d)中的a,使generator继续执行,直至下一个yield或结束
                        yielded = self.gen.send(value)
                    if stack_context._state.contexts is not orig_stack_contexts:
                        self.gen.throw(
                            stack_context.StackContextInconsistentError(
                                'stack_context inconsistency (probably caused '
                                'by yield within a "with StackContext" block)'))
                except (StopIteration, Return) as e:
                # 到末尾或gen.Return了
                    self.finished = True
                    self.future = _null_future
                    if self.pending_callbacks and not self.had_exception:
                        # If we ran cleanly without waiting on all callbacks
                        # raise an error (really more of a warning).  If we
                        # had an exception then some callbacks may have been
                        # orphaned, so skip the check in that case.
                        raise LeakedCallbackError(
                            "finished without waiting for callbacks %r" %
                            self.pending_callbacks)
                    self.result_future.set_result(getattr(e, 'value', None))
                    self.result_future = None
                    self._deactivate_stack_context()
                    return
                except Exception:
                    self.finished = True
                    self.future = _null_future
                    self.result_future.set_exc_info(sys.exc_info())
                    self.result_future = None
                    self._deactivate_stack_context()
                    return
                # 如果还有yield,继续handle_yield,进入下一个循环
                if not self.handle_yield(yielded):
                    return
        finally:
            self.running = False

可能大家会纳闷,为什么有while True循环,怎么还是异步的。其实,到达yield后,由于python所提供的generator特性,代码段会被撇在一边(greenlet也是类似的思路,切换更直观)。在处理yield后面函数时,函数是否结束的检查是交由ioloop的,而函数的执行,是交由iostream等待的,而iostream又是以epoll这类的异步接口为基础的ioloop为基本。在asynclient和motor源码中,都会看到iostream.connect()的身影,结果返回充实到iostream中后,再交继续send给generator。过程太乱,恕我暂时理不清楚。留待以后吧。

 

Meta

发布: 三月 2, 2015

作者: spark 李先斌

评论:  

字数统计: 1095

上一篇: Google Python风格指南

Bookmark and Share

Tags

code python tornado

comments powered by Disqus

返回顶部