[pypy-commit] pypy tealet: Integrate, and use demo1 as the test.

arigo noreply at buildbot.pypy.org
Wed Jul 6 20:28:40 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: tealet
Changeset: r45379:bac623c0bd2f
Date: 2011-06-12 10:53 +0200
http://bitbucket.org/pypy/pypy/changeset/bac623c0bd2f/

Log:	Integrate, and use demo1 as the test.

diff --git a/pypy/rlib/_tealet_rffi.py b/pypy/rlib/_tealet_rffi.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/_tealet_rffi.py
@@ -0,0 +1,27 @@
+import os
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+
+eci = ExternalCompilationInfo(
+    includes=['src/tealet/tealet.h'],
+    separate_module_sources=['#include "src/tealet/tealet.c"\n'],
+    pre_include_bits=['#define TEALET_NO_SHARING'],
+    )
+
+def llexternal(funcname, args, restype):
+    return rffi.llexternal(funcname, args, restype,
+                           compilation_info=eci,
+                           sandboxsafe=True, threadsafe=False,
+                           _nowrapper=True)
+
+TEALET_P = rffi.COpaquePtr('tealet_t', compilation_info=eci)
+TEALET_RUN_P = lltype.Ptr(lltype.FuncType([TEALET_P, rffi.VOIDP], TEALET_P))
+NULL = lltype.nullptr(rffi.VOIDP.TO)
+NULL_TEALET = lltype.nullptr(TEALET_P.TO)
+
+tealet_initialize = llexternal("tealet_initialize", [rffi.VOIDP], TEALET_P)
+tealet_finalize   = llexternal("tealet_finalize", [TEALET_P], lltype.Void)
+tealet_new        = llexternal("tealet_new", [TEALET_P, TEALET_RUN_P,
+                                              rffi.VOIDP], rffi.INT)
+tealet_switch     = llexternal("tealet_switch", [TEALET_P], rffi.INT)
+tealet_current    = llexternal("tealet_current", [TEALET_P], TEALET_P)
diff --git a/pypy/rlib/rtealet.py b/pypy/rlib/rtealet.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/rtealet.py
@@ -0,0 +1,186 @@
+"""
+Interface to 'tealets' for RPython.
+
+Note that you must translate with the --tealet option if you include
+this file in your RPython program.
+"""
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
+from pypy.rpython.annlowlevel import llhelper
+from pypy.rlib.debug import ll_assert
+from pypy.rlib.objectmodel import we_are_translated
+
+from pypy.rlib import _tealet_rffi
+
+
+class TealetError(Exception):
+    pass
+
+
+class Tealet(object):
+    _ll_saved_stack = None
+
+    def __init__(self, main):
+        assert isinstance(main, MainTealet)
+        self.main = main
+        _new(main, self)
+
+    def switch(self):
+        _switch(self)
+
+    def run(self):
+        raise NotImplementedError
+
+
+class MainTealet(Tealet):
+    def __init__(self):
+        assert we_are_translated(), "no support for untranslated runs yet"
+        self.main = self
+        self.current = self
+        self.lltealet = _tealet_rffi.tealet_initialize(_tealet_rffi.NULL)
+
+    def __del__(self):
+        _tealet_rffi.tealet_finalize(self.lltealet)
+
+
+## ------------------------------------------------------------
+## The code below is implementation details.
+
+## XXX No support for multithreading so far!
+
+class Switcher(object):
+    current = None
+    target = None
+    count_roots = 0
+    lst = None
+    exception = None
+switcher = Switcher()
+
+def _new(main, starting_tealet):
+    #if main.ll_stack_base != _getstackbase():
+    #    raise TealetError("starting a new tealet in the wrong thread")
+    switcher.current = main.current
+    switcher.target = starting_tealet
+    llmain = main.lltealet
+    llrun = llhelper(_tealet_rffi.TEALET_RUN_P, _run)
+    llarg = _tealet_rffi.NULL
+    r = _new_critical(llmain, llrun, llarg)
+    _check_exception(r)
+
+def _new_critical(llmain, llrun, llarg):
+    # critical function: no gc operation, and no gc variable alive!
+    _save_shadow_stack()
+    r = _tealet_rffi.tealet_new(llmain, llrun, llarg)
+    _restore_shadow_stack()
+    return r
+_new_critical._dont_inline_ = True
+
+def _run(lltealet, llarg):
+    llop.gc_stack_bottom(lltype.Void)
+    # end of critical code: no gc operation before here!
+    tealet = switcher.target
+    switcher.current = None
+    switcher.target = None
+    tealet.lltealet = lltealet
+    main = tealet.main
+    main.current = tealet
+    #
+    try:
+        other = tealet.run()
+        if other is None:
+            other = main
+        if not other.lltealet:
+            raise TealetError("returning to a dead tealet")
+        if other.main is not main:
+            raise TealetError("returning to a tealet in a different group")
+    except Exception, e:
+        other = main
+        switcher.exception = e
+    tealet.lltealet = _tealet_rffi.NULL_TEALET
+    main.current = other
+    switcher.target = other
+    llresult = other.lltealet
+    return llresult
+
+def _switch(target):
+    #if target.main.ll_stack_base != _getstackbase():
+    #    raise TealetError("cannot switch to a tealet in a different thread")
+    main = target.main
+    switcher.current = main.current
+    switcher.target = target
+    main.current = target
+    r = _switch_critical(target.lltealet)
+    switcher.current = None
+    switcher.target = None
+    _check_exception(r)
+
+def _switch_critical(lltarget):
+    # critical code: no gc operation!
+    _save_shadow_stack()
+    r = _tealet_rffi.tealet_switch(lltarget)
+    _restore_shadow_stack()
+    return r
+_switch_critical._dont_inline_ = True
+
+def _check_exception(r):
+    if rffi.cast(lltype.Signed, r) != 0:
+        # rare case: tealet.c complains, e.g. out of memory.  I think that
+        # in this case it's not really possible to have 'exception != None'.
+        # Clean it anyway to avoid it showing up at a random time later.
+        switcher.exception = None
+        raise TealetError
+    e = switcher.exception
+    if e is not None:
+        switcher.exception = None
+        raise e
+
+# ____________________________________________________________
+
+ROOT_WALKER = lltype.Ptr(lltype.FuncType([llmemory.Address], lltype.Void))
+
+def _count_roots_walker(root):
+    switcher.count_roots += 1
+
+def _save_root_walker(root):
+    i = switcher.count_roots
+    switcher.count_roots = i + 1
+    gcobj = llmemory.cast_adr_to_ptr(root.address[0], llmemory.GCREF)
+    switcher.lst[i] = gcobj
+
+def _save_shadow_stack():
+    switcher.count_roots = 0
+    fn = llhelper(ROOT_WALKER, _count_roots_walker)
+    llop.gc_walk_stack_roots(lltype.Void, fn)
+    n = switcher.count_roots
+    #
+    tealet = switcher.current
+    ll_assert(tealet._ll_saved_stack is None, "tealet stack mismatch (save)")
+    tealet._ll_saved_stack = [lltype.nullptr(llmemory.GCREF.TO)] * n
+    switcher.count_roots = 0
+    switcher.lst = tealet._ll_saved_stack
+    fn = llhelper(ROOT_WALKER, _save_root_walker)
+    llop.gc_walk_stack_roots(lltype.Void, fn)
+    ll_assert(n == switcher.count_roots, "tealet stack mismatch (count1)")
+    switcher.lst = None
+_save_shadow_stack._dont_inline_ = True
+
+def _restore_root_walker(root):
+    i = switcher.count_roots
+    switcher.count_roots = i + 1
+    gcobj = switcher.lst[i]
+    root.address[0] = llmemory.cast_ptr_to_adr(gcobj)
+
+def _restore_shadow_stack():
+    tealet = switcher.target
+    lst = tealet._ll_saved_stack
+    ll_assert(lst is not None, "tealet stack mismatch (restore)")
+    tealet._ll_saved_stack = None
+    switcher.count_roots = 0
+    switcher.lst = lst
+    n = len(lst)
+    fn = llhelper(ROOT_WALKER, _restore_root_walker)
+    llop.gc_set_stack_roots_count(lltype.Void, n)
+    llop.gc_walk_stack_roots(lltype.Void, fn)
+    ll_assert(n == switcher.count_roots, "tealet stack mismatch (count2)")
+    switcher.lst = None
+_restore_shadow_stack._dont_inline_ = True
diff --git a/pypy/rlib/test/demotealet.py b/pypy/rlib/test/demotealet.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/test/demotealet.py
@@ -0,0 +1,144 @@
+import sys
+from pypy.rlib.rtealet import *
+from pypy.rlib.rrandom import Random
+from pypy.rlib.nonconst import NonConstant
+from pypy.rlib.objectmodel import compute_unique_id
+from pypy.rlib.debug import debug_print
+
+def plan_to_do(rand, steps=10000):
+    ops = []
+    live_tealets = {0: None}
+    total = 0
+    current = 0
+    i = 0
+    while i < steps or len(live_tealets) > 1:
+        r = rand.random()
+        if r < 0.06 and i < steps:
+            total += 1
+            ops.append(("new", total))
+            live_tealets[total] = None
+            current = total
+        else:
+            keys = live_tealets.keys()
+            target = keys[int(rand.random() * len(keys))]
+            if r < 0.1 and current > 0 and current != target:
+                ops.append(("finish", target))
+                del live_tealets[current]
+            else:
+                ops.append(("switch", target))
+            current = target
+        #print i, len(live_tealets), r
+        i += 1
+    assert current == 0
+    assert live_tealets.keys() == [0]
+    ops.append(("done", 0))
+    return ops
+
+# ____________________________________________________________
+
+class Replay(object):
+    def setup(self, lst, tealets):
+        self.lst = lst
+        self.index = 0
+        self.tealets = tealets
+        self.mapping = {}
+
+    def next(self):
+        result = self.lst[self.index]
+        self.index += 1
+        return result
+
+replay = Replay()
+
+class X(object):
+    fixed_pattern = 0
+    def __init__(self, value):
+        if NonConstant(True):
+            self.fixed_pattern = 0x6789ABCD
+        self.value = value
+
+class MyTealet(Tealet):
+    def run(self):
+        index = len(replay.tealets)
+        self.index = index
+        replay.tealets.append(self)
+        return_to_index = do(index, X(index))
+        replay.tealets[index] = None
+        assert 0 <= return_to_index < len(replay.tealets)
+        tt = replay.tealets[return_to_index]
+        assert tt
+        return tt
+
+def do_switch(tt):
+    replay.stuff = X(1)
+    tt.switch()
+do_switch._dont_inline_ = True
+
+def do_new(main):
+    MyTealet(main)
+do_new._dont_inline_ = True
+
+def do(current_tealet_index, x):
+    main = replay.tealets[0]
+    assert main
+    if compute_unique_id(x) in replay.mapping.values():
+        for index1, x1 in replay.mapping.items():
+            debug_print("mapping[", index1, "] =", x1)
+        debug_print("current_tealet_index =", current_tealet_index)
+        debug_print("new object x =", x, "(", compute_unique_id(x), ")")
+        assert 0
+    replay.mapping[current_tealet_index] = compute_unique_id(x)
+    while True:
+        #debug_print("in", current_tealet_index, ": x =", x, "(",
+        #            compute_unique_id(x), ")")
+        assert main.current == replay.tealets[current_tealet_index]
+        assert replay.mapping[current_tealet_index] == compute_unique_id(x)
+        assert x.fixed_pattern == 0x6789ABCD
+        assert x.value == current_tealet_index
+        operation, target = replay.next()
+        #debug_print("(", operation, target, ")")
+        if operation == "switch":
+            assert 0 <= target < len(replay.tealets)
+            tt = replay.tealets[target]
+            assert tt
+            do_switch(tt)
+        elif operation == "new":
+            assert target == len(replay.tealets)
+            do_new(main)
+        elif operation == "finish":
+            assert 0 <= target < len(replay.tealets)
+            assert target != current_tealet_index
+            del replay.mapping[current_tealet_index]
+            return target
+        elif operation == "done":
+            assert target == current_tealet_index == 0
+            return -42
+        else:
+            assert 0
+do._dont_inline_ = True
+
+def run_demo(lst):
+    main = MainTealet()
+    replay.setup(lst, [main])
+    res = do(0, X(0))
+    assert res == -42
+    for tt in replay.tealets[1:]:
+        assert not tt
+    assert replay.index == len(replay.lst)
+
+# ____________________________________________________________
+
+def entry_point(argv):
+    if len(argv) > 1:
+        seed = int(argv[1])
+    else:
+        seed = 0
+    print 'Building plan with seed=%d...' % seed
+    lst = plan_to_do(Random(seed))
+    print 'Running...'
+    run_demo(lst)
+    print 'OK'
+    return 0
+
+def target(*args):
+    return entry_point, None
diff --git a/pypy/rlib/test/test_rtealet.py b/pypy/rlib/test/test_rtealet.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/test/test_rtealet.py
@@ -0,0 +1,30 @@
+from pypy.translator.c.test.test_standalone import StandaloneTests
+
+
+class BaseTestTealet(StandaloneTests):
+
+    def setup_class(cls):
+        from pypy.config.pypyoption import get_pypy_config
+        config = get_pypy_config(translating=True)
+        config.translation.gc = "minimark"
+        config.translation.gcrootfinder = cls.gcrootfinder
+        config.translation.tealet = True
+        cls.config = config
+
+    def test_demo1(self):
+        from pypy.rlib.test.demotealet import entry_point
+        t, cbuilder = self.compile(entry_point)
+
+        expected_data = "Running...\nOK\n"
+        for i in range(20):
+            data = cbuilder.cmdexec('%d' % i, env={})
+            assert data.endswith(expected_data)
+            data = cbuilder.cmdexec('%d' % i, env={'PYPY_GC_NURSERY': '10k'})
+            assert data.endswith(expected_data)
+
+
+class TestTealetShadowstack(BaseTestTealet):
+    gcrootfinder = "shadowstack"
+
+class TestTealetAsmgcc(BaseTestTealet):
+    gcrootfinder = "asmgcc"
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -1428,7 +1428,12 @@
         gcdata = self.gcdata
         def set_stack_roots_count(count):
             bytes = count * llmemory.sizeof(llmemory.Address)
