Jump to page: 1 2
Thread overview
I can has @nogc and throw Exceptions?
Feb 13, 2015
Jonathan Marler
Feb 13, 2015
Tobias Pankrath
Feb 13, 2015
Marc Schütz
Jul 13, 2016
Adam Sansier
Jul 13, 2016
Lodovico Giaretta
Jul 13, 2016
Adam Sansier
Jul 13, 2016
Eugene Wissner
Jul 13, 2016
Lodovico Giaretta
Jul 13, 2016
Adam Sansier
Jul 13, 2016
Lodovico Giaretta
Jul 13, 2016
Adam Sansier
Jul 13, 2016
Lodovico Giaretta
Jul 13, 2016
Adam Sansier
Jul 14, 2016
Lodovico Giaretta
Feb 13, 2015
Adam D. Ruppe
Feb 13, 2015
Jonathan Marler
Feb 13, 2015
Jonathan Marler
Jul 19, 2018
timotheecour
February 13, 2015
This question comes from wanting to be able to throw an exception in code that is @nogc.

I don't know if it's possible but I'd like to be able to throw an exception without allocating memory for the garbage collector?  You can do it in C++ so I think you should be able to in D.  One idea I had was to allocate the memory for the Exception beforehand and create the Exception class with the pre-allocated memory.  I came up with the following code:

T construct(T,A...)(void* buffer, A args)
{
  return (cast(T)buffer).__ctor(args);
}

Now to test it:

void main()
{
  ubyte[ __traits(classInstanceSize, Exception)] exceptionBuffer;
  throw construct!(Exception)(exceptionBuffer.ptr, "My Exception Allocated on the STACK!");
}

I got an assertion error. I'm not sure why, but when I print out the contents of the buffer of my stack exception it differs from an exception created for the garbage collector with "new".  It looks like it has some accounting information embedded in the class instance. I figured as much but I didn't think the code that performs the "throw" would be dependent on this.

Also, this doesn't look like a very safe option because the initial values for the class members don't get set using this "construct" template.

If anyone has any other ideas or a way to fix mine let me know, thanks.
February 13, 2015
On Friday, 13 February 2015 at 19:03:10 UTC, Jonathan Marler wrote:
> This question comes from wanting to be able to throw an exception in code that is @nogc.
>
> I don't know if it's possible but I'd like to be able to throw an exception without allocating memory for the garbage collector?  You can do it in C++ so I think you should be able to in D.  One idea I had was to allocate the memory for the Exception beforehand and create the Exception class with the pre-allocated memory.  I came up with the following code:
>
> T construct(T,A...)(void* buffer, A args)
> {
>   return (cast(T)buffer).__ctor(args);
> }
>
> Now to test it:
>
> void main()
> {
>   ubyte[ __traits(classInstanceSize, Exception)] exceptionBuffer;
>   throw construct!(Exception)(exceptionBuffer.ptr, "My Exception Allocated on the STACK!");
> }
>
> I got an assertion error. I'm not sure why, but when I print out the contents of the buffer of my stack exception it differs from an exception created for the garbage collector with "new".
>  It looks like it has some accounting information embedded in the class instance. I figured as much but I didn't think the code that performs the "throw" would be dependent on this.
>
> Also, this doesn't look like a very safe option because the initial values for the class members don't get set using this "construct" template.
>
> If anyone has any other ideas or a way to fix mine let me know, thanks.

1. Throw preallocated exceptions is the way to go
2. Allocating them on the stackframe that will cease to exist by throwing is a bad idea
3. use emplace
To construct a type in preallocated memory
February 13, 2015
On Friday, 13 February 2015 at 19:03:10 UTC, Jonathan Marler wrote:
> T construct(T,A...)(void* buffer, A args)
> {
>   return (cast(T)buffer).__ctor(args);
> }

This is wrong, you need to initialize the memory first to the proper values for the class, gotten via typeid(T).init. std.conv.emplace does this correctly, either use it or look at its source to see how to do it.

>   ubyte[ __traits(classInstanceSize, Exception)] exceptionBuffer;

When the stack unwinds, this will be invalidated... I don't think stack allocated exceptions are ever a good idea. I don't think malloc exceptions are a good idea either, the catcher would  need to know to free it.

