www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - I can has nogc and throw Exceptions?

reply "Jonathan Marler" <johnnymarler gmail.com> writes:
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.
Feb 13 2015
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
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
Feb 13 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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.
Feb 13 2015
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Feb 13 2015
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Feb 13 2015
parent "Jonathan Marler" <johnnymarler gmail.com> writes:
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.
Feb 13 2015
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Feb 13 2015
parent "Jonathan Marler" <johnnymarler gmail.com> writes:
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
Feb 13 2015