www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - struct dynamic allocation error

reply Dechcaudron <no-reply no-email.com> writes:
I believe there is some kind of weird issue that won't allow for 
struct instances to be dynamically allocated in a proper way via 
the 'new' keyword. It does actually allocate them and return a 
valid pointer to operate the instances, but whenever the program 
is exited I get the following exception:

core.exception.InvalidMemoryOperationError src/core/exception.d(693): Invalid
memory operation

Calling 'destroy' on the returned pointer only seems to set it to 
null, but it definitely doesn't call the destructor, neither does 
it prevent said exception from being raised. Code to reproduce:

```
import std.conv;
import std.stdio;

struct Foo
{
     int a;

     this(int a)
     {
         this.a = a;
     }

     ~this()
     {
         writeln("a is " ~ to!string(a));
     }
}

void main()
{
     Foo a = Foo(5);
     Foo* b = new Foo(10);
     writeln("Allocation complete");
     destroy(b); //Does nothing
     //Destructor for a is called
}
```
Sep 15 2016
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 15 September 2016 at 20:38:45 UTC, Dechcaudron wrote:
 I believe there is some kind of weird issue that won't allow 
 for struct instances to be dynamically allocated in a proper 
 way via the 'new' keyword. It does actually allocate them and 
 return a valid pointer to operate the instances, but whenever 
 the program is exited I get the following exception:

 [...]
I would think the GC tries to collect the object which you destroyed before. But I cannot be sure. I avoid the gc.
Sep 15 2016
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/15/2016 01:38 PM, Dechcaudron wrote:
 I believe there is some kind of weird issue that won't allow for struct
 instances to be dynamically allocated in a proper way via the 'new'
 keyword. It does actually allocate them and return a valid pointer to
 operate the instances, but whenever the program is exited I get the
 following exception:

 core.exception.InvalidMemoryOperationError src/core/exception.d(693):
 Invalid memory operation

 Calling 'destroy' on the returned pointer only seems to set it to null,
 but it definitely doesn't call the destructor, neither does it prevent
 said exception from being raised. Code to reproduce:

 ```
 import std.conv;
 import std.stdio;

 struct Foo
 {
     int a;

     this(int a)
     {
         this.a = a;
     }

     ~this()
     {
         writeln("a is " ~ to!string(a));
That operation allocates from the GC, which is not allowed when GC is doing a collection. In this case, you can replace with the following: fprintf(stdout.getFP, "a is %d\n", a);
     }
 }

 void main()
 {
     Foo a = Foo(5);
     Foo* b = new Foo(10);
     writeln("Allocation complete");
     destroy(b); //Does nothing
That's a common gotcha. Unfortunately, you're destroying the pointer itself. :/ Try this: destroy(*b); assert(b.a == int.init);
     //Destructor for a is called
 }
 ```
I describe the destroy() with struct pointer problem here: http://ddili.org/ders/d.en/memory.html#ix_memory.destroy Lastly, we have the Learn forum where this question would be more appreciated. :) Ali
Sep 15 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/15/16 4:38 PM, Dechcaudron wrote:
 I believe there is some kind of weird issue that won't allow for struct
 instances to be dynamically allocated in a proper way via the 'new'
 keyword. It does actually allocate them and return a valid pointer to
 operate the instances, but whenever the program is exited I get the
 following exception:

 core.exception.InvalidMemoryOperationError src/core/exception.d(693):
 Invalid memory operation
This is because you are allocating in the destructor.
 Calling 'destroy' on the returned pointer only seems to set it to null,
 but it definitely doesn't call the destructor, neither does it prevent
 said exception from being raised. Code to reproduce:

 ```
 import std.conv;
 import std.stdio;

 struct Foo
 {
     int a;

     this(int a)
     {
         this.a = a;
     }

     ~this()
     {
         writeln("a is " ~ to!string(a));
     }
 }

 void main()
 {
     Foo a = Foo(5);
     Foo* b = new Foo(10);
     writeln("Allocation complete");
     destroy(b); //Does nothing
     //Destructor for a is called
 }
 ```
You need to destroy structs by passing them by reference. Passing a pointer just destroys the pointer. In this example, you can destroy what b points at (and call its destructor) via: destroy(*b); However, this will not fix the issue. This is because the memory block is not marked as being destroyed already (so it will run the dtor again). Class instances have a feature whereby when you call destroy it marks the memory block as already having the destructor run. Structs do not have this feature. Classes can afford to store extra metadata, structs cannot. So the true fix here is to avoid allocating in the destructor (which is a no-no for heap-allocated items). This may work, it may not: writefln("a is %s", a); I'm not sure if any memory allocation happens there, but if not, you should be OK. -Steve
Sep 15 2016
parent Dechcaudron <no-reply no-email.com> writes:
On Thursday, 15 September 2016 at 21:21:12 UTC, Steven 
Schveighoffer wrote:
 You need to destroy structs by passing them by reference. 
 Passing a pointer just destroys the pointer.

 In this example, you can destroy what b points at (and call its 
 destructor) via:

 destroy(*b);

 However, this will not fix the issue. This is because the 
 memory block is not marked as being destroyed already (so it 
 will run the dtor again). Class instances have a feature 
 whereby when you call destroy it marks the memory block as 
 already having the destructor run. Structs do not have this 
 feature. Classes can afford to store extra metadata, structs 
 cannot.

 So the true fix here is to avoid allocating in the destructor 
 (which is a no-no for heap-allocated items). This may work, it 
 may not:

 writefln("a is %s", a);
That makes it work. Thanks a lot! I thought the GC knew what had already been destroyed, though. Thanks for letting me know that only works for classes, that'll spare me a lot of trouble. Having the destructor be run twice can be a bit of a hassle, but it's nothing I can't find a workaround for. Thank you all who replied! I'm sorry I chose the General room instead of the Learn one. I really thought this was some kind of bug (although it's pretty obvious it would've been reported by someone else if it was).
Sep 16 2016