Stack switching for fun and profit


Published on

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

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Stack switching for fun and profit

  1. 1. Stack switching for fun & profit Saúl Ibarra Corretgé - @saghul FOSDEM 2014
  2. 2. Hi! @saghul FOSDEM Open Source
  3. 3. import open_source
  4. 4. Some background Who knows what greenlet is? Who has used it (standalone)? Who understands how it works?
  5. 5. Greenlet Micro-threads with no implicit scheduling Lightweight Only one can run at a time Spin-off Stackless
  6. 6. Greenlet API greenlet(func, parent=None): creates a greenlet to run ‘func’ greenlet.switch(*args, **kw): switches execution to the target greenlet, the first time func(*args, **kw) will be executed greenlet.throw([typ, [val, [tb]]]): switches execution to the target greenlet and raises the specified exception (GreenletExit by default)
  7. 7. Example import 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) # 42 print g1.switch() # 'hello' print g1.dead # True
  8. 8. 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
  9. 9. How does it work? (II) Organized in a tree structure Each greenlet has a ‘parent’, except main Execution order isn’t always obvious
  10. 10. 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()
  11. 11. Stackless Python Fork of CPython, first release in 1999 Provides tasklets and channels Builtin scheduler Different approaches to switching
  12. 12. 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
  13. 13. 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
  14. 14. import _continuation Continulets are one-shot continuations Switching code is a standalone C library: stacklet rpython/translator/c/src/stacklet/
  15. 15. Continulet API continulet(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
  16. 16. 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
  17. 17. 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
  18. 18. #include <assert.h> #include "stacklet.h" Example 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; }
  19. 19. import fibers Micro-threadling library API inspired by Python threads and greenlet Uses stacklet underneath Works on CPython and PyPy On PyPy it uses continulets Or pip install fibers
  20. 20. fibers API Fiber(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
  21. 21. 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
  22. 22. Example import 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() # 42 print g1.switch() # 'hello' print g1.is_alive() # False
  23. 23. Projects using fibers Should I add yours here?
  24. 24. The Stacklet Sandwich (TM) greenlet / stackless / fibers continulet stacklet
  25. 25. Questions? @saghul