Jump to page: 1 26  
Page
Thread overview
Exceptions in @nogc code
Apr 01, 2017
rikki cattermole
Apr 01, 2017
Jacob Carlborg
Apr 01, 2017
deadalnix
Apr 01, 2017
Eugene Wissner
Apr 01, 2017
Walter Bright
Apr 02, 2017
deadalnix
Apr 04, 2017
Nick B
Apr 05, 2017
Meta
Apr 05, 2017
Mike Parker
Apr 05, 2017
deadalnix
Apr 06, 2017
deadalnix
Apr 06, 2017
Olivier FAURE
Apr 07, 2017
deadalnix
Apr 06, 2017
Z42K0
Apr 06, 2017
jmh530
Apr 04, 2017
Walter Bright
Apr 04, 2017
Adam D. Ruppe
Apr 04, 2017
deadalnix
Apr 05, 2017
Walter Bright
Apr 05, 2017
deadalnix
Apr 05, 2017
Walter Bright
Apr 06, 2017
deadalnix
Apr 06, 2017
Walter Bright
Apr 06, 2017
H. S. Teoh
Apr 06, 2017
Walter Bright
Apr 06, 2017
H. S. Teoh
Apr 06, 2017
Walter Bright
Apr 07, 2017
deadalnix
Apr 08, 2017
Jacob Carlborg
Apr 08, 2017
Walter Bright
Apr 08, 2017
Christophe
Apr 09, 2017
Shachar Shemesh
Apr 09, 2017
irritate
Apr 09, 2017
deadalnix
Apr 09, 2017
KaattuPoochi
Apr 02, 2017
Adam D. Ruppe
Apr 02, 2017
Johannes Pfau
Apr 02, 2017
Adam D. Ruppe
Apr 02, 2017
deadalnix
Apr 02, 2017
Walter Bright
Apr 01, 2017
Dmitry Olshansky
Apr 02, 2017
Walter Bright
Apr 02, 2017
ketmar
Apr 02, 2017
Walter Bright
Apr 08, 2017
Martin Nowak
Apr 01, 2017
Guillaume Piolat
Apr 01, 2017
Eugene Wissner
Apr 02, 2017
Adam D. Ruppe
Apr 02, 2017
Guillaume Piolat
Apr 04, 2017
Guillaume Piolat
Apr 03, 2017
Matthias Bentrup
Apr 03, 2017
deadalnix
April 01, 2017
Walter and I discussed the following promising setup:

Use "throw new scope Exception" from @nogc code. That will cause the exception to be allocated in a special stack-like region.

If the catching code uses "catch (scope Exception obj)", then a reference to the exception thus created will be passed to catch. At the end of the catch block there's no outstanding reference to "obj" so it will be freed. All @nogc code must use this form of catch.

If the catching code uses "catch (Exception obj)", the exception is cloned on the gc heap and then freed.

Finally, if an exception is thrown with "throw new Exception" it can be caught with "catch (scope Exception obj)" by copying the exception from the heap into the special region, and then freeing the exception on the heap.

Such a scheme preserves backward compatibility and leverages the work done on "scope".


Andrei
April 01, 2017
On 01/04/2017 2:34 PM, Andrei Alexandrescu wrote:
> Walter and I discussed the following promising setup:
>
> Use "throw new scope Exception" from @nogc code. That will cause the
> exception to be allocated in a special stack-like region.
>
> If the catching code uses "catch (scope Exception obj)", then a
> reference to the exception thus created will be passed to catch. At the
> end of the catch block there's no outstanding reference to "obj" so it
> will be freed. All @nogc code must use this form of catch.
>
> If the catching code uses "catch (Exception obj)", the exception is
> cloned on the gc heap and then freed.
>
> Finally, if an exception is thrown with "throw new Exception" it can be
> caught with "catch (scope Exception obj)" by copying the exception from
> the heap into the special region, and then freeing the exception on the
> heap.
>
> Such a scheme preserves backward compatibility and leverages the work
> done on "scope".
>
>
> Andrei

I love it!

Could we also have some way to force scope when throwing?

interface ScoppedThrowable {}
class MyException : Exception, ScoppedThrowable { ... }

throw new MyException;
April 01, 2017
On 2017-04-01 15:34, Andrei Alexandrescu wrote:
> Walter and I discussed the following promising setup:
>
> Use "throw new scope Exception" from @nogc code. That will cause the
> exception to be allocated in a special stack-like region.
>
> If the catching code uses "catch (scope Exception obj)", then a
> reference to the exception thus created will be passed to catch. At the
> end of the catch block there's no outstanding reference to "obj" so it
> will be freed. All @nogc code must use this form of catch.
>
> If the catching code uses "catch (Exception obj)", the exception is
> cloned on the gc heap and then freed.
>
> Finally, if an exception is thrown with "throw new Exception" it can be
> caught with "catch (scope Exception obj)" by copying the exception from
> the heap into the special region, and then freeing the exception on the
> heap.
>
> Such a scheme preserves backward compatibility and leverages the work
> done on "scope".

