From: Leopold Toetsch <[EMAIL PROTECTED]>
   Date: Fri, 03 Feb 2006 14:49:00 +0100

   Bob Rogers wrote:

   >    Worse, the closed-over frame is leaked entirely.  (Is this what the
   > "obviously leaks memory" comment in src/register.c is talking about, or
   > are there other cases of leakage?)  But I think I have a handle on
   > what's causing this, and hope to propose a fix shortly.

   Yep re comment. It's probably just a matter of setting the initial 
   ctx->ref_count to 1. A context is either de-allocated immediately, if 
   the sub returns via RetContinuation (re_use := 1 in src/register.c:500) 
   or when the (ret)continuation is destroyed. The latter case needs a 
   proper ref_count setting.

Turns out that the leak is due to C<newclosure>, which causes the
frame's ref_count to jump immediately from 0 to 2.  One increment is due
to the RetContinuation => Continuation promotion, and the other is added
by parrot_new_closure to reflect the ref from the closure.  So the
essence of the fix is to create a Closure:destroy that calls
Parrot_free_context.  But I think it would be better to go further; more
on that below.

   I'm pretty sure that frames left via exception are also leaking 
   currently, which would also be covered by above strategy.

   leo

I'll take a look at that, then.

   From: Leopold Toetsch <[EMAIL PROTECTED]>
   Date: Fri, 3 Feb 2006 17:20:22 +0100

   On Feb 3, 2006, at 15:49, Nicholas Clark wrote:

   > On Tue, Jan 31, 2006 at 02:01:42PM +0100, Leopold Toetsch wrote:

   >> Limiting the callframe range, where the continuation can go. Currently
   >> creating a continuation is rather expensive . . .
   >
   > Could this be done lazily? . . .

   The 'rather' expensive thing is basically:

      while (cont)
            cont->vtable = Parrot_base_vtables[enum_class_Continuation]
            cont = cont->caller

   i.e. placing a new vtable into the whole call chain. Setting some flag 
   or whatever wouldn't be simpler. The only problem with above is that 
   it's O(n) in call depth. Therefore the idea of creating 'limited 
   continuations' that are only allowed to 'jump' between defined places 
   in the call chain.

   No - I don't think that this can be done lazily.

   > Nicholas Clark

   leo

If I understand correctly, this O(n) step is necessary because the new
Continuation might be used to jump back down the call chain.  In that
case, it would be necessary to return more than once through these
contexts, for which RetContinuation doesn't work, being a "use once and
recycle from_ctx immediately" version of Continuation.  For this reason,
there are also a handful of other places in the code that must change
RetContinuation to Continuation because RetContinuation is too
aggressive about recycling contexts that might still be referenced.

   So suppose we turn RetContinuation into a non-agressive "use once and
maybe recycle" continuation.  This could be done by making
RetContinuation use and obey the Parrot_Context ref_count field (and
making sure everybody else does too).  In other words, RetContinuation
would drop its context refs after being called, but would only free the
"from" context if doing so made the ref count drop to zero (i.e. the
normal Parrot_free_context thing).  We should also provide an explicit
"invalidate" operation that causes the context refs to be dropped
immediately; this would be useful for all continuation classes.

   Then, in order to implement a stack-unwinding control structure, one
can create a new RetContinuation in frame X, without triggering
continuation promotion on X's call chain.  The new RetContinuation gets
passed down (or stored in a lexical), and is either used, or explicitly
invalidated before exiting the frame; either way, X's ref count drops
back down.  Then, returning from frame X (still via its RetContinuation)
would recycle it normally [1].

   There are also a number of side benefits:

   1.  In the normal call/return case for which RetContinuation was
designed, "from" contexts would still get recycled immediately.

   2.  Creating a Continuation would still provide the desired "return
many" behavior, albeit still with the O(n) call-chain promotion cost,
but the extra expense would be optional.  Presumably, language
implementors will know which kind of continuation they need; if they
don't, then we can't help them anyway.

   3.  If a Closure or RetContinuation is created, the existing
RetContinuation chain would still work, decrementing the ref_count but
without necessarily freeing the context [2].

   4.  Most of the continuation promotions and the "re_use" parameter to
Parrot_free_context can be made to go away.  (IMHO, this will be easier
to maintain, as Parrot hackers won't be expected to figure out whether a
context could be referenced from elsewhere.)

   5.  Some other context memory leaks should be easier to fix.  For
instance, I notice a few cases of:

        cont->vtable = Parrot_base_vtables[enum_class_Continuation];
        caller->ref_count++;

These promotions are done without first checking whether the continution
is a RetContinuation; if not, the ref_count increment will leak the
context.

   6.  Last but not least, "use once" with explicit invalidation is a
useful thing to provide for supporting HLL semantics.  Indeed, I
discovered the original "Accept return values via a Continuation"
problem when trying to implement non-local exits in my Common Lisp
compiler [3].  I would also like to be able enforce the rule that one
can only exit from a block once; a non-aggressive RetContinuation would
not only support this nicely, it would also recycle the block's frame
that much sooner.

   Sound good?  Unless I've missed something, this seems like a win
across the board . . .

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

[1]  Unless of course there are closures that reference this frame.  I
     am reluctant to suggest that it be possible to invalidate a
     closure.  Maybe if there were a separate DownwardClosure class?

[2]  I think this would also fix a bug.  Closure:invoke promotes the
     return continuation of the :outer context, but not any other
     continuation.  I assume this is just to keep the context alive so
     that the outer_ctx chain can be traversed, but it means that
     invalidate_retc_context on a deeper frame will stop too soon.  If
     RetContinuation was less aggressive, then Closure:invoke wouldn't
     have to promote at all.

[3]  Which ought to be ready for release soon (though I've said that
     before).

Reply via email to