[pypy-commit] pypy stacklet: Rewrite the logic to simplify it and reduce the allocations.

arigo noreply at buildbot.pypy.org
Sun Aug 7 15:11:38 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46346:78fa1d3f1f0b
Date: 2011-08-07 15:03 +0200
http://bitbucket.org/pypy/pypy/changeset/78fa1d3f1f0b/

Log:	Rewrite the logic to simplify it and reduce the allocations.

diff --git a/pypy/module/_stacklet/interp_stacklet.py b/pypy/module/_stacklet/interp_stacklet.py
--- a/pypy/module/_stacklet/interp_stacklet.py
+++ b/pypy/module/_stacklet/interp_stacklet.py
@@ -1,6 +1,7 @@
 import sys
 from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.rlib import rstacklet, jit
+from pypy.rlib import jit
+from pypy.rlib.rstacklet import StackletThread
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.executioncontext import ExecutionContext
@@ -8,48 +9,40 @@
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app
 
-NULLHANDLE = lltype.nullptr(rstacklet.handle.TO)
 
+class SThread(StackletThread):
 
-class SThread(object):
     def __init__(self, space, ec):
+        StackletThread.__init__(self, space.config)
         w_module = space.getbuiltinmodule('_stacklet')
         self.space = space
         self.ec = ec
         self.w_error = space.getattr(w_module, space.wrap('error'))
         self.pending_exception = None
         self.main_stacklet = None
-        self.thrd = rstacklet.newthread()
-        if not self.thrd:
-            raise MemoryError
 
     def __del__(self):
-        thrd = self.thrd
-        if thrd:
-            if self.main_stacklet is not None:
-                self.main_stacklet.__del__()
-            self.thrd = lltype.nullptr(rstacklet.thread_handle.TO)
-            rstacklet.deletethread(thrd)
+        if self.main_stacklet is not None:
+            self.main_stacklet.__del__()
+        StackletThread.__del__(self)
 
     def new_stacklet_object(self, h):
-        if self.pending_exception is not None:
+        if self.pending_exception is None:
+            if self.is_empty_handle(h):
+                return self.space.w_None
+            else:
+                return self.space.wrap(W_Stacklet(self, h))
+        else:
             e = self.pending_exception
             self.pending_exception = None
+            if not self.is_empty_handle(h):
+                self.destroy(h)
             if we_are_translated():
                 raise e
             else:
                 tb = self.pending_tb
                 del self.pending_tb
                 raise e.__class__, e, tb
-        if not h:
-            start_state.sthread = None
-            start_state.w_callable = None
-            start_state.args = None
-            raise MemoryError
-        elif rstacklet.is_empty_handle(h):
-            return self.space.w_None
-        else:
-            return self.space.wrap(W_Stacklet(self, h))
 
 ExecutionContext.stacklet_thread = None
 
@@ -62,14 +55,13 @@
     def __del__(self):
         h = self.h
         if h:
-            self.h = NULLHANDLE
-            space = self.sthread.space
-            rstacklet.destroy(space.config, self.sthread.thrd, h)
+            self.h = self.get_null_handle()
+            self.sthread.destroy(h)
 
     def consume_handle(self):
         h = self.h
         if h:
-            self.h = NULLHANDLE
+            self.h = self.get_null_handle()
             if self is self.sthread.main_stacklet:
                 self.sthread.main_stacklet = None
             return h
@@ -84,7 +76,7 @@
         sthread = self.sthread
         ec = sthread.ec
         saved_frame_top = ec.topframeref
-        h = rstacklet.switch(space.config, sthread.thrd, h)
+        h = sthread.switch(h)
         ec.topframeref = saved_frame_top
         return sthread.new_stacklet_object(h)
 
@@ -148,7 +140,6 @@
     start_state.args = __args__
     saved_frame_top = ec.topframeref
     ec.topframeref = jit.vref_None