Sounds interesting. Any more details about how the exception will be allocated.

-- 
/Jacob Carlborg
April 01, 2017
On Saturday, 1 April 2017 at 13:34:58 UTC, Andrei Alexandrescu wrote:
> Walter and I discussed the following promising setup:
>
> Use "throw new scope Exception" from @nogc code. That will cause the exception to be allocated in a special stack-like region.
>
> If the catching code uses "catch (scope Exception obj)", then a reference to the exception thus created will be passed to catch. At the end of the catch block there's no outstanding reference to "obj" so it will be freed. All @nogc code must use this form of catch.
>
> If the catching code uses "catch (Exception obj)", the exception is cloned on the gc heap and then freed.
>
> Finally, if an exception is thrown with "throw new Exception" it can be caught with "catch (scope Exception obj)" by copying the exception from the heap into the special region, and then freeing the exception on the heap.
>
> Such a scheme preserves backward compatibility and leverages the work done on "scope".
>
>
> Andrei

I'll repeat myself, even if I don't believe it'll be listened to at this point.

The problem you want to address is not GC allocations, it is GC collection cycles. If everything is freed, then there is no GC problem. not only this, but this is the only way GC and nogc code will interact with each others.

As long as a memory allocation has an owner the compiler can track, it can be freed explicitly, and, when it cannot, the compiler transfer ownership to the GC, which is illegal in @nogc code.

Transfering the ownership to the unwind handler when doing:

throw new FooException();

Is not rocket science and doesn't need any new language addition.

Now onto to scope. Scope essentially means that you are going to use some object without taking ownership of it. Indeed, in case of catch(Exception e) the language has to transfers the ownership of the Exception to the GC, which is the thing that should be illegal (not throwing). catch(scope Exception e) would work both with GC owned and runtime owned exception, and, because the runtime know what's up, it can explicitly free the exception when it exit the catch block (there is already a runtime call for that), in the case it owns it.

It doesn't need any kind of throw new scope Exception, and was proposed, literally, years ago during discussion around DIP25 and alike.

I urge you to reconsider the proposal that were made at the time. They solve all the problems you are discovering now, and more. And, while more complex that DIP25 alone, considering DIP25+DIP1000+this thing+the RC object thing, you are already in the zone where the "simple" approach is not so simple already.

Things are unfolding exactly as predicted at the time. Ad hoc solutions to various problems are proposed one by one and the overall complexity is growing much larger than initially proposed solutions.

April 01, 2017
On Saturday, 1 April 2017 at 14:54:21 UTC, deadalnix wrote:
> I'll repeat myself, even if I don't believe it'll be listened to at this point.
>
> The problem you want to address is not GC allocations, it is GC collection cycles. If everything is freed, then there is no GC problem. not only this, but this is the only way GC and nogc code will interact with each others.
>
> As long as a memory allocation has an owner the compiler can track, it can be freed explicitly, and, when it cannot, the compiler transfer ownership to the GC, which is illegal in @nogc code.
>
> Transfering the ownership to the unwind handler when doing:
>
> throw new FooException();
>
> Is not rocket science and doesn't need any new language addition.
>
> Now onto to scope. Scope essentially means that you are going to use some object without taking ownership of it. Indeed, in case of catch(Exception e) the language has to transfers the ownership of the Exception to the GC, which is the thing that should be illegal (not throwing). catch(scope Exception e) would work both with GC owned and runtime owned exception, and, because the runtime know what's up, it can explicitly free the exception when it exit the catch block (there is already a runtime call for that), in the case it owns it.
>
> It doesn't need any kind of throw new scope Exception, and was proposed, literally, years ago during discussion around DIP25 and alike.
>
> I urge you to reconsider the proposal that were made at the time. They solve all the problems you are discovering now, and more. And, while more complex that DIP25 alone, considering DIP25+DIP1000+this thing+the RC object thing, you are already in the zone where the "simple" approach is not so simple already.
>
> Things are unfolding exactly as predicted at the time. Ad hoc solutions to various problems are proposed one by one and the overall complexity is growing much larger than initially proposed solutions.

