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
next sibling 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 parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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.
Jul 12 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
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
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 11:39:11 UTC, Lodovico Giaretta 
wrote:
 On Wednesday, 13 July 2016 at 00:57:38 UTC, Adam Sansier wrote:
 [...]
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
Am I going to have to do all this myself or is it already done for me somewhere?
Jul 13 2016
next sibling parent Eugene Wissner <belka caraus.de> writes:
I'm writing currently a library, that is 100%  nogc but not 
nothrow, and I slowly begin to believe that I should publish it 
already, though it isn't ready yet. At least as example.
std.experimental.allocator doesn't work nicely with  nogc. for 
example dispose calls destroy, that isn't  nogc.
I wrote a primitive native allocator for linux and some help 
functions, that replaces phobos functions till they aren't 
 nogc-ready. For example for throwing the exceptions:

void raise(T : Throwable, A...)(Allocator allocator, auto ref A 
args)
{
	auto e = make!T(allocator, args);
	throw e;
}


and you can throw then with raise!Exception("bla-bla")


On Wednesday, 13 July 2016 at 16:13:21 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 11:39:11 UTC, Lodovico Giaretta 
 wrote:
 On Wednesday, 13 July 2016 at 00:57:38 UTC, Adam Sansier wrote:
 [...]
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
Am I going to have to do all this myself or is it already done for me somewhere?
Jul 13 2016
prev sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 13 July 2016 at 16:13:21 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 11:39:11 UTC, Lodovico Giaretta 
 wrote:
 On Wednesday, 13 July 2016 at 00:57:38 UTC, Adam Sansier wrote:
 [...]
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
Am I going to have to do all this myself or is it already done for me somewhere?
It's actually quite easy. Here's the code (untested): ================================================================ import std.experimental.allocator.building_blocks.region; import std.experimental.allocator.mallocator; import std.experimental.allocator; Region(shared(Mallocator)) exception_allocator; enum EXCEPTION_MEM_SIZE = 256*1024; static this() { exception_allocator = typeof(exception_allocator)(EXCEPTION_MEM_SIZE); } ================================================================ And here is an usage example (untested, too): ================================================================ void throwingFunction() { // try to do something, but fail throw exception_allocator.make!Exception("my wonderful error message"); } void throwingThrowingFunction() { try { // try to call function, which fails throwingFunction; } catch (Exception exc) { // try to recover from failure, but generate other exception (just to show chaining) throw exception_allocator.make!Exception("I love exception chaining"); } } void main() { try { // try to call function, which fails throwingThrowingFunction; } catch (Exception exc) { // recover from failure, then deallocate the exceptions no longer needed exception_allocator.deallocateAll; } } ================================================================
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 16:28:23 UTC, Lodovico Giaretta 
wrote:
 On Wednesday, 13 July 2016 at 16:13:21 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 11:39:11 UTC, Lodovico Giaretta 
 wrote:
 On Wednesday, 13 July 2016 at 00:57:38 UTC, Adam Sansier 
 wrote:
 [...]
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
Am I going to have to do all this myself or is it already done for me somewhere?
It's actually quite easy. Here's the code (untested): ================================================================ import std.experimental.allocator.building_blocks.region; import std.experimental.allocator.mallocator; import std.experimental.allocator; Region(shared(Mallocator)) exception_allocator; enum EXCEPTION_MEM_SIZE = 256*1024; static this() { exception_allocator = typeof(exception_allocator)(EXCEPTION_MEM_SIZE); } ================================================================ And here is an usage example (untested, too): ================================================================ void throwingFunction() { // try to do something, but fail throw exception_allocator.make!Exception("my wonderful error message"); } void throwingThrowingFunction() { try { // try to call function, which fails throwingFunction; } catch (Exception exc) { // try to recover from failure, but generate other exception (just to show chaining) throw exception_allocator.make!Exception("I love exception chaining"); } } void main() { try { // try to call function, which fails throwingThrowingFunction; } catch (Exception exc) { // recover from failure, then deallocate the exceptions no longer needed exception_allocator.deallocateAll; } } ================================================================
Doesn't work. identifier expected on shared. What's the difference of simply using malloc to allocate the memory and creating the exceptions their? Seems like a long and winded way go about it or is there some benefit to using the experimental allocators?
Jul 13 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 13 July 2016 at 20:44:52 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 16:28:23 UTC, Lodovico Giaretta 
 wrote:
 It's actually quite easy. Here's the code (untested):

 ================================================================

 import std.experimental.allocator.building_blocks.region;
 import std.experimental.allocator.mallocator;
 import std.experimental.allocator;


 Region(shared(Mallocator)) exception_allocator;
 enum EXCEPTION_MEM_SIZE = 256*1024;
 static this()
 {
     exception_allocator = 
 typeof(exception_allocator)(EXCEPTION_MEM_SIZE);
 }

 ================================================================

 And here is an usage example (untested, too):

 ================================================================

 void throwingFunction()
 {
     // try to do something, but fail
     throw exception_allocator.make!Exception("my wonderful 
 error message");
 }

 void throwingThrowingFunction()
 {
     try
     {
         // try to call function, which fails
         throwingFunction;
     }
     catch (Exception exc)
     {
         // try to recover from failure, but generate other 
 exception (just to show chaining)
         throw exception_allocator.make!Exception("I love 
 exception chaining");
     }
 }

 void main()
 {
     try
     {
         // try to call function, which fails
         throwingThrowingFunction;
     }
     catch (Exception exc)
     {
         // recover from failure, then deallocate the 
 exceptions no longer needed
         exception_allocator.deallocateAll;
     }
 }
 ================================================================
Doesn't work. identifier expected on shared. What's the difference of simply using malloc to allocate the memory and creating the exceptions their? Seems like a long and winded way go about it or is there some benefit to using the experimental allocators?
`Region(shared(Mallocator))` shall be `Region!(shared Mallocator)` (again, I'm just looking at the code, didn't test it). The advantages over a simple malloc are: 1) You can change between GC allocation, malloc, mmap and other allocators by changing a single line, instead of changing every throw; 2) you can use very fast allocators, based on your needs; this example uses the Region allocator, which is way faster than a call to malloc; 3) the Region allocator has the added value of working even if there's no more memory available (because it preallocated it). In general, the allocators library provides facilities that may seem overkill for simple tasks (and in fact they are), but prove very flexible and useful for advanced uses, or to write generic highly customizable code. Of course, being experimental, this library has still some issues...
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 20:57:49 UTC, Lodovico Giaretta 
wrote:
 On Wednesday, 13 July 2016 at 20:44:52 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 16:28:23 UTC, Lodovico Giaretta 
 wrote:
 It's actually quite easy. Here's the code (untested):

 ================================================================

 import std.experimental.allocator.building_blocks.region;
 import std.experimental.allocator.mallocator;
 import std.experimental.allocator;


 Region(shared(Mallocator)) exception_allocator;
 enum EXCEPTION_MEM_SIZE = 256*1024;
 static this()
 {
     exception_allocator = 
 typeof(exception_allocator)(EXCEPTION_MEM_SIZE);
 }

 ================================================================

 And here is an usage example (untested, too):

 ================================================================

 void throwingFunction()
 {
     // try to do something, but fail
     throw exception_allocator.make!Exception("my wonderful 
 error message");
 }

 void throwingThrowingFunction()
 {
     try
     {
         // try to call function, which fails
         throwingFunction;
     }
     catch (Exception exc)
     {
         // try to recover from failure, but generate other 
 exception (just to show chaining)
         throw exception_allocator.make!Exception("I love 
 exception chaining");
     }
 }

 void main()
 {
     try
     {
         // try to call function, which fails
         throwingThrowingFunction;
     }
     catch (Exception exc)
     {
         // recover from failure, then deallocate the 
 exceptions no longer needed
         exception_allocator.deallocateAll;
     }
 }
 ================================================================
Doesn't work. identifier expected on shared. What's the difference of simply using malloc to allocate the memory and creating the exceptions their? Seems like a long and winded way go about it or is there some benefit to using the experimental allocators?
`Region(shared(Mallocator))` shall be `Region!(shared Mallocator)` (again, I'm just looking at the code, didn't test it). The advantages over a simple malloc are: 1) You can change between GC allocation, malloc, mmap and other allocators by changing a single line, instead of changing every throw;
Ok, I like!
 2) you can use very fast allocators, based on your needs; this 
 example uses the Region allocator, which is way faster than a 
 call to malloc;
I like too! But I'll have to assume you are right since I have no proof.
 3) the Region allocator has the added value of working even if 
 there's no more memory available (because it preallocated it).