-    h = rstacklet.new(space.config, sthread.thrd, new_stacklet_callback,
-                      lltype.nullptr(rffi.VOIDP.TO))
+    h = sthread.new(new_stacklet_callback)
     ec.topframeref = saved_frame_top
     return sthread.new_stacklet_object(h)
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/_rffi_stacklet.py
copy from pypy/rlib/rstacklet.py
copy to pypy/rlib/_rffi_stacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/_rffi_stacklet.py
@@ -2,15 +2,6 @@
 from pypy.tool.autopath import pypydir
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.annlowlevel import llhelper
-
-###
-### Note: stacklets do not reliably work on top of CPython, but well,
-### they seem to work fine after being translated...  This is due
-### to the fact that on CPython, you get strange effects because the
-### PyThreadState is not explicitly handled when we start a new
-### stacklet or switch to another one, notably the 'frame' field.
-###
 
 
 cdir = py.path.local(pypydir) / 'translator' / 'c'
@@ -31,10 +22,12 @@
 handle = rffi.COpaquePtr(typedef='stacklet_handle', compilation_info=eci)
 thread_handle = rffi.COpaquePtr(typedef='stacklet_thread_handle',
                                 compilation_info=eci)
-run_fn = lltype.Ptr(lltype.FuncType([handle, rffi.VOIDP], handle))
+run_fn = lltype.Ptr(lltype.FuncType([handle, llmemory.Address], handle))
 
 # ----- constants -----
 
+null_handle = lltype.nullptr(handle.TO)
+
 def is_empty_handle(h):
     return rffi.cast(lltype.Signed, h) == -1
 
@@ -43,38 +36,11 @@
 newthread = llexternal('stacklet_newthread', [], thread_handle)
 deletethread = llexternal('stacklet_deletethread',[thread_handle], lltype.Void)
 
-_new = llexternal('stacklet_new', [thread_handle, run_fn, rffi.VOIDP],
-                  handle)
-_switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
-_destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
+new = llexternal('stacklet_new', [thread_handle, run_fn, llmemory.Address],
+                 handle)
+switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
+destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
 
 _translate_pointer = llexternal("_stacklet_translate_pointer",
                                 [handle, llmemory.Address],
                                 llmemory.Address)
-
-# ____________________________________________________________
-
-def getgcclass(config):
-    if (config is None or
-        config.translation.gc in ('ref', 'boehm', 'none')):   # for tests
-        gcrootfinder = 'n/a'
-    else:
-        gcrootfinder = config.translation.gcrootfinder
-    gcrootfinder = gcrootfinder.replace('/', '_')
-    module = __import__('pypy.rlib._stacklet_%s' % gcrootfinder,
-                        None, None, ['__doc__'])
-    return module.StackletGcRootFinder
-getgcclass._annspecialcase_ = 'specialize:memo'
-
-def new(config, thrd, runfn, arg):
-    c = getgcclass(config)
-    return c.new(thrd, llhelper(run_fn, runfn), arg)
-new._annspecialcase_ = 'specialize:arg(2)'
-
-def switch(config, thrd, h):
-    c = getgcclass(config)
-    return c.switch(thrd, h)
-
-def destroy(config, thrd, h):
-    c = getgcclass(config)
-    c.destroy(thrd, h)
diff --git a/pypy/rlib/_stacklet_asmgcc.py b/pypy/rlib/_stacklet_asmgcc.py
--- a/pypy/rlib/_stacklet_asmgcc.py
+++ b/pypy/rlib/_stacklet_asmgcc.py
@@ -1,4 +1,4 @@
-from pypy.rlib import rstacklet
+from pypy.rlib import _rffi_stacklet as _c
 from pypy.rlib.debug import ll_assert
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.lltypesystem.lloperation import llop
@@ -35,12 +35,7 @@
             del p
             self.curframe = lltype.malloc(WALKFRAME, flavor='raw')
             self.otherframe = lltype.malloc(WALKFRAME, flavor='raw')
-            initialframedata = anchor[1]
-            ll_assert(initialframedata != llmemory.cast_ptr_to_adr(anchor),
-                      "no anchored stacklet stack found")
-            ll_assert(initialframedata == anchor[0],
-                      "more than one anchored stacklet stack found")
-            self.fill_initial_frame(self.curframe, initialframedata)
+            self.fill_initial_frame(self.curframe, anchor)
             return True
 
         def fill_initial_frame(self, curframe, initialframedata):
@@ -57,7 +52,7 @@
         def teardown(self):
             lltype.free(self.curframe, flavor='raw')
             lltype.free(self.otherframe, flavor='raw')
