Commits

Maciej Fijalkowski  committed 2bf49cf Merge

merge

  • Participants
  • Parent commits 0bed69c, 152ee9d
  • Branches jitframe-on-heap

Comments (0)

Files changed (20)

File pypy/module/_continuation/interp_continuation.py

         #  - normal:      self.sthread != None, not is_empty_handle(self.h)
         #  - finished:    self.sthread != None, is_empty_handle(self.h)
 
-    def __del__(self):
-        sthread = self.sthread
-        if sthread is not None and not sthread.is_empty_handle(self.h):
-            sthread.destroy(self.h)
-
     def check_sthread(self):
         ec = self.space.getexecutioncontext()
         if ec.stacklet_thread is not self.sthread:

File rpython/memory/gc/base.py

                     break
                 obj = self.run_finalizers.popleft()
                 finalizer = self.getfinalizer(self.get_type_id(obj))
-                finalizer(obj, llmemory.NULL)
+                finalizer(obj)
         finally:
             self.finalizer_lock_count -= 1
 

File rpython/memory/gc/minimark.py

             if not self.is_forwarded(obj):
                 finalizer = self.getlightfinalizer(self.get_type_id(obj))
                 ll_assert(bool(finalizer), "no light finalizer found")
-                finalizer(obj, llmemory.NULL)
+                finalizer(obj)
             else:
                 obj = self.get_forwarding_address(obj)
                 self.old_objects_with_light_finalizers.append(obj)
                 # dying
                 finalizer = self.getlightfinalizer(self.get_type_id(obj))
                 ll_assert(bool(finalizer), "no light finalizer found")
-                finalizer(obj, llmemory.NULL)
+                finalizer(obj)
         self.old_objects_with_light_finalizers.delete()
         self.old_objects_with_light_finalizers = new_objects
 

File rpython/memory/gc/semispace.py

                 new_objects.append(self.get_forwarding_address(obj))
             else:
                 finalizer = self.getfinalizer(self.get_type_id(obj))
-                finalizer(obj, llmemory.NULL)
+                finalizer(obj)
         self.objects_with_light_finalizers.delete()
         self.objects_with_light_finalizers = new_objects
 

File rpython/memory/gctransform/asmgcroot.py

         if self._with_jit:
             jit2gc = gctransformer.translator._jit2gc
             self.frame_tid = jit2gc['frame_tid']
+        self.gctransformer = gctransformer
 
     def need_stacklet_support(self, gctransformer, getfn):
         # 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 need_thread_support(self, gctransformer, getfn):
         # Threads supported "out of the box" by the rest of the code.

File rpython/memory/gctransform/framework.py

         c_type_id = rmodel.inputconst(TYPE_ID, type_id)
         info = self.layoutbuilder.get_info(type_id)
         c_size = rmodel.inputconst(lltype.Signed, info.fixedsize)
-        kind_and_fptr = self.special_funcptr_for_type(TYPE)
-        has_finalizer = (kind_and_fptr is not None and
-                         kind_and_fptr[0] == "finalizer")
-        has_light_finalizer = (kind_and_fptr is not None and
-                               kind_and_fptr[0] == "light_finalizer")
+        fptrs = self.special_funcptr_for_type(TYPE)
+        has_finalizer = "finalizer" in fptrs
+        has_light_finalizer = "light_finalizer" in fptrs
         if has_light_finalizer:
             has_finalizer = True
         c_has_finalizer = rmodel.inputconst(lltype.Bool, has_finalizer)
                   [self.root_walker.gc_shadowstackref_context_ptr, op.args[0]],
                   resultvar=op.result)
 
-    def gct_gc_shadowstackref_destroy(self, hop):
-        op = hop.spaceop
-        hop.genop("direct_call",
-                  [self.root_walker.gc_shadowstackref_destroy_ptr, op.args[0]])
-
     def gct_gc_save_current_state_away(self, hop):
         op = hop.spaceop
         hop.genop("direct_call",
                                             None)
 
     def has_light_finalizer(self, TYPE):
