stack switching for fun and profit

Post on 19-May-2015

530 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slides from the talk I gave at FOSDEM 2014 on how Greenlet works and how I built python-fibers.

TRANSCRIPT

Stack switchingfor fun & profitSaúl Ibarra Corretgé - @saghul

FOSDEM 2014

Hi!

@saghul

FOSDEM

Open Source

import open_source

github.com/saghul

Some background

Who knows what greenlet is?

Who has used it (standalone)?

Who understands how it works?

Greenlet

Micro-threads with no implicit scheduling

Lightweight

Only one can run at a time

Spin-off Stackless

Greenlet APIgreenlet(func, parent=None): creates a greenlet to run ‘func’

greenlet.switch(*args, **kw): switches execution to the target greenlet, the first timefunc(*args, **kw) will be executed

greenlet.throw([typ, [val, [tb]]]): switches execution to the target greenlet and raises the specified exception (GreenletExit by default)

Exampleimport greenlet

main = greenlet.getcurrent()

def foo(n): main.switch(n)

def bar(n): foo(n) return 'hello'

g1 = greenlet.greenlet(bar)print g1.switch(42) # 42print g1.switch() # 'hello'print g1.dead # True

How does it work?‘Stack switching’

Non portable asm code

Copy stack slices on the heap

State is saved and restored when switching

CPU registers

Current Python frame, recursion depth and exception state

How does it work? (II)

Organized in a tree structure

Each greenlet has a ‘parent’, except main

Execution order isn’t always obvious

Example (II)import greenlet

main = greenlet.getcurrent()

def foo_async(cb): # Will call cb(result, error) eventually pass

def foo_sync(): current = greenlet.getcurrent() def cb(result, error): if error is not None: current.throw(Exception(error)) else: current.switch(result) foo_async(cb) main.switch()

Stackless Python

Fork of CPython, first release in 1999

Provides tasklets and channels

Builtin scheduler

Different approaches to switching

Stackless Python (II)Different ways of switching: soft and hard

Soft switching

“Move some pointers around”

Hard switching

Platform dependent assembly code

When soft-switching is not possible

Enter PyPy

New shiny and fast implementation of Python

Vast amount of fairy-dust covered unicorns included

Includes implementations of both greenlet and Stackless

Implemented on top of “continulet” objects

import _continuation

Continulets are one-shot continuations

Switching code is a standalone C library: stacklet

rpython/translator/c/src/stacklet/

Continulet APIcontinulet(func, *args, **kw): create a continulet object which will call fun(cont, *args, **kw)

continulet.switch(value=None, to=None): start the continulet or activate the previously suspended one. If to is specified a ‘double switch’ is performed

continulet.throw(type, value=None, tb=None, to=None): similar to switch, but raise the given exception after the switch is done

Stacklet

Tiny library implementing one-shot continuations for C

Single C file (~400 lines) + per-platform asm

Supports x86, x86_64 and ARM

Nice and simple API

Stacklet API

stacklet_newthread(): creates a new thread handle

stacklet_new(thread_handle, run_func, run_arg): calls run(arg) in a new stacklet, starts immediately

stacklet_switch(target): switches execution to target stacklet

Example#include <assert.h>#include "stacklet.h"

static stacklet_thread_handle thrd;

stacklet_handle empty_callback(stacklet_handle h, void *arg){ assert(arg == (void *)123); return h;}

void test_new(void){ stacklet_handle h = stacklet_new(thrd, empty_callback, (void *)123); assert(h == EMPTY_STACKLET_HANDLE);}

int main(int argc, char **argv){ thrd = stacklet_newthread(); test_new(); stacklet_deletethread(thrd); return 0;}

import fibersMicro-threadling library API inspired by Python threads and greenlet

Uses stacklet underneath

Works on CPython and PyPy

On PyPy it uses continulets

github.com/saghul/python-fibers

Or pip install fibers

fibers APIFiber(target=None, args=(), kwargs={}, parent=None): creates a new fiber which will run “target(*args, **kwargs)”

Fiber.switch(value=None): switch execution to the target fiber, value can only be passed after the fiber is running

Fiber.throw(typ, [value, [tb]]): switch execution to the target fiber and raise the specified exception

Motivation

Couldn’t build the API I wanted on top of greenlet

Early binding

More exceptions in expected places

No magic exceptions (GreenletExit)

No switching on GC

Exampleimport fibers

main = fibers.current()

def foo(n): main.switch(n)

def bar(n): foo(n) return 'hello'

g1 = fibers.Fiber(target=bar, args=(42,))print g1.switch() # 42print g1.switch() # 'hello'print g1.is_alive() # False

Projects using fibers

github.com/saghul/evergreen

github.com/geertj/gruvi

github.com/benoitc/offset

Should I add yours here?

The Stacklet Sandwich (TM)

stacklet

continulet

greenlet / stackless / fibers

Questions?

bettercallsaghul.com@saghul

top related