-            self.context = lltype.nullptr(rstacklet.handle.TO)
+            self.context = lltype.nullptr(_c.handle.TO)
             return llmemory.NULL
 
         def next(self, obj, prev):
@@ -116,7 +111,7 @@
                 # loop back
 
         def translateptr(self, addr):
-            return rstacklet._translate_pointer(self.context, addr)
+            return _c._translate_pointer(self.context, addr)
 
     _stackletrootwalker = StackletRootWalker()
     return _stackletrootwalker
@@ -128,110 +123,150 @@
     return stackletrootwalker.next(obj, prev)
 
 
-ASM_FRAMEDATA_HEAD_PTR = lltype.Ptr(lltype.FixedSizeArray(llmemory.Address, 2))
 SUSPSTACK = lltype.GcStruct('SuspStack',
-                            ('handle', rstacklet.handle),
-                            ('anchor', ASM_FRAMEDATA_HEAD_PTR),
-                            ('my_index', lltype.Signed),
-                            ('next_unused', lltype.Signed),
+                            ('handle', _c.handle),
+                            ('anchor', llmemory.Address),
                             rtti=True)
+NULL_SUSPSTACK = lltype.nullptr(SUSPSTACK)
 CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
                                   llmemory.Address)
 customtraceptr = llhelper(lltype.Ptr(CUSTOMTRACEFUNC), customtrace)
 lltype.attachRuntimeTypeInfo(SUSPSTACK, customtraceptr=customtraceptr)
-NULL_SUSPSTACK = lltype.Ptr(SUSPSTACK)
 
+ASM_FRAMEDATA_HEAD_PTR = lltype.Ptr(lltype.ForwardReference())
+ASM_FRAMEDATA_HEAD_PTR.TO.become(lltype.Struct('ASM_FRAMEDATA_HEAD',
+        ('prev', ASM_FRAMEDATA_HEAD_PTR),
+        ('next', ASM_FRAMEDATA_HEAD_PTR)
+    ))
+alternateanchor = lltype.malloc(ASM_FRAMEDATA_HEAD_PTR.TO,
+                                immortal=True)
+alternateanchor.prev = alternateanchor
+alternateanchor.next = alternateanchor
 
-class SuspendedStacks:
-
-    def __init__(self):
-        self.lst = []
-        self.first_unused = -1
-        self.current_index = -1
-
-    def acquire(self):
-        if self.first_unused == -1:
-            p = lltype.malloc(SUSPSTACK)
-            p.handle = lltype.nullptr(rstacklet.handle.TO)
-            p.my_index = len(self.lst)
-            p.next_unused = -42000000
-            p.anchor = lltype.malloc(ASM_FRAMEDATA_HEAD_PTR.TO, flavor='raw',
-                                     track_allocation=False)
-            self.lst.append(p)
-        else:
-            p = self.lst[self.first_unused]
-            self.first_unused = p.next_unused
-        p.anchor[0] = p.anchor[1] = llmemory.cast_ptr_to_adr(p.anchor)
-        self.current_index = p.my_index
-        return p
-
-    def release(self, p):
-        p.next_unused = self.first_unused
-        self.first_unused = p.my_index
-
-suspendedstacks = SuspendedStacks()
-
-FUNCNOARG_P = lltype.Ptr(lltype.FuncType([], rstacklet.handle))
-
+FUNCNOARG_P = lltype.Ptr(lltype.FuncType([], _c.handle))
 pypy_asm_stackwalk2 = rffi.llexternal('pypy_asm_stackwalk',
                                       [FUNCNOARG_P,
                                        ASM_FRAMEDATA_HEAD_PTR],
-                                      rstacklet.handle, sandboxsafe=True,
+                                      _c.handle, sandboxsafe=True,
                                       _nowrapper=True)
 
-def stack_protected_call(callback):
-    p = suspendedstacks.acquire()
-    llop.gc_assume_young_pointers(lltype.Void,
-                                  llmemory.cast_ptr_to_adr(p))
-    r = pypy_asm_stackwalk2(callback, p.anchor)
-    p.handle = lltype.nullptr(rstacklet.handle.TO)
-    suspendedstacks.release(p)
-    return r
-
-def set_handle_on_most_recent(h):
-    index = suspendedstacks.current_index
-    if not h or rstacklet.is_empty_handle(h):
-        assert index == -1
-    else:
-        assert index >= 0
-        suspendedstacks.lst[index].handle = h
-        suspendedstacks.current_index = -1
 
 def _new_callback():