-        special = self.special_funcptr_for_type(TYPE)
-        return special is not None and special[0] == 'light_finalizer'
+        fptrs = self.special_funcptr_for_type(TYPE)
+        return "light_finalizer" in fptrs
 
     def has_custom_trace(self, TYPE):
         rtti = get_rtti(TYPE)
         destrptr = rtti._obj.destructor_funcptr
         DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
         typename = TYPE.__name__
-        def ll_finalizer(addr, ignored):
+        def ll_finalizer(addr):
             v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
             ll_call_destructor(destrptr, v, typename)
-            return llmemory.NULL
         fptr = self.transformer.annotate_finalizer(ll_finalizer,
-                [llmemory.Address, llmemory.Address], llmemory.Address)
-        g = destrptr._obj.graph
-        light = not FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
+                [llmemory.Address], lltype.Void)
+        try:
+            g = destrptr._obj.graph
+            light = not FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
+        except lltype.DelayedPointer:
+            light = False    # XXX bah, too bad
         return fptr, light
 
     def make_custom_trace_funcptr_for_type(self, TYPE):

File rpython/memory/gctransform/shadowstack.py

 from rpython.annotator import model as annmodel
 from rpython.rlib.debug import ll_assert
 from rpython.rlib.nonconst import NonConstant
+from rpython.rlib import rgc
 from rpython.rtyper import rmodel
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rtyper.lltypesystem import lltype, llmemory
             ssref = lltype.cast_opaque_ptr(lltype.Ptr(SHADOWSTACKREF), gcref)
             return ssref.context
 
-        def gc_shadowstackref_destroy(gcref):
-            ssref = lltype.cast_opaque_ptr(lltype.Ptr(SHADOWSTACKREF), gcref)
-            shadow_stack_pool.destroy(ssref)
-
         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)
         self.gc_shadowstackref_context_ptr = getfn(gc_shadowstackref_context,
                                                    [s_gcref], s_addr,
                                                    inline=True)
-        self.gc_shadowstackref_destroy_ptr = getfn(gc_shadowstackref_destroy,
-                                                   [s_gcref], annmodel.s_None,
-                                                   inline=True)
         self.gc_save_current_state_away_ptr = getfn(gc_save_current_state_away,
                                                     [s_gcref, s_addr],
                                                     annmodel.s_None,
         self.gcdata.root_stack_top  = self.unused_full_stack
         self.unused_full_stack = llmemory.NULL
 
-    def destroy(self, shadowstackref):
-        llmemory.raw_free(shadowstackref.base)
-        self._cleanup(shadowstackref)
-
     def _cleanup(self, shadowstackref):
         shadowstackref.base = llmemory.NULL
         shadowstackref.top = llmemory.NULL
     CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
                                       llmemory.Address)
     customtraceptr = llhelper(lltype.Ptr(CUSTOMTRACEFUNC), customtrace)
-    lltype.attachRuntimeTypeInfo(SHADOWSTACKREF, customtraceptr=customtraceptr)
+
+    def shadowstack_destructor(shadowstackref):
+        from rpython.rlib import _rffi_stacklet as _c
+        h = shadowstackref.context
+        h = llmemory.cast_adr_to_ptr(h, _c.handle)
+        llmemory.raw_free(shadowstackref.base)
+        if h:
+            _c.destroy(h)
+
+    destrptr = gctransformer.annotate_helper(shadowstack_destructor,
+                                             [SHADOWSTACKREFPTR], lltype.Void)
+
+    lltype.attachRuntimeTypeInfo(SHADOWSTACKREF, customtraceptr=customtraceptr,
+                                 destrptr=destrptr)
 
     gctransformer._SHADOWSTACKREF = SHADOWSTACKREF
     return SHADOWSTACKREF

File rpython/memory/gctransform/transform.py

File contents unchanged.

File rpython/memory/gctypelayout.py

 
     OFFSETS_TO_GC_PTR = lltype.Array(lltype.Signed)
 
-    # When used as a finalizer, the following functions only take one
-    # address and ignore the second, and return NULL.  When used as a
-    # custom tracer (CT), it enumerates the addresses that contain GCREFs.
+    # A custom tracer (CT), enumerates the addresses that contain GCREFs.
     # It is called with the object as first argument, and the previous
     # returned address (or NULL the first time) as the second argument.
