Jump to page: 1 26  
Page
Thread overview
Proposal 2: Exceptions and @nogc
Apr 09, 2017
Walter Bright
Apr 09, 2017
Joakim
Apr 09, 2017
Nicholas Wilson
Apr 09, 2017
Walter Bright
Apr 09, 2017
Daniel N
Apr 09, 2017
Walter Bright
Apr 09, 2017
deadalnix
Apr 11, 2017
MysticZach
Apr 12, 2017
Walter Bright
Apr 13, 2017
MysticZach
Apr 15, 2017
MysticZach
Apr 09, 2017
Dukc
Apr 09, 2017
Walter Bright
Apr 10, 2017
Andrew Godfrey
Apr 10, 2017
Walter Bright
Apr 10, 2017
Andrew Godfrey
Apr 10, 2017
Walter Bright
Apr 10, 2017
Andrew Godfrey
Apr 10, 2017
Dukc
Apr 10, 2017
Andrew Godfrey
Apr 11, 2017
Walter Bright
Apr 11, 2017
Jonathan M Davis
Apr 11, 2017
Walter Bright
Apr 11, 2017
Dukc
Apr 12, 2017
Nick Treleaven
Apr 13, 2017
Dukc
Apr 15, 2017
Nick Treleaven
Apr 09, 2017
Andrew Godfrey
Apr 09, 2017
Walter Bright
Apr 09, 2017
Jack Stouffer
Apr 10, 2017
Walter Bright
Apr 10, 2017
Jacob Carlborg
Apr 10, 2017
Jonathan Marler
Apr 11, 2017
Jacob Carlborg
Apr 10, 2017
Walter Bright
Apr 10, 2017
Lurker
Apr 10, 2017
Jonathan Marler
Apr 10, 2017
Walter Bright
Apr 11, 2017
rjframe
Apr 11, 2017
HaraldZealot
Apr 11, 2017
Walter Bright
Apr 12, 2017
deadalnix
Apr 14, 2017
Timon Gehr
Apr 14, 2017
Walter Bright
Apr 15, 2017
Meta
[OT] Re: Proposal 2: Exceptions and @nogc
Apr 15, 2017
Timon Gehr
Apr 15, 2017
Walter Bright
Apr 15, 2017
Timon Gehr
Apr 15, 2017
Timon Gehr
Apr 12, 2017
deadalnix
Apr 10, 2017
Meta
Apr 11, 2017
Adam D. Ruppe
Apr 11, 2017
Walter Bright
Apr 11, 2017
Meta
Apr 11, 2017
Jacob Carlborg
Apr 11, 2017
Walter Bright
Apr 15, 2017
Chris M.
April 08, 2017
My previous version did not survive implementation. Here's the revised version. I have submitted it as a DIP, and there's a trial implementation up:

    https://github.com/dlang/dmd/pull/6681

---------------------------------------------
	Exceptions and @nogc version 2

Problem
=======

Exceptions are assumed to be GC collected by the EH design, in that no
attempt is made to control copies or lifetimes. This dependency is not
a performance issue, as exceptions are presumed to be slow.

The issue is it impairs use of @nogc on any code that throws exceptions,
and prevents building D programs that do not link in the GC runtime.

To fix this, the allocation and destruction of the exception objects
must be completely controlled.

Solution
========

Make Throwable optionally ref counted. Add a field `_refcount` which
is !=0 when it is a ref counted instance. The number of parents of
a refcounted Throwable is _refcount+1. This member is private and only
accessible via the @system member function refcount(), to prevent
its use in @safe code.

The only place a refcounted Throwable is ever created is when the following
statement is in the user code:

    throw new E(string);

where E is Throwable or derived from Throwable. Instead of calling the usual
_d_newclass() to allocate E on the GC heap, it will call the new function
_d_newThrowable() which will allocate E and intialize it for refcounting.

When the exception is thrown, either _d_throwc() or _d_throwdwarf() gets called,
which is where druntime takes over. The refcount is incremented in these functions,
usually from 1 to 2.

The thrown object will then wind up either in a catch statement, or in the `next`
linked list of thrown exceptions in flight.

A catch variable `e` as in:

    catch (E e)
    {
        ...
    }

becomes an RAII object, meaning that it is destroyed at the closing }. Such
destruction is done by calling:

    _d_delThrowable(e);

which will test e._refcount to see if it is a ref counted object. If not,
it does nothing. If it is, the refcount is decremented, and if it hits
one then e's destructor is called, followed by free'ing the memory used
by `e`.

In catch blocks, `e` is regarded as `scope`, so that it cannot escape the
catch block. As a special case, if `e` is thrown in the catch block as:

    throw e;

then `e` can escape, and this works because as mentioned before `_d_throwc()` or
`_d_throwdwarf()` will increment the reference count.

The destructor for `Throwable` will, if the refcount is 1, call _d_delThrowable(e.next),
i.e. on the head of the chained list of exceptions. This means that the chained
list can be a mixed collection of refcounted and not-refcounted Throwables, although
the refcounted ones can only be free'd when their antecedent gets reaped by whatever
means.

The chained list must be protected so it cannot be altered or escaped by user code.