-    h = rstacklet._new(suspendedstacks._thrd,
-                       llhelper(rstacklet.run_fn, _new_runfn),
-                       lltype.nullptr(rffi.VOIDP.TO))
-    set_handle_on_most_recent(h)
+    # Here, we just closed the stack.  Get the stack anchor, store
+    # it in the gcrootfinder.suspstack.anchor, and create a new
+    # stacklet with stacklet_new().  If this call fails, then we
+    # are just returning NULL.
+    _stack_just_closed()
+    return _c.new(gcrootfinder.thrd, llhelper(_c.run_fn, _new_runfn),
+                  llmemory.NULL)
+
+def _stack_just_closed():
+    # Immediately unlink the new stackanchor from the doubly-linked
+    # chained list.  When returning from pypy_asm_stackwalk2, the
+    # assembler code will try to unlink it again, which should be
+    # a no-op given that the doubly-linked list is empty.
+    stackanchor = llmemory.cast_ptr_to_adr(alternateanchor.next)
+    gcrootfinder.suspstack.anchor = stackanchor
+    alternateanchor.prev = alternateanchor
+    alternateanchor.next = alternateanchor
+
+def _new_runfn(h, _):
+    # Here, we are in a fresh new stacklet.
+    llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
+    #
+    # There is a fresh suspstack object waiting on the gcrootfinder,
+    # so populate it with data that represents the parent suspended
+    # stacklet and detach the suspstack object from gcrootfinder.
+    suspstack = gcrootfinder.attach_handle_on_suspstack(h)
+    #
+    # Call the main function provided by the (RPython) user.
+    suspstack = gcrootfinder.runfn(suspstack, gcrootfinder.arg)
+    #
+    # Here, suspstack points to the target stacklet to which we want
+    # to jump to next.  Read the 'handle' and forget about the
+    # suspstack object.
+    return _consume_suspstack(suspstack)
+
+def _consume_suspstack(suspstack):
+    h = suspstack.handle
+    ll_assert(bool(h), "_consume_suspstack: null handle")
+    suspstack.handle = _c.null_handle
     return h
 
-def _new_runfn(h, arg):
-    set_handle_on_most_recent(h)
-    r = suspendedstacks._runfn(h, suspendedstacks._arg)
-    llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
-    # ^^^ also, xxx, place it here to prevent the call from being
-    # considered a tail call
-    return r
+def _switch_callback():
+    # Here, we just closed the stack.  Get the stack anchor, store
+    # it in the gcrootfinder.suspstack.anchor, and switch to this
+    # suspstack with stacklet_switch().  If this call fails, then we
+    # are just returning NULL.
+    oldanchor = gcrootfinder.suspstack.anchor
+    _stack_just_closed()
+    h = _consume_suspstack(gcrootfinder.suspstack)
+    #
+    # gcrootfinder.suspstack.anchor is left with the anchor of the
+    # previous place (i.e. before the call to switch()).
+    h2 = _c.switch(gcrootfinder.thrd, h)
+    #
+    if not h2:    # MemoryError: restore
+        gcrootfinder.suspstack.anchor = oldanchor
+        gcrootfinder.suspstack.handle = h
+    return h2
 
-def _switch_callback():
-    h = rstacklet._switch(suspendedstacks._thrd,
-                          suspendedstacks._switchto)
-    set_handle_on_most_recent(h)
-    return h
 
+class StackletGcRootFinder(object):
+    suspstack = NULL_SUSPSTACK
 
-class StackletGcRootFinder:
+    def new(self, thrd, callback, arg):
+        self.thrd = thrd
+        self.runfn = callback
+        self.arg = arg
+        # make a fresh new clean SUSPSTACK
+        newsuspstack = lltype.malloc(SUSPSTACK)
+        newsuspstack.handle = _c.null_handle
+        self.suspstack = newsuspstack
+        # Invoke '_new_callback' by closing the stack
+        h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _new_callback),
+                                alternateanchor)
+        return self.get_result_suspstack(h)
 
