Author: Armin Rigo <[email protected]>
Branch: release-2.0.x
Changeset: r64161:1a64ef0e9672
Date: 2013-05-15 15:58 +0200
http://bitbucket.org/pypy/pypy/changeset/1a64ef0e9672/

Log:    callback-stacklet: merge it manually in release-2.0.x.

diff --git a/rpython/memory/gctransform/asmgcroot.py 
b/rpython/memory/gctransform/asmgcroot.py
--- a/rpython/memory/gctransform/asmgcroot.py
+++ b/rpython/memory/gctransform/asmgcroot.py
@@ -170,12 +170,57 @@
             jit2gc = gctransformer.translator._jit2gc
             self.frame_tid = jit2gc['frame_tid']
         self.gctransformer = gctransformer
+        #
+        # unless overridden in need_thread_support():
+        self.belongs_to_current_thread = lambda framedata: True
 
     def need_stacklet_support(self, gctransformer, getfn):
+        from rpython.annotator import model as annmodel
+        from rpython.rlib import _stacklet_asmgcc
         # stacklet support: BIG HACK for rlib.rstacklet
-        from rpython.rlib import _stacklet_asmgcc
         _stacklet_asmgcc._asmstackrootwalker = self     # as a global! argh
         _stacklet_asmgcc.complete_destrptr(gctransformer)
+        #
+        def gc_detach_callback_pieces():
+            anchor = llmemory.cast_ptr_to_adr(gcrootanchor)
+            result = llmemory.NULL
+            framedata = anchor.address[1]
+            while framedata != anchor:
+                next = framedata.address[1]
+                if self.belongs_to_current_thread(framedata):
+                    # detach it
+                    prev = framedata.address[0]
+                    prev.address[1] = next
+                    next.address[0] = prev
+                    # update the global stack counter
+                    rffi.stackcounter.stacks_counter -= 1
+                    # reattach framedata into the singly-linked list 'result'
+                    framedata.address[0] = rffi.cast(llmemory.Address, -1)
+                    framedata.address[1] = result
+                    result = framedata
+                framedata = next
+            return result
+        #
+        def gc_reattach_callback_pieces(pieces):
+            anchor = llmemory.cast_ptr_to_adr(gcrootanchor)
+            while pieces != llmemory.NULL:
+                framedata = pieces
+                pieces = pieces.address[1]
+                # attach 'framedata' into the normal doubly-linked list
+                following = anchor.address[1]
+                following.address[0] = framedata
+                framedata.address[1] = following
+                anchor.address[1] = framedata
+                framedata.address[0] = anchor
+                # update the global stack counter
+                rffi.stackcounter.stacks_counter += 1
+        #
+        s_addr = annmodel.SomeAddress()
+        s_None = annmodel.s_None
+        self.gc_detach_callback_pieces_ptr = getfn(gc_detach_callback_pieces,
+                                                   [], s_addr)
+        self.gc_reattach_callback_pieces_ptr=getfn(gc_reattach_callback_pieces,
+                                                   [s_addr], s_None)
 
     def need_thread_support(self, gctransformer, getfn):
         # Threads supported "out of the box" by the rest of the code.
@@ -227,6 +272,7 @@
             stack_stop  = llop.stack_current(llmemory.Address)
             return (stack_start <= framedata <= stack_stop or
                     stack_start >= framedata >= stack_stop)
+        self.belongs_to_current_thread = belongs_to_current_thread
 
         def thread_before_fork():
             # before fork(): collect all ASM_FRAMEDATA structures that do
diff --git a/rpython/memory/gctransform/framework.py 
b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -800,6 +800,21 @@
     def gct_gc_adr_of_root_stack_top(self, hop):
         self._gc_adr_of_gcdata_attr(hop, 'root_stack_top')
 
+    def gct_gc_detach_callback_pieces(self, hop):
+        op = hop.spaceop
+        assert len(op.args) == 0
+        hop.genop("direct_call",
+                  [self.root_walker.gc_detach_callback_pieces_ptr],
+                  resultvar=op.result)
+
+    def gct_gc_reattach_callback_pieces(self, hop):
+        op = hop.spaceop
+        assert len(op.args) == 1
+        hop.genop("direct_call",
+                  [self.root_walker.gc_reattach_callback_pieces_ptr,
+                   op.args[0]],
+                  resultvar=op.result)
+
     def gct_gc_shadowstackref_new(self, hop):
         op = hop.spaceop
         livevars = self.push_roots(hop)