-    FINALIZER_OR_CT_FUNC = lltype.FuncType([llmemory.Address,
-                                            llmemory.Address],
-                                           llmemory.Address)
-    FINALIZER_OR_CT = lltype.Ptr(FINALIZER_OR_CT_FUNC)
+    FINALIZER_FUNC = lltype.FuncType([llmemory.Address], lltype.Void)
+    CUSTOMTRACER_FUNC = lltype.FuncType([llmemory.Address, llmemory.Address],
+                                        llmemory.Address)
+    FINALIZER = lltype.Ptr(FINALIZER_FUNC)
+    CUSTOMTRACER = lltype.Ptr(CUSTOMTRACER_FUNC)
+    EXTRA = lltype.Struct("type_info_extra",
+                          ('finalizer', FINALIZER),
+                          ('customtracer', CUSTOMTRACER))
 
     # structure describing the layout of a typeid
     TYPE_INFO = lltype.Struct("type_info",
         ("infobits",       lltype.Signed),    # combination of the T_xxx consts
-        ("finalizer_or_customtrace", FINALIZER_OR_CT),
+        ("extra",          lltype.Ptr(EXTRA)),
         ("fixedsize",      lltype.Signed),
         ("ofstoptrs",      lltype.Ptr(OFFSETS_TO_GC_PTR)),
         hints={'immutable': True},
     def q_finalizer(self, typeid):
         typeinfo = self.get(typeid)
         if typeinfo.infobits & T_HAS_FINALIZER:
-            return typeinfo.finalizer_or_customtrace
+            return typeinfo.extra.finalizer
         else:
-            return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)
+            return lltype.nullptr(GCData.FINALIZER_FUNC)
 
     def q_light_finalizer(self, typeid):
         typeinfo = self.get(typeid)
         if typeinfo.infobits & T_HAS_LIGHTWEIGHT_FINALIZER:
-            return typeinfo.finalizer_or_customtrace
+            return typeinfo.extra.finalizer
         else:
-            return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC)        
+            return lltype.nullptr(GCData.FINALIZER_FUNC)
 
     def q_offsets_to_gc_pointers(self, typeid):
         return self.get(typeid).ofstoptrs
         ll_assert(self.q_has_custom_trace(typeid),
                   "T_HAS_CUSTOM_TRACE missing")
         typeinfo = self.get(typeid)
-        return typeinfo.finalizer_or_customtrace
+        return typeinfo.extra.customtracer
 
     def q_fast_path_tracing(self, typeid):
         # return True if none of the flags T_HAS_GCPTR_IN_VARSIZE,
     infobits = index
     info.ofstoptrs = builder.offsets2table(offsets, TYPE)
     #
-    kind_and_fptr = builder.special_funcptr_for_type(TYPE)
-    if kind_and_fptr is not None:
-        kind, fptr = kind_and_fptr
-        info.finalizer_or_customtrace = fptr
-        if kind == "finalizer":
+    fptrs = builder.special_funcptr_for_type(TYPE)
+    if fptrs:
+        extra = lltype.malloc(GCData.EXTRA, zero=True, immortal=True,
+                              flavor='raw')
+        if "finalizer" in fptrs:
+            extra.finalizer = fptrs["finalizer"]
             infobits |= T_HAS_FINALIZER
-        elif kind == 'light_finalizer':
+        if "light_finalizer" in fptrs:
+            extra.finalizer = fptrs["light_finalizer"]
             infobits |= T_HAS_FINALIZER | T_HAS_LIGHTWEIGHT_FINALIZER
-        elif kind == "custom_trace":
+        if "custom_trace" in fptrs:
+            extra.customtracer = fptrs["custom_trace"]
             infobits |= T_HAS_CUSTOM_TRACE