-    @staticmethod
-    def new(thrd, runfn, arg):
-        suspendedstacks._thrd = thrd
-        suspendedstacks._runfn = runfn
-        suspendedstacks._arg = arg
-        return stack_protected_call(llhelper(FUNCNOARG_P, _new_callback))
+    def switch(self, thrd, suspstack):
+        self.thrd = thrd
+        self.suspstack = suspstack
+        h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _switch_callback),
+                                alternateanchor)
+        return self.get_result_suspstack(h)
 
-    @staticmethod
-    def switch(thrd, h):
-        suspendedstacks._thrd = thrd
-        suspendedstacks._switchto = h
-        return stack_protected_call(llhelper(FUNCNOARG_P, _switch_callback))
+    def attach_handle_on_suspstack(self, handle):
+        s = self.suspstack
+        self.suspstack = NULL_SUSPSTACK
+        ll_assert(bool(s.anchor), "s.anchor should not be null")
+        s.handle = handle
+        llop.gc_assume_young_pointers(lltype.Void, llmemory.cast_ptr_to_adr(s))
+        return s
 
-    destroy = staticmethod(rstacklet._destroy)
+    def get_result_suspstack(self, h):
+        #
+        # Return from a new() or a switch(): 'h' is a handle, possibly
+        # an empty one, that says from where we switched to.
+        if not h:
+            raise MemoryError
+        elif _c.is_empty_handle(h):
+            return NULL_SUSPSTACK
+        else:
+            # This is a return that gave us a real handle.  Store it.
+            return self.attach_handle_on_suspstack(h)
+
+    def destroy(self, thrd, suspstack):
+        h = suspstack.handle
+        a = suspstack.anchor
+        suspstack.handle = _c.null_handle
+        _c.destroy(thrd, h)
+        lltype.free(a, flavor='raw')
+
+    def is_empty_handle(self, suspstack):
+        return not suspstack
+
+    def get_null_handle(self):
+        return lltype.nullptr(SUSPSTACK)
+
+
+gcrootfinder = StackletGcRootFinder()
diff --git a/pypy/rlib/_stacklet_n_a.py b/pypy/rlib/_stacklet_n_a.py
--- a/pypy/rlib/_stacklet_n_a.py
+++ b/pypy/rlib/_stacklet_n_a.py
@@ -1,6 +1,30 @@
-from pypy.rlib import rstacklet
+from pypy.rlib import _rffi_stacklet as _c
+from pypy.rpython.annlowlevel import llhelper
+from pypy.tool.staticmethods import StaticMethods
+
 
 class StackletGcRootFinder:
-    new     = staticmethod(rstacklet._new)
-    switch  = staticmethod(rstacklet._switch)
-    destroy = staticmethod(rstacklet._destroy)
+    __metaclass__ = StaticMethods
+
+    def new(thrd, callback, arg):
+        h = _c.new(thrd, llhelper(_c.run_fn, callback), arg)
+        if not h:
+            raise MemoryError
+        return h
+    new._annspecialcase_ = 'specialize:arg(1)'
+
+    def switch(thrd, h):
+        h = _c.switch(thrd, h)
+        if not h:
+            raise MemoryError
+        return h
+
+    destroy = _c.destroy
+
+    is_empty_handle = _c.is_empty_handle
+
+    def get_null_handle():
+        return _c.null_handle
+
+
+gcrootfinder = StackletGcRootFinder    # class object
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/rstacklet.py
@@ -1,60 +1,40 @@
-import py
-from pypy.tool.autopath import pypydir
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.annlowlevel import llhelper
+from pypy.rlib import _rffi_stacklet as _c
+from pypy.rpython.lltypesystem import lltype, rffi
 
-###
-### Note: stacklets do not reliably work on top of CPython, but well,
-### they seem to work fine after being translated...  This is due
-### to the fact that on CPython, you get strange effects because the
-### PyThreadState is not explicitly handled when we start a new
-### stacklet or switch to another one, notably the 'frame' field.
-###
 
+class StackletThread(object):
 
-cdir = py.path.local(pypydir) / 'translator' / 'c'
+    def __init__(self, config):
+        self._gcrootfinder = _getgcrootfinder(config)
+        self._thrd = _c.newthread()
+        if not self._thrd:
+            raise MemoryError
 
