Jump to page: 1 25  
Page
Thread overview
Proposal: Exceptions and @nogc
Apr 02, 2017
Walter Bright
Apr 02, 2017
rikki cattermole
Apr 02, 2017
Walter Bright
Apr 02, 2017
Walter Bright
Apr 02, 2017
Nicholas Wilson
Apr 02, 2017
Walter Bright
Apr 02, 2017
Dmitry Olshansky
Apr 02, 2017
Walter Bright
Apr 04, 2017
Atila Neves
Apr 04, 2017
Walter Bright
Apr 04, 2017
Atila Neves
Apr 04, 2017
Walter Bright
Apr 06, 2017
Shachar Shemesh
Apr 08, 2017
Walter Bright
Apr 03, 2017
crimaniak
Apr 03, 2017
Walter Bright
Apr 03, 2017
Jerry
Apr 03, 2017
Walter Bright
Apr 03, 2017
Jack Stouffer
Apr 03, 2017
Kagamin
Apr 03, 2017
Jack Stouffer
Apr 03, 2017
Kagamin
Apr 03, 2017
Jack Stouffer
Apr 04, 2017
Kagamin
Apr 03, 2017
Walter Bright
Apr 03, 2017
Jonathan M Davis
Apr 03, 2017
Adam D. Ruppe
Apr 06, 2017
Daniel N
Apr 03, 2017
Walter Bright
Apr 04, 2017
deadalnix
Apr 04, 2017
Jonathan M Davis
Apr 04, 2017
Walter Bright
Apr 04, 2017
Johannes Pfau
Apr 03, 2017
David Nadlinger
Apr 03, 2017
Walter Bright
Apr 05, 2017
Guillaume Piolat
Apr 05, 2017
David Nadlinger
Apr 05, 2017
Guillaume Piolat
Apr 05, 2017
David Nadlinger
Apr 06, 2017
Kagamin
Apr 05, 2017
Jack Stouffer
Apr 06, 2017
Guillaume Piolat
Apr 03, 2017
Jack Stouffer
Apr 04, 2017
Moritz Maxeiner
Apr 04, 2017
Jack Stouffer
Apr 05, 2017
Walter Bright
Apr 05, 2017
Jack Stouffer
Apr 05, 2017
deadalnix
Apr 06, 2017
Dukc
April 01, 2017
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
========

Using a ref counted solution brings with it a host of problems because
the compiler is not set up to ref count class object references, nor
is any existing code set up to deal with that.

Instead, rely on Exception objects having a single, trackable owner.

Create three druntime library functions,

1. allocate an Exception object
2. free an Exception object
3. copy (a.k.a. clone) an Exception object

Whether the implementations use malloc/free, or some other custom allocator,
is immaterial. The contents of the allocated object still need to be
subject to scanning by the GC.

throw Expression
----------------

The Expression is evaluated, and 'copy' is called to make the actual
Exception object that is thrown. If the Expression is:

   new Exception

then it is optimized to call 'allocate' instead.

catch (Exception e)
-------------------

'e' becomes implicitly 'scope', so 'e' cannot escape the
catch clause. At the exit of the catch clause, 'free' is
called on 'e'. If 'e' is rethrown, a 'copy' is made of it.

Chained Exceptions
------------------

These get 'free' called on them when the head of the chain
is free'd. The head of the chain owns these instances.
Access to these by user code needs to be protected by
'scope', and hence must be hidden behind an access function to
enforce that.

Copying Exceptions
------------------

There isn't any current mechanism to copy class objects. The
trouble comes in the form of any postblits that may be required
for fields. An initial solution is to disallow any Exception
objects with postblit fields. The eventual solution is to
auto-generate the copy code, like what is done for structs.

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

This will break an unknown amount of existing code.

Breakage will come in the form of:

