Proposal 2: Exceptions and @nogc
MysticZach via Digitalmars-d
digitalmars-d at puremagic.com
Thu Apr 13 06:19:10 PDT 2017
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/56301A8C.1060808@erdani.com , 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.
More information about the Digitalmars-d
mailing list