Immediate closing of iterators

Chris Hansen renselp at gmail.com
Mon Jan 1 08:36:59 PST 2007


> > Since finalization is tied to the GC (which might be conservative) it
> > cannot, by definition, be relied on to release resources in something
> > that resembles a timely fashion, or at all.
>
> If you can't rely on the GC to recover memory in the absence of
> client code bugs that generate uncollectible graphs, you need a new GC.

If the spec is written such that an implementation is allowed to use a
conservative GC then, by the definition of conservative GC, the spec
must allow for implementations to occasionally not reclaim memory and
hence not close generators.  If the spec mandates generators to be
closed eventually then conservative GCs cannot be used to implement
ES4.

If the spec does require implementations to eventually close
generators then, as Lars points out, there are still non-conservative
GCs that keep several generations and very rarely collect older
generations.  In that case, while you can expect the GC to close
generators, you cannot expect it to happen in a timely fashion.  That
makes it problematic to rely on the GC to release external resources.

Finally, the spec might require generators to be closed in a timely
fashion, for some suitable definition of "timely fashion".  I'm sure
you don't want to do that.

In the absence of finalization none of these problems occur because
then the collection or non-collection of an unreachable object cannot
be observed from the program.

> > There's another issue with finally guarantees that I don't think has
> > been mentioned: which thread is it that closes the generators?
> > Consider this example:
> >
> > let genCount = 0;
> > function makeGenerator() {
> >  genCount++;
> >  return myGenerator();
> > }
> >
> > function myGenerator() {
> >  try {
> >    yield 1;
> >    yield 2;
> >  } finally {
> >    genCount--;
> >  }
> > }
> >
> > Imagine that the program has been running for a while and the GC
> > decides to set in right after makeGenerator has read genCount but
> > before it has incremented it.  If the GC happens to close some of
> > these generators then genCount will be in an inconsistent state when
> > execution continues.  If the GC doesn't then who does?
>
> ECMA-262 does not define anything to-do with threads, and ES4 won't
> either.
>
> In the browser, the execution model is run-to-completion.  No event
> handler, not even a timeout, may preempt a running script (even if
> the script is flying a modal dialog; contrary behavior including in
> Firefox is a bug).

So there's actually a "negative" finalization guarantee:  in a
browser, unreachable and unclosed generators are guaranteed not to be
closed until after the script or event has run to completion.
Long-running scripts should not rely on generators being closed for
them I guess...

> The GC will not preempt makeGenerator at
> arbitrary points, either.  It might run synchronously (nesting on the
> current thread stack) at a backward branch or return (after the
> result has been evaluated), or from a native method, getter, or
> setter.  It won't run in the midst of genCount++.
> Any multi-threaded browser has to preserve the invariants of this
> model, for backward compatibility and developer sanity.
>
> The browser execution rules are not well-specified, but they should
> be, in a browser embedding spec (not directly in ES4).  Fodder for
> the WHATWG or W3C.

I used a counter to keep the example simple but you can easily imagine
a more complex example where the critical region contains a backward
branch, return, or one of the other cases.

As for the spec not specifying threading behavior, I would claim that
this issue _forces_ you to specify it.  If my program relies on
nothing but the semantics defined in the spec then the program should
run correctly on all spec-compliant implementations.  If the spec
leaves this question unanswered then I can't hope to write
implementation- or embedding-independent code.  If the spec doesn't
explicitly disallow close methods to be run preemptively then you
have, in effect, introduced multi threading -- at least, if I want to
write an implementation-independent program I have to consider the
possibility that there will be (spec compliant) implementations that
finalize preemptively.

On the other hand, if you do disallow close methods to be run
preemptively then you're more or less guaranteeing that on any
compliant implementation, close methods will _not_ be run in a timely
fashion, since they will have to wait for the script to finish.  That
may not be a problem in browsers but who knows where people might want
to embed ES4 and run long-running scripts


-- Chris



More information about the Es4-discuss mailing list