diff --git a/rpython/rlib/_stacklet_asmgcc.py b/rpython/rlib/_stacklet_asmgcc.py
--- a/rpython/rlib/_stacklet_asmgcc.py
+++ b/rpython/rlib/_stacklet_asmgcc.py
@@ -32,6 +32,7 @@
             if not p.handle:
                 return False
             self.context = llmemory.cast_ptr_to_adr(p.handle)
+            self.next_callback_piece = p.callback_pieces
             anchor = p.anchor
             del p
             self.curframe = lltype.malloc(WALKFRAME, flavor='raw')
@@ -50,11 +51,19 @@
             retaddraddr = self.translateptr(retaddraddr)
             curframe.frame_address = retaddraddr.address[0]
 
-        def teardown(self):
-            lltype.free(self.curframe, flavor='raw')
-            lltype.free(self.otherframe, flavor='raw')
-            self.context = llmemory.NULL
-            return llmemory.NULL
+        def fetch_next_stack_piece(self):
+            if self.next_callback_piece == llmemory.NULL:
+                lltype.free(self.curframe, flavor='raw')
+                lltype.free(self.otherframe, flavor='raw')
+                self.context = llmemory.NULL
+                return False
+            else:
+                anchor = self.next_callback_piece
+                nextaddr = anchor + sizeofaddr
+                nextaddr = self.translateptr(nextaddr)
+                self.next_callback_piece = nextaddr.address[0]
+                self.fill_initial_frame(self.curframe, anchor)
+                return True
 
         def next(self, obj, prev):
             #
@@ -117,7 +126,10 @@
                                                               location)
                 # ^^^ non-translated
                 if caller.frame_address == llmemory.NULL:
-                    return self.teardown()    # completely done with this stack
+                    # completely done with this piece of stack
+                    if not self.fetch_next_stack_piece():
+                        return llmemory.NULL
+                    continue
                 #
                 self.otherframe = callee
                 self.curframe = caller
@@ -154,6 +166,7 @@
 SUSPSTACK = lltype.GcStruct('SuspStack',
                             ('handle', _c.handle),
                             ('anchor', llmemory.Address),
+                            ('callback_pieces', llmemory.Address),
                             rtti=True)
 NULL_SUSPSTACK = lltype.nullptr(SUSPSTACK)
 CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
@@ -185,6 +198,7 @@
     # stacklet with stacklet_new().  If this call fails, then we
     # are just returning NULL.
     _stack_just_closed()
+    #
     return _c.new(gcrootfinder.newthrd, llhelper(_c.run_fn, _new_runfn),
                   llmemory.NULL)
 
@@ -252,14 +266,36 @@
         newsuspstack.handle = _c.null_handle
         self.suspstack = newsuspstack
         # Invoke '_new_callback' by closing the stack
+        #
+        callback_pieces = llop.gc_detach_callback_pieces(llmemory.Address)
+        newsuspstack.callback_pieces = callback_pieces
+        #
         h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _new_callback),
                                 alternateanchor)
+        #
+        llop.gc_reattach_callback_pieces(lltype.Void, callback_pieces)
         return self.get_result_suspstack(h)
 
     def switch(self, suspstack):
+        # Immediately before the switch, 'suspstack' describes the suspended
+        # state of the *target* of the switch.  Then it is theoretically
+        # freed.  In fact what occurs is that we reuse the same 'suspstack'
+        # object in the target, just after the switch, to store the
+        # description of where we came from.  Then that "other" 'suspstack'
+        # object is returned.
         self.suspstack = suspstack
+        #
+        callback_pieces = llop.gc_detach_callback_pieces(llmemory.Address)
+        old_callback_pieces = suspstack.callback_pieces
+        suspstack.callback_pieces = callback_pieces
+        #
         h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _switch_callback),
                                 alternateanchor)