1. dependency on identifying Exception objects by their addresses,
which won't work anymore because of the copying.
(good code shouldn't rely on this anyway)

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

3. Disallowing Exception objects with postblit fields.

4. 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.

Conclusion
----------

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

References
----------

http://www.digitalmars.com/d/archives/digitalmars/D/Exceptions_in_nogc_code_299261.html
April 02, 2017
On 02/04/2017 6:16 AM, Walter Bright wrote:
> 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
> ========
>
> Using a ref counted solution brings with it a host of problems because
> the compiler is not set up to ref count class object references, nor
> is any existing code set up to deal with that.
>
> Instead, rely on Exception objects having a single, trackable owner.
>
> Create three druntime library functions,
>
> 1. allocate an Exception object
> 2. free an Exception object
> 3. copy (a.k.a. clone) an Exception object
>
> Whether the implementations use malloc/free, or some other custom
> allocator,
> is immaterial. The contents of the allocated object still need to be
> subject to scanning by the GC.
>
> throw Expression
> ----------------
>
> The Expression is evaluated, and 'copy' is called to make the actual
> Exception object that is thrown. If the Expression is:
>
>    new Exception
>
> then it is optimized to call 'allocate' instead.
>
> catch (Exception e)
> -------------------
>
> 'e' becomes implicitly 'scope', so 'e' cannot escape the
> catch clause. At the exit of the catch clause, 'free' is
> called on 'e'. If 'e' is rethrown, a 'copy' is made of it.
>
> Chained Exceptions
> ------------------
>
> These get 'free' called on them when the head of the chain
> is free'd. The head of the chain owns these instances.
> Access to these by user code needs to be protected by
> 'scope', and hence must be hidden behind an access function to
> enforce that.
>
> Copying Exceptions
> ------------------
>
> There isn't any current mechanism to copy class objects. The
> trouble comes in the form of any postblits that may be required
> for fields. An initial solution is to disallow any Exception
> objects with postblit fields. The eventual solution is to
> auto-generate the copy code, like what is done for structs.
>
> Legacy Code Breakage
> --------------------
>
> This will break an unknown amount of existing code.
>
> Breakage will come in the form of:
>
> 1. dependency on identifying Exception objects by their addresses,
> which won't work anymore because of the copying.
> (good code shouldn't rely on this anyway)
>
> 2. leaking Exception objects from catch clauses (caught by
> the compiler)
>
> 3. Disallowing Exception objects with postblit fields.
>
> 4. 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.
>
> Conclusion
> ----------
>
> The result of this should be no leaking memory, no need to link
> in the GC, and memory safety.
>
> References
> ----------
>
> http://www.digitalmars.com/d/archives/digitalmars/D/Exceptions_in_nogc_code_299261.html

Questions:

1) What if an exception dtor throws (yuck but eh)?
2) Could this in some form be paired with allocators? e.g.
typeof(this) dup(IAllocator) { ... }
throw new MyException(8); versus throw alloc.make!MyException(8);
3) Do we really need to copy an exception before rethrowing? It seems overkill, couldn't we introduce a new local variable to determine if it should free or not?

And as probably expected, DIP please. Its a big set of changes and warrants documenting in that form.
April 01, 2017
I should have noted that this does not involve any syntax changes.
April 01, 2017
On 4/1/2017 10:29 PM, rikki cattermole wrote:
> 1) What if an exception dtor throws (yuck but eh)?

That's treated as a new exception.

> 2) Could this in some form be paired with allocators? e.g.
> typeof(this) dup(IAllocator) { ... }
> throw new MyException(8); versus throw alloc.make!MyException(8);

I don't see a purpose for a custom allocator with this. The exception objects will be totally owned by the druntime EH implementation.

> 3) Do we really need to copy an exception before rethrowing? It seems overkill,
> couldn't we introduce a new local variable to determine if it should free or not?

The vast majority of use cases would be:

    throw new ExceptionType;

