digitalmars.D.learn - Ownership semantics
- maik klein (37/37) Jan 31 2016 I recently asked a question about ownership semantics in D
- Matt Elkins (32/69) Jan 31 2016 Interesting. It is something in the dispose, because I changed
- Steven Schveighoffer (11/48) Jan 31 2016 The default allocator is the GC.
- Matt Elkins (4/6) Jan 31 2016 The GC can collect this memory even though there is still an
- Matt Elkins (2/8) Jan 31 2016 Or maybe it isn't root-reachable?
- Matt Elkins (27/36) Jan 31 2016 No, it is still present even if root-reachable:
- Steven Schveighoffer (6/43) Jan 31 2016 Oh, nevermind. This is actually simpler.
- maik klein (34/83) Jan 31 2016 Honestly I don't quite understand why it would refree the ptr,
- Matt Elkins (6/11) Jan 31 2016 So this implies that the UniquePtr implementation originally
I recently asked a question about ownership semantics in D https://stackoverflow.com/questions/35115702/how-do-i-express-ownership-semantics-in-d But a few minutes ago I found an answer on SO that could potentially explain a lot. http://stackoverflow.com/a/35114945/944430 Sadly it has some pseudo code in it so I implemented it with std.experimental.allocator struct UniquePtr(T) { import std.experimental.allocator; private T* ptr = null; disable this(this); // This disables both copy construction and opAssign this(Args...)(auto ref Args args){ ptr = theAllocator.make!T(args); } ~this() { theAllocator.dispose(ptr); } inout(T)* get() inout { return ptr; } // Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that" import std.algorithm.mutation; swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary return this; } } Is this code correct? One problem that I have is UniquePtr!int[int] map; will result in a memory exception and I have no idea why.
Jan 31 2016
On Sunday, 31 January 2016 at 19:34:43 UTC, maik klein wrote:I recently asked a question about ownership semantics in D https://stackoverflow.com/questions/35115702/how-do-i-express-ownership-semantics-in-d But a few minutes ago I found an answer on SO that could potentially explain a lot. http://stackoverflow.com/a/35114945/944430 Sadly it has some pseudo code in it so I implemented it with std.experimental.allocator struct UniquePtr(T) { import std.experimental.allocator; private T* ptr = null; disable this(this); // This disables both copy construction and opAssign this(Args...)(auto ref Args args){ ptr = theAllocator.make!T(args); } ~this() { theAllocator.dispose(ptr); } inout(T)* get() inout { return ptr; } // Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that" import std.algorithm.mutation; swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary return this; } } Is this code correct? One problem that I have is UniquePtr!int[int] map; will result in a memory exception and I have no idea why.Interesting. It is something in the dispose, because I changed the destructor to: [code] ~this() { writeln("Disposing ", ptr, " for this ", &this); theAllocator.dispose(ptr); writeln("Disposed ", ptr, " for this ", &this); } [/code] And I only get the disposing line, not the disposed. I tried taking my ResourceHandle struct that I pasted to you in the other thread earlier and doing the same operation with it (I had to change a const to inout and remove an extraneous 'in' marker to make it compile): [code] alias RH = ResourceHandle!(int*, (int* ptr) {theAllocator.dispose(ptr);}); RH[int] otherMap; otherMap[3] = RH(theAllocator.make!int(5)); [/code] and I get the same invalid memory operation. With either class, I avoid the error if I use a stack member or a static array, but with the associative array or just a dynamic array I get the problem. To see it in a dynamic array: [code] auto map = new UniquePtr!int[1]; map[0] = UniquePtr!int(5); [/code] Not sure what it is...still playing with it.
Jan 31 2016
On 1/31/16 2:34 PM, maik klein wrote:I recently asked a question about ownership semantics in D https://stackoverflow.com/questions/35115702/how-do-i-express-ownership-semantics-in-d But a few minutes ago I found an answer on SO that could potentially explain a lot. http://stackoverflow.com/a/35114945/944430 Sadly it has some pseudo code in it so I implemented it with std.experimental.allocator struct UniquePtr(T) { import std.experimental.allocator; private T* ptr = null; disable this(this); // This disables both copy construction and opAssign this(Args...)(auto ref Args args){ ptr = theAllocator.make!T(args); } ~this() { theAllocator.dispose(ptr); } inout(T)* get() inout { return ptr; } // Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that" import std.algorithm.mutation; swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary return this; } } Is this code correct? One problem that I have is UniquePtr!int[int] map; will result in a memory exception and I have no idea why.The default allocator is the GC. For memory that is destroyed by the GC, you cannot access to GC-allocated members in the destructor (destruction order is not guaranteed by the GC). Therefore, you should not 'dispose(ptr)' in the dtor. What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it. I'm pretty sure there is no facility in the allocator to give you enough information to properly implement this. In other words, for non-GC allocators, you *should* dispose the ptr. But how can you tell? -Steve
Jan 31 2016
On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote:What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it.The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Jan 31 2016
On Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote:On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote:Or maybe it isn't root-reachable?What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it.The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Jan 31 2016
On Sunday, 31 January 2016 at 20:11:07 UTC, Matt Elkins wrote:On Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote:No, it is still present even if root-reachable: [code] unittest { import std.algorithm; int* x; { auto map = new UniquePtr!int[1]; auto uniqueX = UniquePtr!int(5); x = uniqueX.get(); map[0] = move(uniqueX); } } [/code] [output] core.exception.InvalidMemoryOperationError src\core\exception.d(679): Invalid memory operation ---------------- Program exited with code 1 Made 632560 for this 18FD90 Disposing null for this 18FD70 Disposed null for this 18FD70 Disposing null for this 18FD90 Disposed null for this 18FD90 All unit tests have been run successfully. Disposing 632560 for this 632550 [/output]On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote:Or maybe it isn't root-reachable?What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it.The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Jan 31 2016
On 1/31/16 3:15 PM, Matt Elkins wrote:On Sunday, 31 January 2016 at 20:11:07 UTC, Matt Elkins wrote:Oh, nevermind. This is actually simpler. You can't do memory operations inside a destructor during collection. I forgot about that. But the rule I stated is still in force. -SteveOn Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote:No, it is still present even if root-reachable: [code] unittest { import std.algorithm; int* x; { auto map = new UniquePtr!int[1]; auto uniqueX = UniquePtr!int(5); x = uniqueX.get(); map[0] = move(uniqueX); } } [/code] [output] core.exception.InvalidMemoryOperationError src\core\exception.d(679): Invalid memory operation ---------------- Program exited with code 1 Made 632560 for this 18FD90 Disposing null for this 18FD70 Disposed null for this 18FD70 Disposing null for this 18FD90 Disposed null for this 18FD90 All unit tests have been run successfully. Disposing 632560 for this 632550 [/output]On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote:Or maybe it isn't root-reachable?What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it.The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Jan 31 2016
On Sunday, 31 January 2016 at 20:20:52 UTC, Steven Schveighoffer wrote:On 1/31/16 3:15 PM, Matt Elkins wrote:Honestly I don't quite understand why it would refree the ptr, but switching to malloc does seem to solve the problem. struct UniquePtr(T) { import std.experimental.allocator; private T* ptr = null; IAllocator alloc; disable this(this); // This disables both copy construction and opAssign this(Args...)(auto ref Args args){ import std.experimental.allocator.mallocator; alloc = allocatorObject(Mallocator.instance); ptr = alloc.make!T(args); } ~this() { alloc.dispose(ptr); } inout(T)* get() inout { return ptr; } // Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that" import std.algorithm.mutation; swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary return this; } }On Sunday, 31 January 2016 at 20:11:07 UTC, Matt Elkins wrote:Oh, nevermind. This is actually simpler. You can't do memory operations inside a destructor during collection. I forgot about that. But the rule I stated is still in force. -SteveOn Sunday, 31 January 2016 at 20:10:03 UTC, Matt Elkins wrote:No, it is still present even if root-reachable: [code] unittest { import std.algorithm; int* x; { auto map = new UniquePtr!int[1]; auto uniqueX = UniquePtr!int(5); x = uniqueX.get(); map[0] = move(uniqueX); } } [/code] [output] core.exception.InvalidMemoryOperationError src\core\exception.d(679): Invalid memory operation ---------------- Program exited with code 1 Made 632560 for this 18FD90 Disposing null for this 18FD70 Disposed null for this 18FD70 Disposing null for this 18FD90 Disposed null for this 18FD90 All unit tests have been run successfully. Disposing 632560 for this 632550 [/output]On Sunday, 31 January 2016 at 20:07:26 UTC, Steven Schveighoffer wrote:Or maybe it isn't root-reachable?What is likely happening is that ptr is already collected, and you are invalidly attempting to re-free it.The GC can collect this memory even though there is still an outstanding root-reachable pointer to it?
Jan 31 2016
On Sunday, 31 January 2016 at 20:20:52 UTC, Steven Schveighoffer wrote:Oh, nevermind. This is actually simpler. You can't do memory operations inside a destructor during collection. I forgot about that. But the rule I stated is still in force. -SteveSo this implies that the UniquePtr implementation originally posted might work with malloc/free calls, or some other non-GC allocator...of course, in that case one could just use std.typecons.Unique.
Jan 31 2016