Well, one could do this with malloc because one can pre-allocate it too. I figure this is why you stated 2 though because it is pre-allocated? So, really only point 1 stands, but that is probably not even valid since one can wrap the allocator in a template. This is probably exactly what is being done.. So, ultimately no real benefit except the implementation details have been removed. That's not a bad thing as long as it works ;)
 In general, the allocators library provides facilities that may 
 seem overkill for simple tasks (and in fact they are), but 
 prove very flexible and useful for advanced uses, or to write 
 generic highly customizable code.
 Of course, being experimental, this library has still some 
 issues...
Well, I will try out the code and see. You've provided an example and if it works then it should be good enough in my case. If it doesn't limit what I need to do then I'm happy ;) How is phobo's going to deal with such things when it is trying to get off the GC? It surely has to throw exceptions. Similar method or something entirely different? Thanks.
Jul 13 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 13 July 2016 at 21:12:29 UTC, Adam Sansier wrote:
 The advantages over a simple malloc are:
 1) You can change between GC allocation, malloc, mmap and 
 other allocators by changing a single line, instead of 
 changing every throw;
Ok, I like!
 2) you can use very fast allocators, based on your needs; this 
 example uses the Region allocator, which is way faster than a 
 call to malloc;
I like too! But I'll have to assume you are right since I have no proof.
 3) the Region allocator has the added value of working even if 
 there's no more memory available (because it preallocated it).