which is detected by the compiler and the copy doesn't happen. For the rest of the (presumably) rare cases, an extra copy will be insignificant, and has the advantage of simplicity.

> And as probably expected, DIP please. Its a big set of changes and warrants
> documenting in that form.

If it survives the n.g. discussion I will. Though the DIP process is in limbo at the moment since Dicebot is no longer running it.
April 02, 2017
On Sunday, 2 April 2017 at 05:16:23 UTC, Walter Bright wrote:
> Problem
> =======
>
> [...]

How will this interact with preallocated exceptions (e.g.  from Liran's dconf talk last year)?
April 02, 2017
On 4/1/2017 11:50 PM, Nicholas Wilson wrote:
> On Sunday, 2 April 2017 at 05:16:23 UTC, Walter Bright wrote:
>> Problem
>> =======
>>
>> [...]
>
> How will this interact with preallocated exceptions (e.g.  from Liran's dconf
> talk last year)?

It will copy them and throw the copy.
April 02, 2017
On 4/2/17 9:14 AM, Walter Bright wrote:
> On 4/1/2017 11:50 PM, Nicholas Wilson wrote:
>> On Sunday, 2 April 2017 at 05:16:23 UTC, Walter Bright wrote:
>>> Problem
>>> =======
>>>
>>> [...]
>>
>> How will this interact with preallocated exceptions (e.g.  from
>> Liran's dconf
>> talk last year)?
>
> It will copy them and throw the copy.

Copy means allocate and then deallocate in the catch, defeating the whole propose of preallocating. Would it be possible to just set a bit somewhere that indicates that the exception is preallocated and need not be freed.

So for instance:

throw new Exception; // this is allocated exception

auto e = makeMeAnException();
throw e; // this is preallocated exception (need not be freed in the catch)

---
Dmitry Olshansky
April 02, 2017
On 4/2/2017 8:24 AM, Dmitry Olshansky wrote:
> Copy means allocate and then deallocate in the catch, defeating the whole
> propose of preallocating.

That's right.


> Would it be possible to just set a bit somewhere that
> indicates that the exception is preallocated and need not be freed.

Yes, it's possible. But I'd have to be convinced that there isn't some other problem with a design that requires preallocated exceptions.

Exceptions are slow; they are designed to totally favor the non-throwing path. Walking the stack and looking through the tables looking for the right stack frame information is slow. Unwinding is slow. The druntime exception handling code is dog slow (it's just as agonizing in C++, it's not a D thang).

If the D exception allocator uses a pool, and exceptions are held to being just a few bytes long (they can always use PIMPL if they have large data requirements), they'll be very cheap to allocate and initialize. It's hard to see how that would be a problem, given the rest of what goes on in throwing/unwinding.

I can see preallocated exceptions needed for avoiding GC and hence an adversely timed GC pause/collect cycle. But with this proposal, I can't see the value.
April 03, 2017
On Sunday, 2 April 2017 at 05:16:23 UTC, Walter Bright wrote:
> Using a ref counted solution brings with it a host of problems because
> the compiler is not set up to ref count class object references, nor
> is any existing code set up to deal with that.
Please describe in more detail the problems in this part. If you are still planning to introduce RC-based objects into the language, then it is not obvious that the dedicated allocators in the runtime are better than using RC-based exception objects.


April 02, 2017
On 4/2/2017 7:02 PM, crimaniak wrote:
> On Sunday, 2 April 2017 at 05:16:23 UTC, Walter Bright wrote:
>> Using a ref counted solution brings with it a host of problems because
>> the compiler is not set up to ref count class object references, nor
>> is any existing code set up to deal with that.
> Please describe in more detail the problems in this part. If you are still
> planning to introduce RC-based objects into the language, then it is not obvious
> that the dedicated allocators in the runtime are better than using RC-based
> exception objects.


RC objects in D will be done with structs, not classes, due to this problem.
« First   ‹ Prev
1 2 3 4 5