+        #
+        llop.gc_reattach_callback_pieces(lltype.Void, callback_pieces)
+        if not h:
+            self.suspstack.callback_pieces = old_callback_pieces
+        #
         return self.get_result_suspstack(h)
 
     def attach_handle_on_suspstack(self, handle):
diff --git a/rpython/rlib/test/test_rstacklet.py 
b/rpython/rlib/test/test_rstacklet.py
--- a/rpython/rlib/test/test_rstacklet.py
+++ b/rpython/rlib/test/test_rstacklet.py
@@ -82,6 +82,29 @@
                 return True
         return False
 
+    @here_is_a_test
+    def test_c_callback(self):
+        #
+        self.steps = [0]
+        self.main_h = self.sthread.new(cb_stacklet_callback, llmemory.NULL)
+        self.steps.append(2)
+        call_qsort_rec(10)
+        self.steps.append(9)
+        assert not self.sthread.is_empty_handle(self.main_h)
+        self.main_h = self.sthread.switch(self.main_h)
+        assert self.sthread.is_empty_handle(self.main_h)
+        #
+        # check that self.steps == [0,1,2, 3,4,5,6, 3,4,5,6, 3,4,5,6,..., 9]
+        print self.steps
+        expected = 0
+        assert self.steps[-1] == 9
+        for i in range(len(self.steps)-1):
+            if expected == 7:
+                expected = 3
+            assert self.steps[i] == expected
+            expected += 1
+        assert expected == 7
+
 
 class FooObj:
     def __init__(self, n, d, next=None):
@@ -211,6 +234,43 @@
     print "LEAVING %d to go to %d" % (self.n, n)
     return h
 
+QSORT_CALLBACK_PTR = lltype.Ptr(lltype.FuncType(
+    [llmemory.Address, llmemory.Address], rffi.INT))
+qsort = rffi.llexternal('qsort',
+                        [llmemory.Address, rffi.SIZE_T, rffi.SIZE_T,
+                         QSORT_CALLBACK_PTR],
+                        lltype.Void)
+def cb_compare_callback(a, b):
+    runner.steps.append(3)
+    assert not runner.sthread.is_empty_handle(runner.main_h)
+    runner.main_h = runner.sthread.switch(runner.main_h)
+    assert not runner.sthread.is_empty_handle(runner.main_h)
+    runner.steps.append(6)
+    return rffi.cast(rffi.INT, 1)
+def cb_stacklet_callback(h, arg):
+    runner.steps.append(1)
+    while True:
+        assert not runner.sthread.is_empty_handle(h)
+        h = runner.sthread.switch(h)
+        assert not runner.sthread.is_empty_handle(h)
+        if runner.steps[-1] == 9:
+            return h
+        runner.steps.append(4)
+        rgc.collect()
+        runner.steps.append(5)
+class GcObject(object):
+    num = 1234
+def call_qsort_rec(r):
+    if r > 0:
+        g = GcObject()
+        g.num += r
+        call_qsort_rec(r - 1)
+        assert g.num == 1234 + r
+    else:
+        raw = llmemory.raw_malloc(5)
+        qsort(raw, 5, 1, cb_compare_callback)
+        llmemory.raw_free(raw)
+
 
 def entry_point(argv):
     seed = 0
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -886,6 +886,11 @@
     def op_gc_stack_bottom(self):
         pass       # marker for trackgcroot.py
 
+    def op_gc_detach_callback_pieces(self):
+        raise NotImplementedError("gc_detach_callback_pieces")
+    def op_gc_reattach_callback_pieces(self):
+        raise NotImplementedError("gc_reattach_callback_pieces")
+
     def op_gc_shadowstackref_new(self):   # stacklet+shadowstack
         raise NotImplementedError("gc_shadowstackref_new")
     def op_gc_shadowstackref_context(self):
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -516,6 +516,10 @@
     'gc_asmgcroot_static':  LLOp(sideeffects=False),
     'gc_stack_bottom':      LLOp(canrun=True),
 
+    # for stacklet+asmgcroot support
+    'gc_detach_callback_pieces': LLOp(),
+    'gc_reattach_callback_pieces': LLOp(),
+
     # for stacklet+shadowstack support
     'gc_shadowstackref_new':      LLOp(canmallocgc=True),
     'gc_shadowstackref_context':  LLOp(),
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to