+    def __del__(self):
+        thrd = self._thrd
+        if thrd:
+            self._thrd = lltype.nullptr(_c.thread_handle.TO)
+            _c.deletethread(thrd)
 
-eci = ExternalCompilationInfo(
-    include_dirs = [cdir],
-    includes = ['src/stacklet/stacklet.h'],
-    separate_module_sources = ['#include "src/stacklet/stacklet.c"\n'],
-)
+    def new(self, callback, arg=lltype.nullptr(rffi.VOIDP.TO)):
+        return self._gcrootfinder.new(self._thrd, callback, arg)
+    new._annspecialcase_ = 'specialize:arg(1)'
 
-def llexternal(name, args, result):
-    return rffi.llexternal(name, args, result, compilation_info=eci,
-                           _nowrapper=True)
+    def switch(self, stacklet):
+        return self._gcrootfinder.switch(self._thrd, stacklet)
 
-# ----- types -----
+    def destroy(self, stacklet):
+        self._gcrootfinder.destroy(self._thrd, stacklet)
 
-handle = rffi.COpaquePtr(typedef='stacklet_handle', compilation_info=eci)
-thread_handle = rffi.COpaquePtr(typedef='stacklet_thread_handle',
-                                compilation_info=eci)
-run_fn = lltype.Ptr(lltype.FuncType([handle, rffi.VOIDP], handle))
+    def is_empty_handle(self, stacklet):
+        return self._gcrootfinder.is_empty_handle(stacklet)
 
-# ----- constants -----
-
-def is_empty_handle(h):
-    return rffi.cast(lltype.Signed, h) == -1
-
-# ----- functions -----
-
-newthread = llexternal('stacklet_newthread', [], thread_handle)
-deletethread = llexternal('stacklet_deletethread',[thread_handle], lltype.Void)
-
-_new = llexternal('stacklet_new', [thread_handle, run_fn, rffi.VOIDP],
-                  handle)
-_switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
-_destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
-
-_translate_pointer = llexternal("_stacklet_translate_pointer",
-                                [handle, llmemory.Address],
-                                llmemory.Address)
+    def get_null_handle(self):
+        return self._gcrootfinder.get_null_handle()
 
 # ____________________________________________________________
 
-def getgcclass(config):
+def _getgcrootfinder(config):
     if (config is None or
         config.translation.gc in ('ref', 'boehm', 'none')):   # for tests
         gcrootfinder = 'n/a'
@@ -63,18 +43,5 @@
     gcrootfinder = gcrootfinder.replace('/', '_')
     module = __import__('pypy.rlib._stacklet_%s' % gcrootfinder,
                         None, None, ['__doc__'])
-    return module.StackletGcRootFinder
-getgcclass._annspecialcase_ = 'specialize:memo'
-
-def new(config, thrd, runfn, arg):
-    c = getgcclass(config)
-    return c.new(thrd, llhelper(run_fn, runfn), arg)
-new._annspecialcase_ = 'specialize:arg(2)'
-
-def switch(config, thrd, h):
-    c = getgcclass(config)
-    return c.switch(thrd, h)
-
-def destroy(config, thrd, h):
-    c = getgcclass(config)
-    c.destroy(thrd, h)
+    return module.gcrootfinder
+_getgcrootfinder._annspecialcase_ = 'specialize:memo'
diff --git a/pypy/rlib/test/test_rstacklet.py b/pypy/rlib/test/test_rstacklet.py
--- a/pypy/rlib/test/test_rstacklet.py
+++ b/pypy/rlib/test/test_rstacklet.py
@@ -1,3 +1,4 @@
+import gc
 from pypy.rlib import rstacklet, rrandom
 from pypy.rlib.rarithmetic import intmask
 from pypy.rpython.lltypesystem import lltype, rffi
@@ -10,11 +11,12 @@
     config = None
 
     def init(self, seed):
-        self.thrd = rstacklet.newthread()
+        self.sthread = rstacklet.StackletThread(self.config)
         self.random = rrandom.Random(seed)
 
     def done(self):
-        rstacklet.deletethread(self.thrd)
+        self.sthread = None
+        gc.collect(); gc.collect(); gc.collect()
 
     TESTS = []
     def here_is_a_test(fn, TESTS=TESTS):
@@ -24,10 +26,9 @@
     @here_is_a_test
     def test_new(self):
         print 'start'