-        else:
-            assert 0, kind
+        info.extra = extra
     #
     if not TYPE._is_varsize():
         info.fixedsize = llarena.round_up_for_allocation(
             return self._special_funcptrs[TYPE]
         fptr1, is_lightweight = self.make_finalizer_funcptr_for_type(TYPE)
         fptr2 = self.make_custom_trace_funcptr_for_type(TYPE)
-        assert not (fptr1 and fptr2), (
-            "type %r needs both a finalizer and a custom tracer" % (TYPE,))
+        result = {}
         if fptr1:
             if is_lightweight:
-                kind_and_fptr = "light_finalizer", fptr1
+                result["light_finalizer"] = fptr1
             else:
-                kind_and_fptr = "finalizer", fptr1
-        elif fptr2:
-            kind_and_fptr = "custom_trace", fptr2
-        else:
-            kind_and_fptr = None
-        self._special_funcptrs[TYPE] = kind_and_fptr
-        return kind_and_fptr
+                result["finalizer"] = fptr1
+        if fptr2:
+            result["custom_trace"] = fptr2
+        self._special_funcptrs[TYPE] = result
+        return result
 
     def make_finalizer_funcptr_for_type(self, TYPE):
         # must be overridden for proper finalizer support

File rpython/memory/gcwrapper.py

 
         t = self.llinterp.typer.annotator.translator
         light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
-        def ll_finalizer(addr, dummy):
-            assert dummy == llmemory.NULL
+        def ll_finalizer(addr):
             try:
                 v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
                 self.llinterp.eval_graph(destrgraph, [v], recursive=True)
             except llinterp.LLException:
                 raise RuntimeError(
                     "a finalizer raised an exception, shouldn't happen")
-            return llmemory.NULL
-        return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer), light
+        return llhelper(gctypelayout.GCData.FINALIZER, ll_finalizer), light
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         from rpython.memory.gctransform.support import get_rtti

File rpython/rlib/_rffi_stacklet.py

 
 new = llexternal('stacklet_new', [thread_handle, run_fn, llmemory.Address],
                  handle, random_effects_on_gcobjs=True)
-switch = llexternal('stacklet_switch', [thread_handle, handle], handle,
+switch = llexternal('stacklet_switch', [handle], handle,
                     random_effects_on_gcobjs=True)
-destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
+destroy = llexternal('stacklet_destroy', [handle], lltype.Void)
 
 _translate_pointer = llexternal("_stacklet_translate_pointer",
                                 [llmemory.Address, llmemory.Address],

File rpython/rlib/_stacklet_asmgcc.py

-from rpython.rlib import _rffi_stacklet as _c
 from rpython.rlib.debug import ll_assert
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
-from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator
+from rpython.annotator import model as annmodel
+from rpython.rlib import _rffi_stacklet as _c
 
 
 _asmstackrootwalker = None    # BIG HACK: monkey-patched by asmgcroot.py
     return _stackletrootwalker
 get_stackletrootwalker._annspecialcase_ = 'specialize:memo'
 
+def complete_destrptr(gctransformer):
+    translator = gctransformer.translator
+    mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
+    args_s = [annmodel.lltype_to_annotation(lltype.Ptr(SUSPSTACK))]
+    s_result = annmodel.s_None
+    destrptr = mixlevelannotator.delayedfunction(suspstack_destructor,
+                                                 args_s, s_result)
+    mixlevelannotator.finish()
+    lltype.attachRuntimeTypeInfo(SUSPSTACK, destrptr=destrptr)
+
 
 def customtrace(obj, prev):
     stackletrootwalker = get_stackletrootwalker()
     return stackletrootwalker.next(obj, prev)
 
+def suspstack_destructor(suspstack):
+    h = suspstack.handle
+    if h:
+        _c.destroy(h)
+
 
 SUSPSTACK = lltype.GcStruct('SuspStack',
                             ('handle', _c.handle),
     # 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),
+    return _c.new(gcrootfinder.newthrd, llhelper(_c.run_fn, _new_runfn),
                   llmemory.NULL)
 
 def _stack_just_closed():
     #
     # 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)
+    h2 = _c.switch(h)
     #
     if not h2:    # MemoryError: restore
         gcrootfinder.suspstack.anchor = oldanchor
     suspstack = NULL_SUSPSTACK
 
     def new(self, thrd, callback, arg):
-        self.thrd = thrd._thrd
+        self.newthrd = thrd._thrd
         self.runfn = callback
         self.arg = arg
         # make a fresh new clean SUSPSTACK
                                 alternateanchor)
         return self.get_result_suspstack(h)
 
-    def switch(self, thrd, suspstack):
-        self.thrd = thrd._thrd
+    def switch(self, suspstack):
         self.suspstack = suspstack
         h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _switch_callback),
                                 alternateanchor)
             # 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