-            gcdata.root_stack_top = gcdata.root_stack_base + bytes
+            p = gcdata.root_stack_base
+            end = gcdata.root_stack_top = p + bytes
+            INVALID_NONNULL_ADDRESS = llmemory.cast_int_to_adr(8)
+            while p != end:
+                p.address[0] = INVALID_NONNULL_ADDRESS
+                p += llmemory.sizeof(llmemory.Address)
         self.set_stack_roots_count_ptr = getfn(set_stack_roots_count,
                                                [annmodel.SomeInteger()],
                                                annmodel.s_None)
diff --git a/pypy/translator/c/src/tealet/README b/pypy/translator/c/src/tealet/README
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/README
@@ -0,0 +1,9 @@
+The C sources for tealets are a direct copy of (one of) the following
+places.  In case they need updating, please consider changing them there
+instead.
+
+    From Armin Rigo's user repository:
+    http://bitbucket.org/arigo/arigo (directory hack/pypy-hack/tealet)
+
+    From Kristj&#65533;n Valur J&#65533;nsson's repository:
+    http://bitbucket.org/krisvale/public
diff --git a/pypy/translator/c/src/tealet/slp_platformselect.h b/pypy/translator/c/src/tealet/slp_platformselect.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/slp_platformselect.h
@@ -0,0 +1,12 @@
+
+#if   defined(_M_IX86)
+#include "src/tealet/switch_x86_msvc.h" /* MS Visual Studio on X86 */
+#elif defined(_M_X64)
+#include "src/tealet/switch_x64_msvc.h" /* MS Visual Studio on X64 */
+#elif defined(__GNUC__) && defined(__amd64__)
+#include "src/tealet/switch_x86_64_gcc.h" /* gcc on amd64 */
+#elif defined(__GNUC__) && defined(__i386__)
+#include "src/tealet/switch_x86_gcc.h" /* gcc on X86 */
+#else
+#error "Unsupported platform!"
+#endif
diff --git a/pypy/translator/c/src/tealet/switch_x64_msvc.asm b/pypy/translator/c/src/tealet/switch_x64_msvc.asm
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x64_msvc.asm
@@ -0,0 +1,103 @@
+;
+; stack switching code for MASM on x64
+; Kristjan Valur Jonsson, apr 2011
+;
+
+include macamd64.inc
+
+pop_reg MACRO reg
+	pop reg
+ENDM
+
+load_xmm128 macro Reg, Offset
+	movdqa  Reg, Offset[rsp]
+endm
+
+.code
+
+;arguments save_state, restore_state, extra are passed in rcx, rdx, r8 respectively
+;slp_switch PROC FRAME
+NESTED_ENTRY slp_switch, _TEXT$00
+	; save all registers that the x64 ABI specifies as non-volatile.
+	; This includes some mmx registers.  May not always be necessary,
+	; unless our application is doing 3D, but better safe than sorry.
+	alloc_stack 168; 10 * 16 bytes, plus 8 bytes to make stack 16 byte aligned
+	save_xmm128 xmm15, 144
+	save_xmm128 xmm14, 128
+	save_xmm128 xmm13, 112
+	save_xmm128 xmm12, 96
+	save_xmm128 xmm11, 80
+	save_xmm128 xmm10, 64
+	save_xmm128 xmm9,  48
+	save_xmm128 xmm8,  32
+	save_xmm128 xmm7,  16
+	save_xmm128 xmm6,  0
+	
+	push_reg r15
+	push_reg r14
+	push_reg r13
+	push_reg r12
+	
+	push_reg rbp
+	push_reg rbx
+	push_reg rdi
+	push_reg rsi
+	
+	sub rsp, 20h ;allocate shadow stack space for the arguments (must be multiple of 16)
+	.allocstack 20h
+.endprolog
+
+	;save argments in nonvolatile registers
+	mov r12, rcx ;save_state
+	mov r13, rdx
+	mov r14, r8
+
+	; load stack base that we are saving minus the callee argument
+	; shadow stack.  We don't want that clobbered
+	lea rcx, [rsp+20h] 
+	mov rdx, r14
+	call r12 ;pass stackpointer, return new stack pointer in eax
+	
+	; an odd value means that we don't restore, could be
+	; an error (e.g. -1) or indication that we only want
+	; to save state (1).  return that value to the caller.
+	test rax, 1
+	jnz exit
+	
+	;actual stack switch (and re-allocating the shadow stack):
+	lea rsp, [rax-20h]
+	
+	mov rcx, rax ;pass new stack pointer
+	mov rdx, r14
+	call r13
+	;return the rax
+EXIT:
+	
+	add rsp, 20h
+	pop_reg rsi
+	pop_reg rdi
+	pop_reg rbx
+	pop_reg rbp
+	
+	pop_reg r12
+	pop_reg r13
+	pop_reg r14
+	pop_reg r15
+	
+	load_xmm128 xmm15, 144
+	load_xmm128 xmm14, 128
+	load_xmm128 xmm13, 112
+	load_xmm128 xmm12, 96
+	load_xmm128 xmm11, 80
+	load_xmm128 xmm10, 64
+	load_xmm128 xmm9,  48
+	load_xmm128 xmm8,  32
+	load_xmm128 xmm7,  16
+	load_xmm128 xmm6,  0
+	add rsp, 168
+	ret
+	
+NESTED_END slp_switch, _TEXT$00
+;slp_switch ENDP 
+	
+END
\ No newline at end of file
diff --git a/pypy/translator/c/src/tealet/switch_x64_msvc.h b/pypy/translator/c/src/tealet/switch_x64_msvc.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x64_msvc.h
@@ -0,0 +1,7 @@
+/* The actual stack saving function, which just stores the stack,
+ * this declared in an .asm file
+ */
+extern void *slp_switch(void *(*save_state)(void*, void*),
+                        void *(*restore_state)(void*, void*),
+                        void *extra);
+
diff --git a/pypy/translator/c/src/tealet/switch_x86_64_gcc.h b/pypy/translator/c/src/tealet/switch_x86_64_gcc.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x86_64_gcc.h
@@ -0,0 +1,55 @@
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+                        void *(*restore_state)(void*, void*),
+                        void *extra)
+{
+  void *result, *garbage1, *garbage2;
+  __asm__ volatile (
+     "pushq %%rbp\n"
+     "pushq %%rbx\n"       /* push the registers specified as caller-save */
+     "pushq %%r12\n"
+     "pushq %%r13\n"
+     "pushq %%r14\n"
+     "pushq %%r15\n"
+
+     "movq %%rax, %%r12\n" /* save 'restore_state' for later */
+     "movq %%rsi, %%r13\n" /* save 'extra' for later         */
+
+                           /* arg 2: extra (already in rsi)      */
+     "movq %%rsp, %%rdi\n" /* arg 1: current (old) stack pointer */
+     "call *%%rcx\n"       /* call save_state()                  */
+
+     "testb $1, %%al\n"        /* skip the rest if the return value is odd */
+     "jnz 0f\n"
+
+     "movq %%rax, %%rsp\n"     /* change the stack pointer */
+
+     /* From now on, the stack pointer is modified, but the content of the
+        stack is not restored yet.  It contains only garbage here. */
+
+     "movq %%r13, %%rsi\n" /* arg 2: extra                       */
+     "movq %%rax, %%rdi\n" /* arg 1: current (new) stack pointer */
+     "call *%%r12\n"       /* call restore_state()               */
+
+     /* The stack's content is now restored. */
+
+     "0:\n"
+     "popq %%r15\n"
+     "popq %%r14\n"
+     "popq %%r13\n"
+     "popq %%r12\n"
+     "popq %%rbx\n"
+     "popq %%rbp\n"
+
+     : "=a"(result),             /* output variables */
+       "=c"(garbage1),
+       "=S"(garbage2)
+     : "a"(restore_state),       /* input variables  */
+       "c"(save_state),
+       "S"(extra)
+     : "memory", "rdx", "rdi", "r8", "r9", "r10", "r11",
+       "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+       "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15"
+     );
+  return result;
+}
diff --git a/pypy/translator/c/src/tealet/switch_x86_gcc.h b/pypy/translator/c/src/tealet/switch_x86_gcc.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x86_gcc.h
@@ -0,0 +1,56 @@
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+                        void *(*restore_state)(void*, void*),
+                        void *extra)
+{
+  void *result, *garbage1, *garbage2;
+  __asm__ volatile (
+     "pushl %%ebp\n"
+     "pushl %%ebx\n"       /* push some registers that may contain */
+     "pushl %%esi\n"       /* some value that is meant to be saved */
+     "pushl %%edi\n"
+
+     "movl %%eax, %%esi\n" /* save 'restore_state' for later */
+     "movl %%edx, %%edi\n" /* save 'extra' for later         */
+
+     "movl %%esp, %%eax\n"
+
+     "pushl %%edx\n"       /* arg 2: extra                       */
+     "pushl %%eax\n"       /* arg 1: current (old) stack pointer */
+     "call *%%ecx\n"       /* call save_state()                  */
+
+     "testl $1, %%eax\n"       /* skip the rest if the return value is odd */
+     "jnz 0f\n"
+
+     "movl %%eax, %%esp\n"     /* change the stack pointer */
+
+     /* From now on, the stack pointer is modified, but the content of the
+        stack is not restored yet.  It contains only garbage here. */
+
+     "pushl %%edi\n"       /* arg 2: extra                       */
+     "pushl %%eax\n"       /* arg 1: current (new) stack pointer */
+     "call *%%esi\n"       /* call restore_state()               */
+
+     /* The stack's content is now restored. */
+
+     "0:\n"
+     "addl $8, %%esp\n"
+     "popl %%edi\n"
+     "popl %%esi\n"
+     "popl %%ebx\n"
+     "popl %%ebp\n"
+
+     : "=a"(result),             /* output variables */
+       "=c"(garbage1),
+       "=d"(garbage2)
+     : "a"(restore_state),       /* input variables  */
+       "c"(save_state),
+       "d"(extra)
+     : "memory"
+     );
+  /* Note: we should also list all fp/xmm registers, but is there a way
+     to list only the ones used by the current compilation target?
+     For now we will just ignore the issue and hope (reasonably) that
+     this function is never inlined all the way into 3rd-party user code. */
+  return result;
+}
diff --git a/pypy/translator/c/src/tealet/switch_x86_msvc.asm b/pypy/translator/c/src/tealet/switch_x86_msvc.asm
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x86_msvc.asm
@@ -0,0 +1,44 @@
+
+.386
+.model flat, c
+
+.code
+
+slp_switch_raw PROC save_state:DWORD, restore_state:DWORD, extra:DWORD
+  
+  ;save registers. EAX ECX and EDX are available for function use and thus
+  ;do not have to be stored.
+  push ebx
+  push esi
+  push edi
+  push ebp
+  
+  mov esi, restore_state ; /* save 'restore_state' for later */
+  mov edi, extra ;         /* save 'extra' for later         */
+
+  mov eax, esp
+
+  push edi ;               /* arg 2: extra                       */
+  push eax ;               /* arg 1: current (old) stack pointer */
+  mov  ecx, save_state
+  call ecx ;               /* call save_state()                  */
+
+  test eax, 1;             /* skip the restore if the return value is odd */
+  jnz exit
+
+  mov esp, eax;            /* change the stack pointer */
+
+  push edi ;               /* arg 2: extra                       */
+  push eax ;               /* arg 1: current (new) stack pointer */
+  call esi ;               /* call restore_state()               */
+
+exit:
+  add esp, 8
+  pop  ebp
+  pop  edi
+  pop  esi
+  pop  ebx
+  ret
+slp_switch_raw ENDP
+
+end
\ No newline at end of file
diff --git a/pypy/translator/c/src/tealet/switch_x86_msvc.h b/pypy/translator/c/src/tealet/switch_x86_msvc.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/switch_x86_msvc.h
@@ -0,0 +1,26 @@
+/* The actual stack saving function, which just stores the stack,
+ * this declared in an .asm file
+ */
+extern void *slp_switch_raw(void *(*save_state)(void*, void*),
+                        void *(*restore_state)(void*, void*),
+                        void *extra);
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* Store any other runtime information on the local stack */
+#pragma optimize("", off) /* so that autos are stored on the stack */
+#pragma warning(disable:4733) /* disable warning about modifying FS[0] */
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+                        void *(*restore_state)(void*, void*),
+                        void *extra)
+{
+    /* store the structured exception state for this stack */
+    DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList));
+    void * result = slp_switch_raw(save_state, restore_state, extra);
+    __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state);
+    return result;
+}
+#pragma warning(default:4733) /* disable warning about modifying FS[0] */
+#pragma optimize("", on)
diff --git a/pypy/translator/c/src/tealet/tealet.c b/pypy/translator/c/src/tealet/tealet.c
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/tealet.c
@@ -0,0 +1,600 @@
+/********** A minimal coroutine package for C **********
+ * By Armin Rigo
+ * Documentation: see the source code of the greenlet package from
+ *
+ *     http://codespeak.net/svn/greenlet/trunk/c/_greenlet.c
+ */
+
+#include "src/tealet/tealet.h"
+
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+
+/************************************************************
+ * platform specific code
+ */
+
+/* The default stack direction is downwards, 0, but platforms
+ * can redefine it to upwards growing, 1.
+ */
+#define STACK_DIRECTION 0   
+
+#include "src/tealet/slp_platformselect.h"
+
+#if STACK_DIRECTION == 0
+#define STACK_STOP_MAIN     ((char*) -1)    /* for stack_stop */
+#define STACK_LE(a, b)      ((a) <= (b))    /* to compare stack position */
+#define STACK_SUB(a, b)     ((a) - (b))     /* to subtract stack pointers */
+#else
+#define STACK_STOP_MAIN     ((char*) 1)     /* for stack_stop */
+#define STACK_LE(a, b)      ((b) <= (a))    /* to compare stack position */
+#define STACK_SUB(a, b)     ((b) - (a))     /* to subtract stack pointers */
+#endif
+
+/************************************************************/
+
+/* #define DEBUG_DUMP */
+
+#ifdef DEBUG_DUMP
+#include <stdio.h>
+static int counter = 0;
+#endif
+
+
+/* the actual tealet structure as used internally */
+typedef struct tealet_sub_s {
+  tealet_t base;
+
+  /* The stack claimed by this tealet.  Since we allow for architectures
+   * where stack can be allocated downwards in memory (most common) or
+   * upwards (less common), we use the terms near or far to discern this.
+   * The main tealet will have stack_stop set to the end of memory.
+   * stack_start is zero for a running tealet, otherwise it contains
+   * the address where the stack_copy should go.
+   * In addition, stack_stop is set to NULL value to indicate
+   * that a tealet is exiting.
+   */
+  char *stack_start;                /* the "near" end of the stack */
+  char *stack_stop;                 /* the "far" end of the stack. */
+  
+  /* if the tealet is inactive, the following contain its saved
+   * stack, otherwise, both are zero.
+   * stack_saved can also be -1, meaning that saving the stack
+   * failed and the tealet is invalid.
+   */
+  char *stack_copy;                 /* saved data */
+  size_t stack_saved;               /* the amount saved */
+
+  /* A linked list of partially or completely unsaved tealets linked
+   * from the currently executing tealet.  Each one is has it's
+   * stack_stop higher to the previous one.  This is used
+   * to enable just-in-time saving of stacks.
+   */
+  struct tealet_sub_s *stack_prev;
+#ifdef DEBUG_DUMP
+  int counter;
+#endif
+} tealet_sub_t;
+
+/* The main tealet has additional fields for housekeeping */
+typedef struct {
+  tealet_sub_t base;
+  tealet_sub_t *g_current;
+  tealet_sub_t *g_target;   /* Temporary store when switching */
+  tealet_run_t  g_run;      /* run function and arguments */
+  void         *g_run_arg;
+  tealet_alloc_t g_alloc;   /* the allocation context used */
+} tealet_main_t;
+
+#define TEALET_IS_MAIN(t)  (((tealet_sub_t *)(t))->stack_stop == STACK_STOP_MAIN)
+#define TEALET_MAIN(t)     ((tealet_main_t *)((t)->base.main))
+
+/************************************************************/
+
+int (*_tealet_switchstack)(tealet_main_t*);
+int (*_tealet_initialstub)(tealet_main_t*, long*);
+
+/************************************************************
+ * helpers to call the malloc functions provided by the user
+ */
+static void *g_malloc(tealet_main_t *g_main, size_t size)
+{
+    return g_main->g_alloc.malloc_p(size, g_main->g_alloc.context);
+}
+static void *g_realloc(tealet_main_t *g_main, void *ptr, size_t size)
+{
+    return g_main->g_alloc.realloc_p(ptr, size, g_main->g_alloc.context);
+}
+static void g_free(tealet_main_t *g_main, void *ptr)
+{
+    g_main->g_alloc.free_p(ptr, g_main->g_alloc.context);
+}
+
+/*************************************************************
+ * Helpers to allocate, grow and duplicate stacks, using reference
+ * counts. This allows us to duplicate tealets (stubs primarily)
+ * cheaply and without all sorts of special casing.
+ */
+static void* stack_grow(tealet_main_t *main, void *old, size_t oldsize, size_t newsize)
+{
+#ifndef TEALET_NO_SHARING
+    char *result;
+    newsize += sizeof(size_t);
+    assert(oldsize < newsize);
+    if (old == NULL) {
+        result = (char*)g_malloc(main, newsize);
+    } else {
+        char *realp = (char*)old - sizeof(size_t);
+        size_t cnt = *(size_t*)realp;
+        if (cnt == 1) {
+            result = (char*)g_realloc(main, (void*)realp, newsize);
+        } else {
+            /* we can't grow a shared stack, un-share it */
+            result = (char*)g_malloc(main, newsize);
+            if (result != NULL) {
+                --(*(size_t*)realp);
+                memcpy((void*)result, (void*)realp, oldsize + sizeof(size_t));
+            }
+        }
+    }
+    if (result == NULL)
+        return NULL;
+    *(size_t*)result = 1;
+    return (void*)(result + sizeof(size_t));
+#else
+    return g_realloc(main, old, newsize);
+#endif
+}
+
+static void stack_free(tealet_main_t *main, void *ptr)
+{
+#ifndef TEALET_NO_SHARING
+    if (ptr != NULL) {
+        char *realp = (char*)ptr - sizeof(size_t);
+        if (--(*(size_t*)realp) == 0)
+            g_free(main, (void*)realp);
+    }
+#else
+    g_free(main, ptr);
+#endif
+}
+
+#ifndef TEALET_NO_SHARING
+static void* stack_dup(void *ptr)
+{
+    if (ptr != NULL) {
+        /* just increment the reference count */
+        char *realp = (char*)ptr - sizeof(size_t);
+        ++(*(size_t*)realp);
+    }
+    return ptr;
+}
+#endif
+
+/***************************************************************/
+
+static int g_save(tealet_sub_t* g, char* stop, int fail_ok)
+{
+    /* Save more of g's stack into the heap -- at least up to 'stop'
+
+       In the picture below, the C stack is on the left, growing down,
+       and the C heap on the right.  The area marked with xxx is the logical
+       stack of the tealet 'g'.  It can be half in the C stack (its older
+       part), and half in the heap (its newer part).
+
+       g->stack_stop |________|
+                     |xxxxxxxx|
+                     |xxx __ stop       .........
+                     |xxxxxxxx|    ==>  :       :
+                     |________|         :_______:
+                     |        |         |xxxxxxx|
+                     |        |         |xxxxxxx|
+      g->stack_start |        |         |_______| g->stack_copy
+
+     */
+    ptrdiff_t sz1 = g->stack_saved;
+    ptrdiff_t sz2 = STACK_SUB(stop, g->stack_start);
+    assert(g->stack_stop != NULL); /* it is not exiting */
+    assert(STACK_LE(stop, g->stack_stop));
+
+    if (sz2 > sz1) {
+        tealet_main_t *g_main = (tealet_main_t *)g->base.main;
+        char* c = stack_grow(g_main, g->stack_copy, sz1, sz2);
+        if (c == NULL) {
+            if (fail_ok)
+                return -1;
+            /* we cannot signal a failure, either because this is an exit
+             * switch, or the user is doing an emergency switch, ignoring
+             * failures.
+             * We must therefore mark this tealet's saved state as
+             * invalid, so that we can't switch to it again.
+             */
+            g->stack_saved = (size_t)-1; /* invalid saved stack */
+            return 0;
+        }
+#if STACK_DIRECTION == 0
+        memcpy(c+sz1, g->stack_start+sz1, sz2-sz1);
+#else
+        memmove(c+sz2-sz1, c, sz1);
+        memcpy(c, stop, sz2-sz1);
+#endif
+        g->stack_copy = c;
+        g->stack_saved = sz2;
+    }
+    return 0;
+}
+
+/* main->g_target contains the tealet we are switching to:
+ * target->stack_stop is the limit to which we must save the old stack
+ * target->stack_start can be NULL, indicating that the target stack
+ * needs not be restored.
+ */
+static void *g_save_state(void *old_stack_pointer, void *main)
+{
+    /* must free all the C stack up to target->stack_stop */
+    tealet_main_t *g_main = (tealet_main_t *)main;
+    tealet_sub_t *g_target = g_main->g_target;
+    tealet_sub_t *g_current = g_main->g_current;
+    char* target_stop = g_target->stack_stop;
+    int exiting;
+    assert(target_stop != NULL);
+    assert(g_current != g_target);
+    assert(g_current->stack_saved == 0);
+    assert(g_current->stack_start == NULL);
+
+    exiting = g_current->stack_stop == NULL;
+    if (exiting) {
+        /* tealet is exiting. We don't save its stack, and delete it, but
+         * may need to save other stacks on demand
+         */
+        tealet_sub_t *g;
+        assert(!TEALET_IS_MAIN(g_current));
+        g = g_current;
+        g_current = g_current->stack_prev;
+        g_free(g_main, g);
+    } else
+        g_current->stack_start = old_stack_pointer;
+
+    /* save and unlink tealets that are completely within
+     * the area to clear. 
+     */
+    while (g_current != NULL && STACK_LE(g_current->stack_stop, target_stop)) {
+        tealet_sub_t *prev = g_current->stack_prev;
+        if (g_current != g_target) {            /* but don't save the target */
+            assert(!TEALET_IS_MAIN(g_current)); /* never completely save main */
+            if (g_save(g_current, g_current->stack_stop, exiting) == -1) {
+                /* make sure that stack chain is intact if we have error */
+                if (g_current != g_main->g_current)
+                    g_main->g_current->stack_prev = g_current;
+                return (void *)-1; /* error */
+            }
+        }
+        g_current->stack_prev = NULL;
+        g_current = prev;
+    }
+
+    /* save a partial stack */
+    if (g_current != NULL && g_save(g_current, target_stop, exiting) == -1)
+        return (void *) -1; /* error */
+
+    assert(g_target->stack_prev == NULL);
+    g_target->stack_prev = g_current;
+      
+    if (g_target->stack_start == NULL)
+        return (void *)1; /* don't restore */
+
+    return g_target->stack_start;
+}
+
+static void *g_restore_state(void *new_stack_pointer, void *main)
+{
+    tealet_main_t *g_main = (tealet_main_t *)main;
+    tealet_sub_t *g = g_main->g_target;
+
+    /* Restore the heap copy back into the C stack */
+    assert(g->stack_start != NULL);
+    if (g->stack_saved != 0) {
+        size_t stack_saved = g->stack_saved;
+#if STACK_DIRECTION == 0
+        memcpy(g->stack_start, g->stack_copy, stack_saved);
+#else
+        memcpy(g->stack_start - stack_saved, g->stack_copy, stack_saved);
+#endif
+        stack_free(g_main, g->stack_copy);
+        g->stack_copy = NULL;
+        g->stack_saved = 0;
+    }
+    g->stack_start = NULL; /* mark as running */
+    return NULL;
+}
+
+static int g_switchstack(tealet_main_t *g_main)
+{
+    /* note: we can't pass g_target simply as an argument here, because
+     of the mix between different call stacks: after slp_switch() it
+     might end up with a different value.  But g_main is safe, because
+     it should have always the same value before and after the switch. */
+    void *res;
+    assert(g_main->g_target);
+    assert(g_main->g_target != g_main->g_current);
+    /* if the target saved stack is invalid (due to a failure to save it
+    * during the exit of another tealet), we detect this here and
+    * report an error
+    * return value is:
+    *  0 = successful switch
+    *  1 = successful save only
+    * -1 = error, couldn't save state
+    * -2 = error, target tealet corrupt
+    */
+    if (g_main->g_target->stack_saved == (size_t)-1)
+        return -2;
+    res = slp_switch(g_save_state, g_restore_state, g_main);
+    if ((ptrdiff_t)res >= 0)
+        g_main->g_current = g_main->g_target;
+    g_main->g_target = NULL;
+    return (ptrdiff_t)res;
+}
+
+/* This function gets called for two cases:  In the first,
+ * case, we are initializing and switching to a new stub,
+ * in order to immediately start a new tealet's execution.
+ * In this case, g_main->run will be non-zero.
+ * The other case is when we are just saving the current
+ * execution state in the stub, in order to reawaken it
+ * later.  In this case, g_main->run is zero.
+ */
+static int g_initialstub(tealet_main_t *g_main, long *dummymarker)
+{
+    int result;
+    tealet_sub_t *g_current = g_main->g_current;
+    tealet_sub_t *g_target = g_main->g_target;
+    assert(g_target->stack_start == NULL);
+    g_target->stack_stop = (char *)dummymarker;
+    
+    if (g_main->g_run == NULL) {
+        /* if are saving the execution state in the stub, we set
+         * things up so that the stub is running, and then switch back
+         * from it to our caller.
+         */
+        g_target->stack_prev = g_main->g_current;
+        g_main->g_target = g_current;
+        g_main->g_current = g_target;
+    }
+    /* The following call can return multiple times.  The first
+     * time it returns with 1, when the stub is saved.
+     * Then it can return with 0 when there is a switch into the
+     * stub.
+     */
+    result = _tealet_switchstack(g_main);
+    if (result < 0) {
+        /* couldn't allocate stack */
+        g_main->g_current = g_current;
+        return result;
+    }
+
+    if (g_main->g_run) {
+        /* this is the invocation of a new tealet */
+        tealet_sub_t *g, *g_target;
+        tealet_run_t run = g_main->g_run;
+        void *run_arg = g_main->g_run_arg;
+        g_main->g_run = NULL;
+        g_main->g_run_arg = NULL;
+        g = g_main->g_current;
+        assert(g->stack_start == NULL);     /* running */      
+#ifdef DEBUG_DUMP
+          printf("starting %p\n", g);
+#endif
+
+        g_target = (tealet_sub_t *)(run((tealet_t *)g, run_arg));
+        if (!g_target)
+            g_target = &g_main->base;
+        assert(g_target != g);
+        assert(TEALET_MAIN(g_target) == g_main);
+        assert(g_main->g_current == g);
+#ifdef DEBUG_DUMP
+        printf("ending %p -> %p\n", g, g_target);
+#endif
+        assert(g->stack_copy == NULL);
+        g->stack_stop = NULL;              /* dying */
+        g_main->g_target = g_target;
+        _tealet_switchstack(g_main);
+        assert(!"This point should not be reached");
+    }
+    return 0;
+}
+
+static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc)
+{
+    size_t size;
+    tealet_sub_t *g;
+    size = g_main == NULL ? sizeof(tealet_main_t) : sizeof(tealet_sub_t);
+    g = alloc->malloc_p(size, alloc->context);
+    if (g == NULL)
+        return NULL;
+    if (g_main == NULL)
+        g_main = (tealet_main_t *)g;
+    g->base.main = (tealet_t *)g_main;
+    g->base.data = NULL;
+    g->stack_start = NULL;
+    g->stack_stop = NULL;
+    g->stack_copy = NULL;
+    g->stack_saved = 0;
+    g->stack_prev = NULL;
+#ifdef DEBUG_DUMP
+    g->counter = counter++;
+#endif
+    return g;
+}
+
+static int tealet_new_int(tealet_t *main, tealet_run_t run, void *run_arg, tealet_sub_t **res)
+{
+    long dummymarker;
+    int result;
+    tealet_main_t *g_main = (tealet_main_t *)main->main;
+    assert(TEALET_IS_MAIN(g_main));
+    assert(!g_main->g_target);
+    assert(!g_main->g_run);
+    assert(!g_main->g_run_arg);
+    g_main->g_target = tealet_alloc(g_main, &g_main->g_alloc);
+    if (g_main->g_target == NULL)
+        return -1; /* Could not allocate */
+    if (res != NULL)
+        *res = g_main->g_target;
+    g_main->g_run = run;
+    g_main->g_run_arg = run_arg;
+    result = _tealet_initialstub(g_main, &dummymarker);
+    if (result < 0) {
+        /* could not save stack */
+        g_free(g_main, g_main->g_target);
+        g_main->g_target = NULL;
+        g_main->g_run = NULL;
+        g_main->g_run_arg = NULL;
+        return result;
+    }
+    return 0;
+}
+
+/************************************************************/
+
+static tealet_alloc_t stdalloc = TEALET_MALLOC;
+
+tealet_t *tealet_initialize(tealet_alloc_t *alloc)
+{
+    /* NB. there are a lot of local variables with names starting with 'g_'.
+       In the original stackless and greenlet code these were typically
+       globals.  There are no global variables left in tealets. */
+    tealet_sub_t *g;
+    tealet_main_t *g_main;
+    if (alloc == NULL)
+      alloc = &stdalloc;
+    g = tealet_alloc(NULL, alloc);
+    if (g == NULL)
+        return NULL;
+    g_main = (tealet_main_t *)g;
+    g->stack_start = NULL;
+    g->stack_stop = STACK_STOP_MAIN;
+    g_main->g_current = g;
+    g_main->g_target = NULL;
+    g_main->g_run = NULL;
+    g_main->g_run_arg = NULL;
+    g_main->g_alloc = *alloc;
+    assert(TEALET_IS_MAIN(g_main));
+    /* set up the following field with an indirection, which is needed
+     to prevent any inlining */
+    _tealet_initialstub = g_initialstub;
+    _tealet_switchstack = g_switchstack;
+    return (tealet_t *)g_main;
+}
+
+void tealet_finalize(tealet_t *main)
+{
+    tealet_main_t *g_main = (tealet_main_t *)main;
+    assert(TEALET_IS_MAIN(g_main));
+    assert(g_main->g_current == (tealet_sub_t *)g_main);
+    g_free(g_main, g_main);
+}
+
+int tealet_new(tealet_t *main, tealet_run_t run, void *run_arg)
+{
+    return tealet_new_int(main, run, run_arg, NULL);
+}
+ 
+tealet_t *tealet_current(tealet_t *main)
+{
+    tealet_main_t *g_main = (tealet_main_t *)main;
+    assert(TEALET_IS_MAIN(g_main));
+    return (tealet_t *)g_main->g_current;
+}
+
+int tealet_switch(tealet_t *target)
+{
+    tealet_sub_t *g_target = (tealet_sub_t *)target;
+    tealet_main_t *g_main = TEALET_MAIN(g_target);
+    int result = 0;
+    if (g_target != g_main->g_current) {
+    #ifdef DEBUG_DUMP
+        printf("switch %p -> %p\n", g_main->g_current, g_target);
+    #endif
+        g_main->g_target = g_target;
+        result = _tealet_switchstack(g_main);
+    #ifdef DEBUG_DUMP
+        printf("done switching, res=%d, now in %p\n", result, g_main->g_current);
+    #endif
+    }
+    return result;
+}
+
+int tealet_exit(tealet_t *target)
+{
+    tealet_sub_t *g_target = (tealet_sub_t *)target;
+    tealet_main_t *g_main = TEALET_MAIN(g_target);
+    char *stack_stop = g_target->stack_stop;
+    int result;
+    if (TEALET_IS_MAIN(g_target) || g_target == g_main->g_current)
+        return -2; /* invalid tealet */
+
+    g_target->stack_stop = NULL; /* signal exit */
+    g_main->g_target = g_target;
+    result = _tealet_switchstack(g_main);
+    assert(result < 0); /* only return here if there was failure */
+    g_target->stack_stop = stack_stop;
+    return result;
+}
+
+tealet_t *tealet_stub_new(tealet_t *main)
+{
+    tealet_sub_t *g_result;
+    if (tealet_new_int(main, NULL, NULL, &g_result) < 0)
+        return NULL;
+    assert(g_result->stack_copy);
+    assert(STACK_SUB(g_result->stack_stop, g_result->stack_start) == g_result->stack_saved);
+    return (tealet_t*)g_result;
+}
+
+int tealet_stub_run(tealet_t *stub, tealet_run_t run, void *run_arg)
+{
+    tealet_sub_t *g_target = (tealet_sub_t *)stub;
+    tealet_main_t *g_main = TEALET_MAIN(g_target);
+    int result = 0;
+    assert(g_target != g_main->g_current && g_target != (tealet_sub_t*)g_main);
+    assert(g_main->g_run == 0);
+    assert(g_main->g_run_arg == 0);
+    g_main->g_run = run;
+    g_main->g_run_arg = run_arg;
+#ifdef DEBUG_DUMP
+    printf("stub run %p -> %p\n", g_main->g_current, g_target);
+#endif
+    g_main->g_target = g_target;
+    result = _tealet_switchstack(g_main);
+#ifdef DEBUG_DUMP
+    printf("done stub_run, res=%d, now in %p\n", result, g_main->g_current);
+#endif
+    return result;
+}
+
+#ifndef TEALET_NO_SHARING
+tealet_t *tealet_duplicate(tealet_t *tealet)
+{
+    tealet_sub_t *g_tealet = (tealet_sub_t *)tealet;
+    tealet_main_t *g_main = TEALET_MAIN(g_tealet);
+    tealet_sub_t *g_copy;
+    assert(g_tealet != g_main->g_current && g_tealet != (tealet_sub_t*)g_main);
+    g_copy = tealet_alloc(g_main, &g_main->g_alloc);
+    if (g_copy == NULL)
+        return NULL;
+    *g_copy = *g_tealet;
+    g_copy->stack_copy = stack_dup(g_copy->stack_copy);
+    return (tealet_t*)g_copy;
+}
+#endif
+
+void tealet_delete(tealet_t *target)
+{
+    tealet_sub_t *g_target = (tealet_sub_t *)target;
+    tealet_main_t *g_main = TEALET_MAIN(g_target);
+    /* XXX this is wrong.  Deleting a random tealet is delicate,
+       because it can be part of the stack_prev chained list */
+    stack_free(g_main, g_target->stack_copy);
+    g_free(g_main, g_target);
+}
diff --git a/pypy/translator/c/src/tealet/tealet.h b/pypy/translator/c/src/tealet/tealet.h
new file mode 100644
--- /dev/null
+++ b/pypy/translator/c/src/tealet/tealet.h
@@ -0,0 +1,147 @@
+/********** A minimal coroutine package for C **********/
+#ifndef _TEALET_H_
+#define _TEALET_H_
+
+#include <stdlib.h>
+
+
+#ifdef WIN32
+#if defined TEALET_EXPORTS
+#define TEALET_API __declspec(dllexport)
+#elif defined TEALET_IMPORTS
+#define TEALET_API __declspec(dllimport)
+#else
+#define TEALET_API
+#endif
+#else /* win32 */
+#define TEALET_API
+#endif
+
+
+/* A structure to define the memory allocation api used.
+ * the functions have C89 semantics and take an additional "context"
+ * pointer that they can use as they please
+ */
+typedef struct tealet_alloc_s {
+  void *(*malloc_p)(size_t size, void *context);
+  void *(*realloc_p)(void *ptr, size_t size, void *context);
+  void  (*free_p)(void *ptr, void *context);
+  void *context;
+} tealet_alloc_t;
+
+/* use the following macro to initialize a tealet_alloc_t
+ * structure with stdlib malloc functions, for convenience, e.g.:
+ * tealet_alloc_t stdalloc = TEALET_MALLOC;
+ */
+#define TEALET_MALLOC {\
+    (void *(*)(size_t, void*))malloc, \
+    (void *(*)(void*, size_t, void*))realloc, \
+    (void (*)(void*, void*))free, \
+    0 \
+}
+
+
+/* The user-visible tealet structure */
+typedef struct tealet_s {
+  struct tealet_s *main;   /* pointer to the main tealet */
+  void *data;              /* general-purpose, store whatever you want here */
+  /* private fields follow */
+} tealet_t;
+
+/* The "run" function of a tealet.  It is called with the
+ * current tealet and the argument provided to its start function 
+ */
+typedef tealet_t *(*tealet_run_t)(tealet_t *current, void *arg);
+
+
+/* error codes.  API functions that return int return a negative value
+ * to signal an error.
+ * Those that return tealet_t pointers return NULL to signal a memory
+ * error.
+ */
+#define TEALET_ERR_MEM -1       /* memory allocation failed */
+#define TEALET_ERR_INVALID -2   /* the target tealet is corrupt */
+
+
+/* Initialize and return the main tealet.  The main tealet contains the whole
+ * "normal" execution of the program; it starts when the program starts and
+ * ends when the program ends.  This function and tealet_finalize() should
+ * be called together from the same (main) function which calls the rest of
+ * the program.  It is fine to nest several uses of initialize/finalize,
+ * or to call them in multiple threads in case of multithreaded programs,
+ * as long as you don't try to switch to tealets created with a
+ * different main tealet.
+ */
+TEALET_API
+tealet_t *tealet_initialize(tealet_alloc_t *alloc);
+
+/* Tear down the main tealet.  Call e.g. after a thread finishes (including
+ * all its tealets).
+ */
+TEALET_API
+void tealet_finalize(tealet_t *main);
+
+/* Allocate a new tealet 'g', and call 'run(g, arg)' in it.
+ * The return value of run() must be the next tealet in which to continue
+ * execution, which must be a different one, like for example the main tealet.
+ * When 'run(g)' returns, the tealet 'g' is freed.
+ */
+TEALET_API
+int tealet_new(tealet_t *main, tealet_run_t run, void *arg);
+
+/* Return the current tealet, i.e. the one in which the caller of this
+ * function currently is.  "main" can be any tealet derived from the
+ * main tealet.
+ */
+TEALET_API
+tealet_t *tealet_current(tealet_t *main);
+
+/* Switch to another tealet.  Execution continues there.  The tealet
+ * passed in must not have been freed yet and must descend from
+ * the same main tealet as the current one.  In multithreaded applications,
+ * it must also belong to the current thread (otherwise, segfaults).
+ */
+TEALET_API
+int tealet_switch(tealet_t *target);
+
+/* Exit the current tealet.  Same as tealet_switch except that
+ * it the current tealet is deleted.  Use this only in emergency,
+ * if tealet_switch() fails due to inability to save the stack.
+ * This call fails only if the target has invalid state, otherwise
+ * it never returns.
+ */
+TEALET_API
+int tealet_exit(tealet_t *target);
+
+/* Duplicate a tealet. This is intended for the duplication of stubs so
+ * that new stubs can be recretaed with a predetermined stack.
+ */
+#ifndef TEALET_NO_SHARING
+TEALET_API
+tealet_t *tealet_duplicate(tealet_t *tealet);
+#endif
+
+/* Deallocate a tealet.  Use this to delete a stub that
+ * is no longer being used for tealet_stub_dup(), or to deallocate
+ * a tealet that has become invalid due to memory errors.
+ * It can also delete a suspended tealet, which effectively kills it.
+ *
+ * XXX probably buggy
+ */
+TEALET_API
+void tealet_delete(tealet_t *target);
+
+/* Allocate a new tealet stub at this position.  This can later
+ * be run with tealet_stub_new(), duplicated with tealet_duplicate()
+ * and deleted with tealet_stub_del().
+ */
+TEALET_API
+tealet_t *tealet_stub_new(tealet_t *main);
+
+/* Run a stub.  The combination of tealet_stub_new() and tealet_stub_run()
+ * is exactly the same as tealet_new()
+ */
+TEALET_API
+int tealet_stub_run(tealet_t *stub, tealet_run_t run, void *run_arg);
+
+#endif /* _TEALET_H_ */


More information about the pypy-commit mailing list