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