Well, one could do this with malloc because one can pre-allocate it too. I figure this is why you stated 2 though because it is pre-allocated? So, really only point 1 stands, but that is probably not even valid since one can wrap the allocator in a template. This is probably exactly what is being done.. So, ultimately no real benefit except the implementation details have been removed. That's not a bad thing as long as it works ;)
 In general, the allocators library provides facilities that 
 may seem overkill for simple tasks (and in fact they are), but 
 prove very flexible and useful for advanced uses, or to write 
 generic highly customizable code.
 Of course, being experimental, this library has still some 
 issues...
Well, I will try out the code and see. You've provided an example and if it works then it should be good enough in my case. If it doesn't limit what I need to do then I'm happy ;) How is phobo's going to deal with such things when it is trying to get off the GC? It surely has to throw exceptions. Similar method or something entirely different? Thanks.
At the end, all memory comes from one of these: GC heap, malloc, mmap, sbrk. All other allocators build on top of these (or on top of user supplied buffers, which come from these as well). What those "wrapper" allocators do is managing the given memory, either using different allocations strategies for different allocation sizes, or keeping lists of free blocks instead of returning them using "free", or other things (have a look at the docs). So at the end they do what you would manually do in C/C++ to "personalize" the allocations, with the aim of reducing waste and/or being faster. They are also arbitrarily composable on top of each other. I don't know how Phobos will handle exceptions. Maybe use reference counting (coming soon in D, maybe)? Maybe algorithms that already have to allocate will switch from using the GC to using a user-supplied custom allocator, and will use it for exceptions too. Currently I'm working on a replacement for std.xml and I'm making every component take a template parameter to specify the allocator. Initially the allocators look like a real mess, but after a couple of days playing with them, you start understanding the mechanics and at the end you really enjoy them. Changing a single parameters allows to switch between safe gc code and nogc code in the unittests, without changing the implementation.
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 21:27:16 UTC, Lodovico Giaretta 
wrote:
 On Wednesday, 13 July 2016 at 21:12:29 UTC, Adam Sansier wrote:
 [...]
Ok, I like!
 [...]
I like too! But I'll have to assume you are right since I have no proof.
 [...]
Well, one could do this with malloc because one can pre-allocate it too. I figure this is why you stated 2 though because it is pre-allocated? So, really only point 1 stands, but that is probably not even valid since one can wrap the allocator in a template. This is probably exactly what is being done.. So, ultimately no real benefit except the implementation details have been removed. That's not a bad thing as long as it works ;)
 [...]
