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).