I also want to add that the syntax is ugly. A lot of work is being done on "scope" currently, but I don't understand why "scope" should appear now in every line of code: scope parameters, scope exceptions... How do I decide if I should use "new Exception" or "throw new scope Exception"? If it is a migration path and everyone should migrate to "new scope Exception", then "throw new scope Exception" is still one keyword longer than the simple "throw new Exception". Imho it would be better to change the way exceptions are allocated instead of making a complex language even more complex.
April 01, 2017
On 4/1/17 3:34 PM, Andrei Alexandrescu wrote:
> Walter and I discussed the following promising setup:
>
> Use "throw new scope Exception" from @nogc code. That will cause the
> exception to be allocated in a special stack-like region.
>
> If the catching code uses "catch (scope Exception obj)", then a
> reference to the exception thus created will be passed to catch. At the
> end of the catch block there's no outstanding reference to "obj" so it
> will be freed. All @nogc code must use this form of catch.
>
> If the catching code uses "catch (Exception obj)", the exception is
> cloned on the gc heap and then freed.
>
> Finally, if an exception is thrown with "throw new Exception" it can be
> caught with "catch (scope Exception obj)" by copying the exception from
> the heap into the special region, and then freeing the exception on the
> heap.
>

Horrible. Introducing the verbose way for the most commonly useful scenario and pushing the complexity on to programmers.

I don't understand what's so difficult to just recognize that "throw new Exception" creates a unique object that is passed to the exception handler. So just mark the exception as unique in the exception handler code so that catch site is aware of it. Are we just bend on penalizing programmers for not typing out "scope"?

In turn I don't see what adding "scope" to the catch site accomplishes - you still need to ensure the reference doesn't escape. And if it doesn't escape you may just as well deallocate the exception object.

> Such a scheme preserves backward compatibility and leverages the work
> done on "scope".

Pardon but I don't see how. It has one good thing going for it - the same keyword.


---
Dmitry Olshansky
>
> Andrei

April 01, 2017
On Saturday, 1 April 2017 at 13:34:58 UTC, Andrei Alexandrescu wrote:
> Walter and I discussed the following promising setup:
>
> Use "throw new scope Exception" from @nogc code. That will cause the exception to be allocated in a special stack-like region.
>
> If the catching code uses "catch (scope Exception obj)", then a reference to the exception thus created will be passed to catch. At the end of the catch block there's no outstanding reference to "obj" so it will be freed. All @nogc code must use this form of catch.
>
> If the catching code uses "catch (Exception obj)", the exception is cloned on the gc heap and then freed.
>
> Finally, if an exception is thrown with "throw new Exception" it can be caught with "catch (scope Exception obj)" by copying the exception from the heap into the special region, and then freeing the exception on the heap.
>
> Such a scheme preserves backward compatibility and leverages the work done on "scope".
>
>
> Andrei

(currently using @nogc exceptions with malloc+emplace and destroy+free)

OK. The important bit imho is that exception don't become special class objects.
ie. "scope new" is not specific to exceptions.
The other @nogc blocker is .destroy
April 01, 2017
On Saturday, 1 April 2017 at 18:56:56 UTC, Guillaume Piolat wrote:
> (currently using @nogc exceptions with malloc+emplace and destroy+free)
>
> OK. The important bit imho is that exception don't become special class objects.
> ie. "scope new" is not specific to exceptions.
> The other @nogc blocker is .destroy

If I understand correctly exceptions are anyway special class objects. A scope Exception wouldn't be allocated in the current scope, but elsewhere, so it can be catched outside the current stack frame. The same as Errors are special class objects, they can be thrown regardless nothrow and @nogc, because these aren't recoverable errors.
April 01, 2017
On 4/1/2017 7:54 AM, deadalnix wrote:
> It doesn't need any kind of throw new scope Exception, and was proposed,
> literally, years ago during discussion around DIP25 and alike.

A link to that proposal would be appreciated.

April 02, 2017
On Saturday, 1 April 2017 at 14:54:21 UTC, deadalnix wrote:
> The problem you want to address is not GC allocations, it is GC collection cycles. If everything is freed, then there is no GC problem. not only this, but this is the only way GC and nogc code will interact with each others.

Amen. Moreover, for little things like exceptions, you can probably also just hack it to not do a collection cycle when allocating them.

pseudocode:

So right now, we have:

void* gc_alloc(T)(size_t sz) {
    auto alloc = malloc(sz);
    if(alloc is null) {
       collection_cycle();
       retry();
       if(alloc is null) // still
          onOutOfMemoryError();
    }
    return alloc;
}

But imagine if it was

    if(alloc is null && !is(T : Throwable))


so it just abort()'s if it can't allocate the exception instead of collecting. How would that be any different than malloc()'ing it?

But, if you DO get a chance to run the GC, it will be collected, and, of course, you can manually/automatically free it too.

Works with existing code with no more likelyhood of leaking than we already have, maintaining memory safety... and removes the risk of a new exception of triggering a gc cycle.

so why not?

> Things are unfolding exactly as predicted at the time. Ad hoc solutions to various problems are proposed one by one and the overall complexity is growing much larger than initially proposed solutions.

Yeah, my gut reaction to this was "gross moar attributes come on"
« First   ‹ Prev
1 2 3 4 5 6