-        suspstack.handle = _c.null_handle
-        _c.destroy(thrd._thrd, h)
-
     def is_empty_handle(self, suspstack):
         return not suspstack
 

File rpython/rlib/_stacklet_shadowstack.py

         return get_result_suspstack(h)
     new._dont_inline_ = True
 
-    def switch(thrd, suspstack):
+    def switch(suspstack):
         # suspstack has a handle to target, i.e. where to switch to
         ll_assert(suspstack != gcrootfinder.oldsuspstack,
                   "stacklet: invalid use")
         gcrootfinder.newsuspstack = suspstack
-        thread_handle = thrd._thrd
         h = llop.gc_shadowstackref_context(llmemory.Address, suspstack)
         h = llmemory.cast_adr_to_ptr(h, _c.handle)
         prepare_old_suspstack()
-        h = _c.switch(thread_handle, h)
+        h = _c.switch(h)
         return get_result_suspstack(h)
     switch._dont_inline_ = True
 
-    def destroy(thrd, suspstack):
-        h = llop.gc_shadowstackref_context(llmemory.Address, suspstack)
-        h = llmemory.cast_adr_to_ptr(h, _c.handle)
-        llop.gc_shadowstackref_destroy(lltype.Void, suspstack)
-        _c.destroy(thrd._thrd, h)
-
     def is_empty_handle(suspstack):
         return not suspstack
 

File rpython/rlib/rstacklet.py

     def switch(self, stacklet):
         if DEBUG:
             debug.remove(stacklet)
-        h = self._gcrootfinder.switch(self, stacklet)
+        h = self._gcrootfinder.switch(stacklet)
         if DEBUG:
             debug.add(h)
         return h
 
-    @jit.dont_look_inside
-    def destroy(self, stacklet):
-        if DEBUG:
-            debug.remove(stacklet)
-        self._gcrootfinder.destroy(self, stacklet)
-
     def is_empty_handle(self, stacklet):
         # note that "being an empty handle" and being equal to
         # "get_null_handle()" may be the same, or not; don't rely on it

File rpython/rlib/test/test_rstacklet.py

 except CompilationError, e:
     py.test.skip("cannot import rstacklet: %s" % e)
 
-from rpython.rlib import rrandom
+from rpython.rlib import rrandom, rgc
 from rpython.rlib.rarithmetic import intmask
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.translator.c.test.test_standalone import StandaloneTests
             self.status = 0
             h = self.sthread.new(switchbackonce_callback,
                                  rffi.cast(llmemory.Address, 321))
-            self.sthread.destroy(h)
+            # 'h' ignored
+            if (i % 5000) == 2500:
+                rgc.collect()
 
     def any_alive(self):
         for task in self.tasks:

File rpython/rtyper/llinterp.py

         raise NotImplementedError("gc_shadowstackref_new")
     def op_gc_shadowstackref_context(self):
         raise NotImplementedError("gc_shadowstackref_context")
-    def op_gc_shadowstackref_destroy(self):
-        raise NotImplementedError("gc_shadowstackref_destroy")
     def op_gc_save_current_state_away(self):
         raise NotImplementedError("gc_save_current_state_away")
     def op_gc_forget_current_state(self):

File rpython/rtyper/lltypesystem/lloperation.py

     # for stacklet+shadowstack support
     'gc_shadowstackref_new':      LLOp(canmallocgc=True),
     'gc_shadowstackref_context':  LLOp(),
-    'gc_shadowstackref_destroy':  LLOp(),
     'gc_save_current_state_away': LLOp(),
     'gc_forget_current_state':    LLOp(),
     'gc_restore_state_from':      LLOp(),

File rpython/translator/c/gc.py

 
     def struct_setup(self, structdefnode, rtti):
         if rtti is not None and hasattr(rtti._obj, 'destructor_funcptr'):