You might preallocate a pool of GC'd exceptions though, then throw the next one in the list instead of making a new one each time.
February 13, 2015
On 2/13/15 2:03 PM, Jonathan Marler wrote:
> This question comes from wanting to be able to throw an exception in
> code that is @nogc.
>
> I don't know if it's possible but I'd like to be able to throw an
> exception without allocating memory for the garbage collector? You can
> do it in C++ so I think you should be able to in D.  One idea I had was
> to allocate the memory for the Exception beforehand and create the
> Exception class with the pre-allocated memory.  I came up with the
> following code:
>
> T construct(T,A...)(void* buffer, A args)
> {
>    return (cast(T)buffer).__ctor(args);
> }
>
> Now to test it:
>
> void main()
> {
>    ubyte[ __traits(classInstanceSize, Exception)] exceptionBuffer;
>    throw construct!(Exception)(exceptionBuffer.ptr, "My Exception
> Allocated on the STACK!");
> }
>
> I got an assertion error. I'm not sure why, but when I print out the
> contents of the buffer of my stack exception it differs from an
> exception created for the garbage collector with "new".  It looks like
> it has some accounting information embedded in the class instance. I
> figured as much but I didn't think the code that performs the "throw"
> would be dependent on this.
>
> Also, this doesn't look like a very safe option because the initial
> values for the class members don't get set using this "construct" template.
>
> If anyone has any other ideas or a way to fix mine let me know, thanks.

You need to actually allocate the memory on the heap. Your data lives on the stack frame of main, which goes away as soon as main exits, and your exception is caught outside main.

-Steve
February 13, 2015
On Friday, 13 February 2015 at 19:13:02 UTC, Steven Schveighoffer wrote:
> You need to actually allocate the memory on the heap. Your data lives on the stack frame of main, which goes away as soon as main exits, and your exception is caught outside main.
>
> -Steve

Yes I am aware of this.  That doesn't mean you have to allocate on the GC heap.  You can

1. Make sure the exception is caught before the function that allocated the memory for it on the stack (not the safest thing to do but works)
2. Allocate the memory on the NON-GC heap
3. Allocate the memory to a global

February 13, 2015
On Friday, 13 February 2015 at 19:10:00 UTC, Adam D. Ruppe wrote:
> On Friday, 13 February 2015 at 19:03:10 UTC, Jonathan Marler wrote:
>> T construct(T,A...)(void* buffer, A args)
>> {
>>  return (cast(T)buffer).__ctor(args);
>> }
>
> This is wrong, you need to initialize the memory first to the proper values for the class, gotten via typeid(T).init. std.conv.emplace does this correctly, either use it or look at its source to see how to do it.

That's what I was looking for! Thanks.

>
>>  ubyte[ __traits(classInstanceSize, Exception)] exceptionBuffer;
>
> When the stack unwinds, this will be invalidated... I don't think stack allocated exceptions are ever a good idea. I don't think malloc exceptions are a good idea either, the catcher would  need to know to free it.
>
> You might preallocate a pool of GC'd exceptions though, then throw the next one in the list instead of making a new one each time.

Yes I am aware of these things. Stack allocated exception are dangerous if you let them get thrown above the function they were allocated in.  But this is easy to prevent by simply making sure you catch the exception in the function you allocate it in.  And yes malloc'd exceptions would be odd since the convention is to allocate them on the GC heap so no one would think they had to free them.  Also you could use a global...but I'm also aware of the caveats of this, between excessive TLS memory and the dangers of using shared or __gshare memory. A pool of pre-allocated exceptions is an idea I was throwing around...but with this new emplace function it opens up my options.  Thanks.


February 13, 2015
On Friday, 13 February 2015 at 19:09:43 UTC, Tobias Pankrath wrote:
> 1. Throw preallocated exceptions is the way to go

... and because noone has yet shown an explicit example:

    void myThrowingNogcFunc() @nogc {
        static const exc = new Exception("something went wrong");
        throw exc;
    }

As long as you don't need to pass a runtime argument to the constructor, there's no need for emplace acrobatics.
February 13, 2015
On 2/13/15 4:08 PM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
> On Friday, 13 February 2015 at 19:09:43 UTC, Tobias Pankrath wrote:
>> 1. Throw preallocated exceptions is the way to go
>
> .... and because noone has yet shown an explicit example:
>
>      void myThrowingNogcFunc() @nogc {
>          static const exc = new Exception("something went wrong");
>          throw exc;
>      }
>
> As long as you don't need to pass a runtime argument to the constructor,
> there's no need for emplace acrobatics.

shared static const, unless you want one per thread (doesn't make a whole lot of sense, since it's const).

-Steve
July 13, 2016
On Friday, 13 February 2015 at 21:08:58 UTC, Marc Schütz wrote:
> On Friday, 13 February 2015 at 19:09:43 UTC, Tobias Pankrath wrote:
>> 1. Throw preallocated exceptions is the way to go
>
> ... and because noone has yet shown an explicit example:
>
>     void myThrowingNogcFunc() @nogc {
>         static const exc = new Exception("something went wrong");
>         throw exc;
>     }
>
> As long as you don't need to pass a runtime argument to the constructor, there's no need for emplace acrobatics.

Why not? If the argument is static? (A string literal, surely this shouldn't be a problem and usually what is used?)


void myThrowingNogcFunc(string s)() @nogc
{
        static const exc = new Exception(s);
        throw exc;
}


?

I too am looking for nogc exceptions.

How about simply setting aside a 100kb of memory as a pool for exceptions. Seems like a lot but still under 640kb, hell, even 1MB would still be tiny.

After all, it's not like exceptions are common or happen in complex ways.

Does anyone have a proper solution to this problem? I'd like nogc exception handling with run-time generated args.




July 13, 2016
On Wednesday, 13 July 2016 at 00:57:38 UTC, Adam Sansier wrote:
> How about simply setting aside a 100kb of memory as a pool for exceptions. Seems like a lot but still under 640kb, hell, even 1MB would still be tiny.
>
> After all, it's not like exceptions are common or happen in complex ways.
>
> Does anyone have a proper solution to this problem? I'd like nogc exception handling with run-time generated args.

You shall use a static per-thread Region allocator[1] backed by Mallocator[2].
Then you just make[3] exceptions inside it and throw them.
So you can allocate and chain exceptions until you end the memory established on creation.
Whenever you don't need the exception chain anymore (i.e.: you catched them and program is back in "normal" mode, you just reset the region allocator, so you have all of your memory again, for the next exception chain).

[1] https://dlang.org/phobos/std_experimental_allocator_building_blocks_region.html
[2] https://dlang.org/phobos/std_experimental_allocator_mallocator.html
[3] https://dlang.org/phobos/std_experimental_allocator.html
« First   ‹ Prev
1 2