def annotate_walker_functions(self, getfn): self.incr_stack_ptr = getfn(self.root_walker.incr_stack, [annmodel.SomeInteger()], SomeAddress(), inline=True) self.decr_stack_ptr = getfn(self.root_walker.decr_stack, [annmodel.SomeInteger()], SomeAddress(), inline=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 _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 = 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_stacklet_support(self, gctransformer, getfn): shadow_stack_pool = self.shadow_stack_pool SHADOWSTACKREF = get_shadowstackref(self, gctransformer) def gc_shadowstackref_new(): ssref = shadow_stack_pool.allocate(SHADOWSTACKREF) return lltype.cast_opaque_ptr(llmemory.GCREF, ssref) def gc_shadowstackref_context(gcref): ssref = lltype.cast_opaque_ptr(lltype.Ptr(SHADOWSTACKREF), gcref) return ssref.context def gc_save_current_state_away(gcref, ncontext): ssref = lltype.cast_opaque_ptr(lltype.Ptr(SHADOWSTACKREF), gcref) shadow_stack_pool.save_current_state_away(ssref, ncontext) def gc_forget_current_state(): shadow_stack_pool.forget_current_state() def gc_restore_state_from(gcref): ssref = lltype.cast_opaque_ptr(lltype.Ptr(SHADOWSTACKREF), gcref) shadow_stack_pool.restore_state_from(ssref) def gc_start_fresh_new_state(): shadow_stack_pool.start_fresh_new_state() s_gcref = SomePtr(llmemory.GCREF) s_addr = SomeAddress() self.gc_shadowstackref_new_ptr = getfn(gc_shadowstackref_new, [], s_gcref, minimal_transform=False) self.gc_shadowstackref_context_ptr = getfn(gc_shadowstackref_context, [s_gcref], s_addr, inline=True) self.gc_save_current_state_away_ptr = getfn(gc_save_current_state_away, [s_gcref, s_addr], annmodel.s_None, inline=True) self.gc_forget_current_state_ptr = getfn(gc_forget_current_state, [], annmodel.s_None, inline=True) self.gc_restore_state_from_ptr = getfn(gc_restore_state_from, [s_gcref], annmodel.s_None, inline=True) self.gc_start_fresh_new_state_ptr = getfn(gc_start_fresh_new_state, [], annmodel.s_None, inline=True)
def test_rtype_nongc_object(): class TestClass(object): _alloc_flavor_ = "raw" def __init__(self, a): self.a = a def method1(self): return self.a def malloc_and_free(a): ci = TestClass(a) b = ci.method1() free_non_gc_object(ci) return b a = RPythonAnnotator() #does not raise: s = a.build_types(malloc_and_free, [SomeAddress()]) assert isinstance(s, SomeAddress) rtyper = RPythonTyper(a) rtyper.specialize()
def ann_cast_int_to_adr(s): return SomeAddress()
def ann_cast_ptr_to_adr(s): from rpython.rtyper.llannotation import SomeInteriorPtr assert not isinstance(s, SomeInteriorPtr) return SomeAddress()
def compute_annotation(self): from rpython.rtyper.llannotation import SomeAddress return SomeAddress()
def ann_raw_malloc(s_size, s_zero=None): assert isinstance(s_size, SomeInteger) # XXX add noneg...? assert s_zero is None or isinstance(s_zero, SomeBool) return SomeAddress()
def need_thread_support(self, gctransformer, getfn): # Threads supported "out of the box" by the rest of the code. # The whole code in this function is only there to support # fork()ing in a multithreaded process :-( # For this, we need to handle gc_thread_start and gc_thread_die # to record the mapping {thread_id: stack_start}, and # gc_thread_before_fork and gc_thread_after_fork to get rid of # all ASM_FRAMEDATA structures that do no belong to the current # thread after a fork(). from rpython.rlib import rthread from rpython.memory.support import AddressDict from rpython.memory.support import copy_without_null_values from rpython.annotator import model as annmodel gcdata = self.gcdata def get_aid(): """Return the thread identifier, cast to an (opaque) address.""" return llmemory.cast_int_to_adr(rthread.get_ident()) def thread_start(): value = llmemory.cast_int_to_adr(llop.stack_current(lltype.Signed)) gcdata.aid2stack.setitem(get_aid(), value) thread_start._always_inline_ = True def thread_setup(): gcdata.aid2stack = AddressDict() gcdata.dead_threads_count = 0 # to also register the main thread's stack thread_start() thread_setup._always_inline_ = True def thread_die(): gcdata.aid2stack.setitem(get_aid(), llmemory.NULL) # from time to time, rehash the dictionary to remove # old NULL entries gcdata.dead_threads_count += 1 if (gcdata.dead_threads_count & 511) == 0: copy = copy_without_null_values(gcdata.aid2stack) gcdata.aid2stack.delete() gcdata.aid2stack = copy def belongs_to_current_thread(framedata): # xxx obscure: the answer is Yes if, as a pointer, framedata # lies between the start of the current stack and the top of it. stack_start = gcdata.aid2stack.get(get_aid(), llmemory.NULL) ll_assert(stack_start != llmemory.NULL, "current thread not found in gcdata.aid2stack!") stack_stop = llmemory.cast_int_to_adr( llop.stack_current(lltype.Signed)) 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 # not belong to the current thread, and move them out of the # way, i.e. out of the main circular doubly linked list. detached_pieces = llmemory.NULL anchor = llmemory.cast_ptr_to_adr(gcrootanchor) initialframedata = anchor.address[1] while initialframedata != anchor: # while we have not looped back if not belongs_to_current_thread(initialframedata): # Unlink it prev = initialframedata.address[0] next = initialframedata.address[1] prev.address[1] = next next.address[0] = prev # Link it to the singly linked list 'detached_pieces' initialframedata.address[0] = detached_pieces detached_pieces = initialframedata rffi.stackcounter.stacks_counter -= 1 # Then proceed to the next piece of stack initialframedata = initialframedata.address[1] return detached_pieces def thread_after_fork(result_of_fork, detached_pieces): if result_of_fork == 0: # We are in the child process. Assumes that only the # current thread survived. All the detached_pieces # are pointers in other stacks, so have likely been # freed already by the multithreaded library. # Nothing more for us to do. pass else: # We are still in the parent process. The fork() may # have succeeded or not, but that's irrelevant here. # We need to reattach the detached_pieces now, to the # circular doubly linked list at 'gcrootanchor'. The # order is not important. anchor = llmemory.cast_ptr_to_adr(gcrootanchor) while detached_pieces != llmemory.NULL: reattach = detached_pieces detached_pieces = detached_pieces.address[0] a_next = anchor.address[1] reattach.address[0] = anchor reattach.address[1] = a_next anchor.address[1] = reattach a_next.address[0] = reattach rffi.stackcounter.stacks_counter += 1 self.thread_setup = thread_setup self.thread_start_ptr = getfn(thread_start, [], annmodel.s_None, inline=True) self.thread_die_ptr = getfn(thread_die, [], annmodel.s_None) self.thread_before_fork_ptr = getfn(thread_before_fork, [], SomeAddress()) self.thread_after_fork_ptr = getfn( thread_after_fork, [annmodel.SomeInteger(), SomeAddress()], annmodel.s_None) # # check that the order of the need_*() is correct for us: if we # need both threads and stacklets, need_thread_support() must be # called first, to initialize self.belongs_to_current_thread. assert not hasattr(self, 'gc_detach_callback_pieces_ptr')
def need_thread_support(self, gctransformer, getfn): from rpython.rlib import rthread # xxx fish gcdata = self.gcdata # the interfacing between the threads and the GC is done via # two completely ad-hoc operations at the moment: # gc_thread_run and gc_thread_die. See docstrings below. shadow_stack_pool = self.shadow_stack_pool SHADOWSTACKREF = get_shadowstackref(self, gctransformer) # this is a dict {tid: SHADOWSTACKREF}, where the tid for the # current thread may be missing so far gcdata.thread_stacks = None shadow_stack_pool.has_threads = True # Return the thread identifier, as an integer. get_tid = rthread.get_ident def thread_setup(): tid = get_tid() gcdata.main_tid = tid gcdata.active_tid = tid def thread_run(): """Called whenever the current thread (re-)acquired the GIL. This should ensure that the shadow stack installed in gcdata.root_stack_top/root_stack_base is the one corresponding to the current thread. No GC operation here, e.g. no mallocs or storing in a dict! Note that here specifically we don't call rthread.get_ident(), but rthread.get_or_make_ident(). We are possibly in a fresh new thread, so we need to be careful. """ tid = rthread.get_or_make_ident() if gcdata.active_tid != tid: switch_shadow_stacks(tid) def thread_die(): """Called just before the final GIL release done by a dying thread. After a thread_die(), no more gc operation should occur in this thread. """ tid = get_tid() if tid == gcdata.main_tid: return # ignore calls to thread_die() in the main thread # (which can occur after a fork()). # we need to switch somewhere else, so go to main_tid gcdata.active_tid = gcdata.main_tid thread_stacks = gcdata.thread_stacks new_ref = thread_stacks[gcdata.active_tid] try: del thread_stacks[tid] except KeyError: pass # no more GC operation from here -- switching shadowstack! shadow_stack_pool.forget_current_state() shadow_stack_pool.restore_state_from(new_ref) def switch_shadow_stacks(new_tid): # we have the wrong shadowstack right now, but it should not matter thread_stacks = gcdata.thread_stacks try: if thread_stacks is None: gcdata.thread_stacks = thread_stacks = {} raise KeyError new_ref = thread_stacks[new_tid] except KeyError: new_ref = lltype.nullptr(SHADOWSTACKREF) try: old_ref = thread_stacks[gcdata.active_tid] except KeyError: # first time we ask for a SHADOWSTACKREF for this active_tid old_ref = shadow_stack_pool.allocate(SHADOWSTACKREF) thread_stacks[gcdata.active_tid] = old_ref # # no GC operation from here -- switching shadowstack! shadow_stack_pool.save_current_state_away(old_ref) if new_ref: shadow_stack_pool.restore_state_from(new_ref) else: shadow_stack_pool.start_fresh_new_state() # done # gcdata.active_tid = new_tid switch_shadow_stacks._dont_inline_ = True def thread_after_fork(result_of_fork, opaqueaddr): # we don't need a thread_before_fork in this case, so # opaqueaddr == NULL. This is called after fork(). if result_of_fork == 0: # We are in the child process. Assumes that only the # current thread survived, so frees the shadow stacks # of all the other ones. gcdata.thread_stacks = None # Finally, reset the stored thread IDs, in case it # changed because of fork(). Also change the main # thread to the current one (because there is not any # other left). tid = get_tid() gcdata.main_tid = tid gcdata.active_tid = tid self.thread_setup = thread_setup self.thread_run_ptr = getfn(thread_run, [], annmodel.s_None, minimal_transform=False) self.thread_die_ptr = getfn(thread_die, [], annmodel.s_None, minimal_transform=False) # no thread_before_fork_ptr here self.thread_after_fork_ptr = getfn( thread_after_fork, [annmodel.SomeInteger(), SomeAddress()], annmodel.s_None, minimal_transform=False)
def ann_raw_malloc(s_size): assert isinstance(s_size, SomeInteger) # XXX add noneg...? return SomeAddress()