Well, I will try out the code and see. You've provided an example and if it works then it should be good enough in my case. If it doesn't limit what I need to do then I'm happy ;) How is phobo's going to deal with such things when it is trying to get off the GC? It surely has to throw exceptions. Similar method or something entirely different? Thanks.
At the end, all memory comes from one of these: GC heap, malloc, mmap, sbrk. All other allocators build on top of these (or on top of user supplied buffers, which come from these as well). What those "wrapper" allocators do is managing the given memory, either using different allocations strategies for different allocation sizes, or keeping lists of free blocks instead of returning them using "free", or other things (have a look at the docs). So at the end they do what you would manually do in C/C++ to "personalize" the allocations, with the aim of reducing waste and/or being faster. They are also arbitrarily composable on top of each other. I don't know how Phobos will handle exceptions. Maybe use reference counting (coming soon in D, maybe)? Maybe algorithms that already have to allocate will switch from using the GC to using a user-supplied custom allocator, and will use it for exceptions too. Currently I'm working on a replacement for std.xml and I'm making every component take a template parameter to specify the allocator. Initially the allocators look like a real mess, but after a couple of days playing with them, you start understanding the mechanics and at the end you really enjoy them. Changing a single parameters allows to switch between safe gc code and nogc code in the unittests, without changing the implementation.
Ok. Is there a way to bundle allocations so that one free will work? For example, your exception handling looks good but I need to supply custom messges. If I use sformat I have to create the array for the buffer to create the message in. This means I would have to free both the exception and the string. It would be nice to be able to do this in one go. Also, could one create a struct or class for the exception so that it is automatically free'ed at the end of a catch block, if caught? Probably not without language help? Sometimes it's nice to include runtime info in an error message such as an OS error code.
Jul 13 2016
parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 13 July 2016 at 22:42:36 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 21:27:16 UTC, Lodovico Giaretta 
 wrote:
 At the end, all memory comes from one of these: GC heap, 
 malloc, mmap, sbrk. All other allocators build on top of these 
 (or on top of user supplied buffers, which come from these as 
 well). What those "wrapper" allocators do is managing the 
 given memory, either using different allocations strategies 
 for different allocation sizes, or keeping lists of free 
 blocks instead of returning them using "free", or other things 
 (have a look at the docs). So at the end they do what you 
 would manually do in C/C++ to "personalize" the allocations, 
 with the aim of reducing waste and/or being faster. They are 
 also arbitrarily composable on top of each other.

 I don't know how Phobos will handle exceptions. Maybe use 
 reference counting (coming soon in D, maybe)? Maybe algorithms 
 that already have to allocate will switch from using the GC to 
 using a user-supplied custom allocator, and will use it for 
 exceptions too.

 Currently I'm working on a replacement for std.xml and I'm 
 making every component take a template parameter to specify 
 the allocator. Initially the allocators look like a real mess, 
 but after a couple of days playing with them, you start 
 understanding the mechanics and at the end you really enjoy 
 them. Changing a single parameters allows to switch between 
  safe gc code and  nogc code in the unittests, without 
 changing the implementation.
Ok. Is there a way to bundle allocations so that one free will work? For example, your exception handling looks good but I need to supply custom messges. If I use sformat I have to create the array for the buffer to create the message in. This means I would have to free both the exception and the string. It would be nice to be able to do this in one go. Sometimes it's nice to include runtime info in an error message such as an OS error code.
As you probably saw, sformat still has some problems with nogc, because it internally uses std.uft.encode, which may throw a GC-allocated exception, but this can be solved. Not all allocators keep track of the memory ranges they allocated (e.g.: Mallocator). The ones that do (like Region) usually provide a deallocateAll method. So the idea is that you allocate all data needed by your exception (string buffers or whatever) and the exception itself with one of these allocators, and at the end of a catch you deallocateAll.
 Also, could one create a struct or class for the exception so 
 that it is automatically free'ed at the end of a catch block, 
 if caught? Probably not without language help?
I don't think it's feasible without language support for reference counted classes (which may be added sooner or later, as it has been asked and proposals have been made, and would be very useful). Also note that you don't want RAII or scope(exit) in this case but scope(success), as the exception shall not be deallocated if another one is raised in the catch block, because in D the second exception does not "overwrite" the first, but is chained to it, so that an outer catch can see both of them.
Jul 14 2016
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 next 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
prev sibling parent timotheecour <timothee.cour2 gmail.com> 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.
https://dlang.org/changelog/2.079.0.html#dip1008 ```d void main() nogc { throw new Exception("I'm nogc now"); } ```
Jul 19 2018