On Wednesday, 12 April 2017 at 19:01:25 UTC, Walter Bright wrote:
On 4/11/2017 10:24 AM, MysticZach wrote:
Hi guys. Hey Walter. So, about this point. On the lifetime study thread, http://forum.dlang.org/post/[email protected] , the following two problems were stated by Andrei, but I don't think they were adequately addressed
in the subsequent posts:

The way they will be addressed is to increment the reference count in the call to the function that takes a reference to an RC object.

Makes sense, and I thought the same thing until after I wrote my post — but now I realize it's not quite good enough. Merely incrementing the refcount (and decrementing it afterwards) has the following problem:

@rc class RC;

void fun(ref RC a, RC b) {
   a = new RC; // (1)
   // b...
}

void main() {
   auto r = new RC; // (2)
   --> r.opInc(); // compiler insert
   fun(r, r);
   --> r.opDec();
}

Let's assume the reference counting scheme involves the methods opInc and opDec, inserted automatically by the compiler as the result of detecting a duplicate parameter. If you read closely, you'll realize that at mark 1 above, the program will leak the data acquired at mark 2. The assign statement of the refcounted object will decrement the data it points to before it is reassigned. But since the data at (2)'s refcount was prematurely incremented, it will fall to 1 and never get deleted. Furthermore, the opDec in main() will decrement and delete the data acquired at mark 1, thinking it was the mark 2 data.

The problem is that the calling context at "fun(r,r);" fails to keep a real reference to the mark 2 data. If one of the parameters is sent by reference, we can't assume it points to the same data upon returning as when it was sent. And the mark 2 data can't be deleted before fun() returns, or it will invalidate 'b' in fun(). This suggests we need to save a real, separate reference to 'r' before sending two or more versions of it.

That said, I believe the following compiler inserts in main() would do the trick:

void main() {
   auto r = new RC; // (2)
   --> RC __rsave = r; // compiler insert
   --> scope(exit) __rsave = null;
   fun(r, r);
}

This solution uses only the assign method of RC to inc and dec the refcount, suggesting that opInc and opDec are ultimately unnecessary, except as (very rare) optimizations. But you still need for the compiler to distinguish a refcounted class from a regular one, so maybe the presence of an opDec() could indicate that, opDec being the quasi-destroyer that turns a refcounted class variable into an RAII type. Otherwise, you might need something like an @rc attribute.

At any rate, your original claim was, "a general mechanism for safe refcounting of classes has eluded us." Is this still the case, considering the above?

As far as the original post, even if a general mechanism were found, it doesn't mean you'd have to use it in the case of Exception anyway. To generalize the expression 'throw new ...', you'd have to redefine 'new' to be different with refcounted classes than with regular ones. Exceptions are probably worth a more specialized solution like the one you proposed. Otherwise everyone will have to change their 'throw new' code to accommodate '@nogc'. Adam D. Ruppe gave us his solution here:

http://arsdnet.net/exception.d

tl;dr Everyone would now have to say things like 'throw emplace!...' or 'raise!"my_exception"(...)'.

I guess the proposed hack of 'throw new Exception' is simply the shadow of D's original philosophy of wanting the GC to do too much.

Reply via email to