Piers Cawley wrote: > > The Perl 6 Summary for the week ending 20030601 > Another Monday, another Perl 6 Summary. Does this man never take a > holiday? (Yes, but only to go to Perl conferences this year, how did > that happen?) > > We start with the internals list as usual. > > More on timely destruction > The discussion of how to get timely destruction with Parrot's Garbage > Collection system continued. Last week Dan announced that, for languages > that make commitments to call destructors as soon as a thing goes out of > scope, there would be a set of helpful ops to allow languages to trigger > conditional DOD (Dead Object Detection) runs at appropriate times. > People weren't desperately keen on the performance hit that this entails > (though the performance hit involved with reference counting is pretty > substantial...) but we didn't manage to come up with a better solution > to the issue. > > http://xrl.us/iu5
I'd like to reiterate (and clarify) my idea, of a hybrid scheme combining refcounting and DoD. All values needing timely destruction would inherit from a class RefCounted. They should (in the normal scheme of things) be stored in variables with an "is refcounted" trait (or a trait which inherits or includes that trait, if that's possible). Hopefully, only a *small* number of objects in a system need timely destruction, and (for best performance) all variables which will hold those objects will have the "is refcounted" trait set on them. (If most or all objects needed timely destruction, then we wouldn't be moving away from perl5's pure-refcount GC, would we?) All operations on a variable or variables having the "is refcounted" trait result in opcodes to call the refcount(inc|dec) method(s) of the values in those variables. Under normal circumstances, when a refcounted value's refcount goes to zero, it self destructs^W^W cleans itself up. This is quite perl5-esque, and of course slower than simply letting the DoD-GC handle everything, BUT, in the absence of circular reference loops, it does produce timely cleanup of those objects which need it, which pure-DoD wouldn't, and DOESN'T result in numerous, and thus expensive, requests for full DoD runs. For all of the other schemes, full DoD runs would be run on every scope exit, even if there's only one single variable in scope which needs timely destruction. Here's where the non-normal circumstances are described, and how they're handled: It's legal for a refcounted value to end up referenced by a non- refcounted variable. This has to be so, since adding the "is refcounted" trait everywhere would get cumbersome; also we may sometimes *need*, for one reason or another, to store refcounted values in the same containers which also hold non-refcounted values. Thus, if a refcounted value gets stored in a non-refcounted variable, then that value could quite easily still be reachable when it's refcount goes to zero. Obviously, when this is the case, we need to avoid a premature cleanup. To avoid premature cleanup, any time that the contents of a refcounted variable is assigned to a non-refcounted variable, an opcode to set a "reachable by non-refcounted variable" flag on the value precedes the assignment. If a refcounted values's refcount goes to zero, and it has this flag set, it does NOT self-destruct[*]. A global counter keeps track of how many values have a refcount of zero and have this flag set. >From here, we are in a similar situation as other proposals -- for the timeliest possible destruction, then on every single scope exit, we check if this global counter is nonzero, and if so, do a DoD run. For potentially less timely destruction, we do this check whenever we leave a scope where a refcounted variable was declared, but not when we leave other scopes. Which of these two is done is a choice of the language designer, but might possibly be controlled by a pragma. Note that in the absence of marking any variables with the "is refcounted" trait, and with the use of the first option here (at every scope exit, check the counter and maybe do DoD), this scheme behaves *identically* with one of the other schemes proposed. It would be *no* slower at run time, since the refcount(inc|dec) methods are only called when doing operations dealing with variables with the "is refcounted" trait, and if noone uses that trait, there's no extra work. DoD would be a tiny bit slower with my scheme than the other, due to the extra flag checking, but (assuming that *most* code with objects needing timely destruction properly marks it's variables with the necessary "is refcounted" trait) that's a small price to pay for doing fewer DoD runs. [*] How does this flag get cleared, you might ask? Simplest would be to not clear it at all. This would be mostly harmless in terms of when objects get destructed, but would probably result in more DoD runs than we really need -- blech. [**] A more "proper" solution would be to clear it during/after each DoD run. During the course of a DoD run, when we look for what PMCs are reachable, we keep track of what each thing was reachable *from*. If a PMC was found to be reachable through a non-refcounted variable, then we set a flag saying so. At the end of DoD, every reachable refcounted value which has the first flag set, but this other flag not set, gets it's first flag cleared. [**] Please note: Never doing any checking or attempting to clear this flag (and not adding any "is refcounted" traits to variables) would result in my scheme having the EXACT same performance as the other proposed scheme, and not be a single bit slower. -- $a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca );{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "[EMAIL PROTECTED] ]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}