[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�n Valur J�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