Legacy Code Breakage
--------------------

This will break an unknown amount of existing code.

Breakage will come in the form of:

1. leaking Exception objects from catch clauses (caught by
the compiler)

2. Disallowing Exception objects with postblit fields.

3. Catch objects being 'scope' will cause problems in that
everything done with those objects will also have to be 'scope'.
The most likely problem will be printing the objects which
relies on Object.toString() which is not 'scope'. One possible
solution is to force Throwable.toString() to be 'scope', which
will likely cause minimal disruption. Of course, compiling
with -dip1000 will disable such checking and can work in
the interim. Code that needs to leak the thrown exception object
can clone the object.

Conclusion
----------

The result of this should be no leaking memory, no need to link
in the GC, and memory safety.

GC, stack, and refcounted exception objects can coexist in the same
program.

References
----------

http://www.digitalmars.com/d/archives/digitalmars/D/Exceptions_in_nogc_code_299261.html
April 09, 2017
On Sunday, 9 April 2017 at 03:26:14 UTC, Walter Bright wrote:
> My previous version did not survive implementation. Here's the revised version. I have submitted it as a DIP, and there's a trial implementation up:
>
>     https://github.com/dlang/dmd/pull/6681

Two small procedural notes

- I don't see a DIP proposal in this list:

https://github.com/dlang/DIPs/pulls

- I suggest you not use -dip1006 as the flag to enable this, as the DIP numbering used to be assigned after the DIP proposal was merged and there's currently another proposal in that list that makes the same mistake of claiming that number.  Better to use some other flag for now, until you're sure of the DIP number, or the name clashes will be confusing.
April 09, 2017
On Sunday, 9 April 2017 at 03:26:14 UTC, Walter Bright wrote:
> My previous version did not survive implementation. Here's the revised version. I have submitted it as a DIP, and there's a trial implementation up:
>
>     https://github.com/dlang/dmd/pull/6681
>
> [ . . . ]

Pardon my ignorance but it does not seem to be clear how preallocated exceptions would work. What about exceptions allocated ultimately by the GC but actually via an intermediate allocator (e.g. FallBack)?
April 08, 2017
On 4/8/2017 9:30 PM, Nicholas Wilson wrote:
> Pardon my ignorance but it does not seem to be clear how preallocated exceptions
> would work. What about exceptions allocated ultimately by the GC but actually
> via an intermediate allocator (e.g. FallBack)?

You can allocate them any way you like - the runtime won't recognize those as its own refcounted objects, and will not try to free them.
April 09, 2017
On Sunday, 9 April 2017 at 03:26:14 UTC, Walter Bright wrote:
> Solution
> ========
>
> Make Throwable optionally ref counted.

I like this new direction...

> The only place a refcounted Throwable is ever created is when the following
> statement is in the user code:
>
>     throw new E(string);
>

... but why not go all the way, making it "always" refcounted? (for any "new E", not emplace).

April 09, 2017
On Sunday, 9 April 2017 at 03:26:14 UTC, Walter Bright wrote:
> The only place a refcounted Throwable is ever created is when the following
> statement is in the user code:
>
>     throw new E(string);

object aMemoryLeak;

void someFunc()
{   throw (aMemoryLeak = new Exception("hello world!"));
}

Would the compiler warn about this or make the exception normally garbage collected?

I'm not sure, but perhaps an entirely different syntax would be in place for throwing a refcounted object, so there's no special casing. For example, could a throw statement without the new keyword mean a refcounted throw?


April 09, 2017
On Sunday, 9 April 2017 at 03:26:14 UTC, Walter Bright wrote:
> My previous version did not survive implementation. Here's the revised version. I have submitted it as a DIP, and there's a trial implementation up:
>
> [...]

Just a quick note to reduce confusion for reviewers:

> The number of parents of a refcounted Throwable is _refcount+1.

I believe what you mean here is "_refcount-1".
Later you say that the exception is reclaimed when the refcount goes to 1.
April 09, 2017
On 4/9/2017 1:16 AM, Daniel N wrote:
> ... but why not go all the way, making it "always" refcounted? (for any "new E",
> not emplace).

Backwards compatibility, for one. For another, a general mechanism for safe refcounting of classes has eluded us.

April 09, 2017
On 4/9/2017 1:35 AM, Dukc wrote:
> object aMemoryLeak;
>
> void someFunc()
> {   throw (aMemoryLeak = new Exception("hello world!"));
> }
>
> Would the compiler warn about this or make the exception normally garbage
> collected?

That would be a regular gc allocated Exception.


> I'm not sure, but perhaps an entirely different syntax would be in place for
> throwing a refcounted object, so there's no special casing. For example, could a
> throw statement without the new keyword mean a refcounted throw?

Didn't want to do a new syntax.
April 09, 2017
On 4/9/2017 12:05 PM, Andrew Godfrey wrote:
> Just a quick note to reduce confusion for reviewers:
>
>> The number of parents of a refcounted Throwable is _refcount+1.
>
> I believe what you mean here is "_refcount-1".
> Later you say that the exception is reclaimed when the refcount goes to 1.

Thanks for the correction.
« First   ‹ Prev
1 2 3 4 5 6