-            destrptr = rtti._obj.destructor_funcptr
+            gctransf = self.db.gctransformer
+            TYPE = structdefnode.STRUCT
+            fptrs = gctransf.special_funcptr_for_type(TYPE)
             # make sure this is seen by the database early, i.e. before
             # finish_helpers() on the gctransformer
+            destrptr = rtti._obj.destructor_funcptr
             self.db.get(destrptr)
             # the following, on the other hand, will only discover ll_finalizer
             # helpers.  The get() sees and records a delayed pointer.  It is
             # still important to see it so that it can be followed as soon as
             # the mixlevelannotator resolves it.
-            gctransf = self.db.gctransformer
-            TYPE = structdefnode.STRUCT
-            kind_and_fptr = gctransf.special_funcptr_for_type(TYPE)
-            if kind_and_fptr:
-                self.db.get(kind_and_fptr[1])
+            for fptr in fptrs.values():
+                self.db.get(fptr)
 
     def array_setup(self, arraydefnode):
         pass

File rpython/translator/c/src/stacklet/stacklet.c

      * main stack.
      */
     struct stacklet_s *stack_prev;
+
+    stacklet_thread_handle stack_thrd;  /* the thread where the stacklet is */
 };
 
 void *(*_stacklet_switchstack)(void*(*)(void*, void*),
     stacklet->stack_stop  = thrd->g_current_stack_stop;
     stacklet->stack_saved = 0;
     stacklet->stack_prev  = thrd->g_stack_chain_head;
+    stacklet->stack_thrd  = thrd;
     thrd->g_stack_chain_head = stacklet;
     return 0;
 }
     return thrd->g_source;
 }
 
-stacklet_handle stacklet_switch(stacklet_thread_handle thrd,
-                                stacklet_handle target)
+stacklet_handle stacklet_switch(stacklet_handle target)
 {
     long stackmarker;
+    stacklet_thread_handle thrd = target->stack_thrd;
     if (thrd->g_current_stack_stop <= (char *)&stackmarker)
         thrd->g_current_stack_stop = ((char *)&stackmarker) + 1;
 
     return thrd->g_source;
 }
 
-void stacklet_destroy(stacklet_thread_handle thrd, stacklet_handle target)
+void stacklet_destroy(stacklet_handle target)
 {
-    /* remove 'target' from the chained list 'unsaved_stack', if it is there */
-    struct stacklet_s **pp = &thrd->g_stack_chain_head;
-    for (; *pp != NULL; pp = &(*pp)->stack_prev)
-        if (*pp == target) {
-            *pp = target->stack_prev;
-            break;
-        }
+    if (target->stack_prev != NULL) {
+        /* 'target' appears to be in the chained list 'unsaved_stack',
+           so remove it from there.  Note that if 'thrd' was already
+           deleted, it means that we left the thread and all stacklets
+           still in the thread should be fully copied away from the
+           stack --- so should have stack_prev == NULL.  In this case
+           we don't even read 'stack_thrd', already deallocated. */
+        stacklet_thread_handle thrd = target->stack_thrd;
+        struct stacklet_s **pp = &thrd->g_stack_chain_head;
+        for (; *pp != NULL; pp = &(*pp)->stack_prev)
+            if (*pp == target) {
+                *pp = target->stack_prev;
+                break;
+            }
+    }
     free(target);
 }
 

File rpython/translator/c/src/stacklet/stacklet.h

  * Don't call this with an already-used target, with EMPTY_STACKLET_HANDLE,
  * or with a stack handle from another thread (in multithreaded apps).
  */
-stacklet_handle stacklet_switch(stacklet_thread_handle thrd,
-                                stacklet_handle target);
+stacklet_handle stacklet_switch(stacklet_handle target);
 
 /* Delete a stack handle without resuming it at all.
  * (This works even if the stack handle is of a different thread)
  */
-void stacklet_destroy(stacklet_thread_handle thrd, stacklet_handle target);
+void stacklet_destroy(stacklet_handle target);
 
 /* stacklet_handle _stacklet_switch_to_copy(stacklet_handle) --- later */