-        h = rstacklet.new(self.config, self.thrd, empty_callback,
-                          rffi.cast(rffi.VOIDP, 123))
+        h = self.sthread.new(empty_callback, rffi.cast(rffi.VOIDP, 123))
         print 'end', h
-        assert rstacklet.is_empty_handle(h)
+        assert self.sthread.is_empty_handle(h)
 
     def nextstatus(self, nextvalue):
         print 'expected nextvalue to be %d, got %d' % (nextvalue,
@@ -38,14 +39,14 @@
     @here_is_a_test
     def test_simple_switch(self):
         self.status = 0
-        h = rstacklet.new(self.config, self.thrd, switchbackonce_callback,
-                          rffi.cast(rffi.VOIDP, 321))
-        assert not rstacklet.is_empty_handle(h)
+        h = self.sthread.new(switchbackonce_callback,
+                             rffi.cast(rffi.VOIDP, 321))
+        assert not self.sthread.is_empty_handle(h)
         self.nextstatus(2)
-        h = rstacklet.switch(self.config, runner.thrd, h)
+        h = self.sthread.switch(h)
         self.nextstatus(4)
         print 'end', h
-        assert rstacklet.is_empty_handle(h)
+        assert self.sthread.is_empty_handle(h)
 
     @here_is_a_test
     def test_various_depths(self):
@@ -74,7 +75,7 @@
 class Task:
     def __init__(self, n):
         self.n = n
-        self.h = lltype.nullptr(rstacklet.handle.TO)
+        self.h = runner.sthread.get_null_handle()
         self.lst = []
 
     def withdepth(self, d):
@@ -108,15 +109,14 @@
             if not task.h:
                 # start a new stacklet
                 print "NEW", n
-                h = rstacklet.new(runner.config, runner.thrd,
-                                  variousstackdepths_callback,
-                                  rffi.cast(rffi.VOIDP, n))
+                h = runner.sthread.new(variousstackdepths_callback,
+                                       rffi.cast(rffi.VOIDP, n))
             else:
                 # switch to this stacklet
                 print "switch to", n
                 h = task.h
-                task.h = lltype.nullptr(rstacklet.handle.TO)
-                h = rstacklet.switch(runner.config, runner.thrd, h)
+                task.h = runner.sthread.get_null_handle()
+                h = runner.sthread.switch(h)
 
             print "back in self.n = %d, coming from %d" % (self.n,
                                                            runner.comefrom)
@@ -131,7 +131,7 @@
                 assert not task.h
                 task.h = h
             else:
-                assert rstacklet.is_empty_handle(h)
+                assert runner.sthread.is_empty_handle(h)
             runner.comefrom = -1
             runner.gointo = -1
         assert (res & (res-1)) == 0   # to prevent a tail-call to withdepth()
@@ -150,10 +150,10 @@
     print 'in switchbackonce_callback:', h, arg
     assert rffi.cast(lltype.Signed, arg) == 321
     runner.nextstatus(1)
-    assert not rstacklet.is_empty_handle(h)
-    h = rstacklet.switch(runner.config, runner.thrd, h)
+    assert not runner.sthread.is_empty_handle(h)
+    h = runner.sthread.switch(h)
     runner.nextstatus(3)
-    assert not rstacklet.is_empty_handle(h)
+    assert not runner.sthread.is_empty_handle(h)
     return h
 
 def variousstackdepths_callback(h, arg):
@@ -167,7 +167,7 @@
     assert 0 <= runner.comefrom < 10
     task = runner.tasks[runner.comefrom]
     assert not task.h
-    assert bool(h) and not rstacklet.is_empty_handle(h)
+    assert bool(h) and not runner.sthread.is_empty_handle(h)
     task.h = h
     runner.comefrom = -1
     runner.gointo = -1
@@ -183,8 +183,8 @@
         if h:
             break
 
-    assert not rstacklet.is_empty_handle(h)
-    runner.tasks[n].h = lltype.nullptr(rstacklet.handle.TO)
+    assert not runner.sthread.is_empty_handle(h)
+    runner.tasks[n].h = runner.sthread.get_null_handle()
     runner.comefrom = -42
     runner.gointo = n
     assert runner.nextstep == -1


More information about the pypy-commit mailing list