roxen.lists.pike.general

Subject Author Date
Re: MutexKeys, garbage collectors, destructors and optimisers Stephen R. van den Berg <srb[at]cuci[dot]nl> 03-02-2009
Martin Stjernholm wrote:
>"Stephen R. van den Berg" <<srb[at]cuci.nl>> wrote:
>> It's quite unfortunate that you need to know how the optimiser works to
>> ensure that certain side-effects in destructors are performed not too
>> soon or too late.

>Considering how bug prone, but more importantly thoroughly disruptive
>that semantic (i.e. that things are destructed immediately when
>reaching zero refs) is to gc optimizations, I'd say we should rather

Well, not quite that bad, I'd say.  As I stated before as well,
there is a difference between garbage collection of dead space and
explicit destructors actually causing side-effects other than merely
deallocating used space.

With respect to the differences in optimisation possibilities:
- Delaying the release of the space because the GC would like to do so
  is still possible (just like in your solution).
- Performing the release of space *earlier* is still possible, unless
  there are actually destructors.
- Actually running destructors early is not possible, but then again
  if that were possible, the programmer might as well have shortened the
  block in the first place.

>try to go away from it and replace it with explicit language
>constructs where these things matter.

>    destruct_on_exit (Thread.MutexKey key = roxenloader.sq_cache_lock()) {

>      destruct(key);

>        key = roxenloader.sq_cache_lock();

>Here the "destruct_on_exit" block informs the compiler that the
>contents of the variable "key" must be destructed when execution
>leaves the block. It would do that regardless of the amount of refs to
>the MutexKey object. (The name "destruct_on_exit" would preferably be

The "regardless of the amount of refs to the object" rule is actually
the only real advantage this construct has.

>Explicit constructs both makes the code more clear on what the
>programmer intends,

I find block-scope a rather simple and well-understood concept.
Using explicit constructs like the above rank the same
as simply adding a comment to the regular block construct in readability
(at least to me they do).

The counterargument in this case, BTW, is exactly the same:
*Not* using the explicit construction actually just happens to work
in a lot of cases, not because the compiler makes sure it does, but by
sheer luck.  So needing an explicit construction to make this happen will
ensure that some programmers will simply "forget" to use the special construct
because they either forget (it works most of the time) or because they don't
know (C and C++ don't have that problem, so if they come from that background
they simply expect it to work without the construct).
So by making the programmer specify it, you introduce an extra opportunity
for bugs to sneak in.

> and allows the compiler to optimize better, e.g.
>do more aggressive tail recursion when there is no "destruct_on_exit"
>block.

The above two cases I outlined should have made clear that the
optimisation opportunities for the compiler are neither worse nor
better in the my proposed semantics than in your explicit construction.

The "destruct even if there still are references" functionality can
simply be obtained by doing an explicit destruct(obj); just before the
end of the block, of course.
So in order to make the language more robust (without sacrificing optimisation
opportunities for the compiler) I'd still prefer the C++ destructor
semantics (principle of least surprise for programmers already familiar with
C/C++).
-- 
Sincerely,
           Stephen R. van den Berg.

"My best friend ran away with my wife.  I miss him."