digitalmars.D.learn - Creating a reference counted type?
- Gary Willoughby (30/30) Jun 12 2016 I'm wondering if it's this easy to create a reference counted
- Gary Willoughby (3/10) Jun 12 2016 Actually, this doesn't puzzle me at all! I think I must be tired.
- ketmar (20/20) Jun 12 2016 this won't work at all. let's insert `writeln("FREE!");` in dtor,
- Gary Willoughby (3/8) Jun 12 2016 Hmmm. I thought it looked *too* simple. Have you any idea if
- ketmar (42/44) Jun 12 2016 yes. you have to turn your refcount to pointer. ;-)
- Gary Willoughby (2/5) Jun 12 2016 Thanks for the replies guys.
- ZombineDev (9/17) Jun 12 2016 You need to allocate the ref count on the heap so it is actually
I'm wondering if it's this easy to create a reference counted type: struct Foo { int _refCount = 1; this(...) { // allocate resources, etc. } this(this) { this._refCount++; } ~this() { this._refCount--; if (this._refCount == 0) { // free resources. } } } Are there any other considerations I need to handle? Another thing that is puzzling me is that when creating an instance of the above struct and passing as an argument to a function, the copy constructor is called and the reference count is incremented. This is expected. However, when the function returns, the destructor is called (on the copy) and the reference count lowered. How does the original instance know of the updated reference count from the copy?
Jun 12 2016
On Sunday, 12 June 2016 at 14:29:19 UTC, Gary Willoughby wrote:Another thing that is puzzling me is that when creating an instance of the above struct and passing as an argument to a function, the copy constructor is called and the reference count is incremented. This is expected. However, when the function returns, the destructor is called (on the copy) and the reference count lowered. How does the original instance know of the updated reference count from the copy?Actually, this doesn't puzzle me at all! I think I must be tired. Ignore this paragraph, it doesn't make sense. lol.
Jun 12 2016
this won't work at all. let's insert `writeln("FREE!");` in dtor, and test it: auto foo (Foo foo) { version(dump) writeln(foo._refCount); return foo; } void main () { auto f = foo(Foo()); version(dump) writeln(f._refCount); } it prints "FREE" once, so it looks like the whole thing is working. but now let's try this with `-version=dump`: 1 FREE! 2 ahem... wut?! we have one copy of our struct freed half the way, and another copy has refcount of 2, so it won't be freed at all. it doesn't so innocent as it looks: we may try to use `f` in `main`... just to find out that resources was mysteriously freed, and refcount is total garbage.
Jun 12 2016
On Sunday, 12 June 2016 at 14:45:12 UTC, ketmar wrote:ahem... wut?! we have one copy of our struct freed half the way, and another copy has refcount of 2, so it won't be freed at all. it doesn't so innocent as it looks: we may try to use `f` in `main`... just to find out that resources was mysteriously freed, and refcount is total garbage.Hmmm. I thought it looked *too* simple. Have you any idea if there is a simple solution to this?
Jun 12 2016
On Sunday, 12 June 2016 at 14:49:18 UTC, Gary Willoughby wrote:Hmmm. I thought it looked *too* simple. Have you any idea if there is a simple solution to this?yes. you have to turn your refcount to pointer. ;-) the cause of "misbehave" is the fact that there can exist several copies of the struct made in different program points, and they all have *independent* refcounter. but all those copies should have the *same* refcounter. the easiest way to solve this is to allocate refcounter separately, and only share *pointer* to it, like this: struct Foo { int* refcount; void allocResources () { import core.stdc.stdlib : malloc; assert(refcount is null); refcount = cast(int*)malloc(int.size); *refcount = 1; // init other resources } this (this) { if (refcount !is null) *refcount += 1; } // we need to do this in opAssign() and in dtor, hence the function private void decRef () { if (refcount !is null) { if ((*refcount -= 1) == 0) { import core.stdc.stdlib : free; free(refcount); // free resources } } } ~this (this) { decRef(); } // yes, we need this too void opAssign (Foo src) { *src.refcount += 1; // it is important to increase it first, in case `src` and `this` are sharing refcount decRef(); // release our resources *refcount = *src.refcount; // copy other handles and so on } } this is basically how refcounted structs are done. note that i just typed the code into reply box, so it may not compile or contain some small bugs, but i think you got the idea.
Jun 12 2016
On Sunday, 12 June 2016 at 15:05:53 UTC, ketmar wrote:this is basically how refcounted structs are done. note that i just typed the code into reply box, so it may not compile or contain some small bugs, but i think you got the idea.Thanks for the replies guys.
Jun 12 2016
On Sunday, 12 June 2016 at 14:49:18 UTC, Gary Willoughby wrote:On Sunday, 12 June 2016 at 14:45:12 UTC, ketmar wrote:You need to allocate the ref count on the heap so it is actually shared between all copies of the struct. Also if you want to share the reference between multiple threads, you need to use core.atomic.atomicOp for incrementing and decrementing. See std.typecons.RefCounteed's implementation for reference https://github.com/dlang/phobos/blob/v2.071.0/std/typecons.d#L4712 You can use AffixAllocator http://dlang.org/phobos-prerelease/std_experimental_allocator_building_blocks_a fix_allocator.html, so that ref count is automatically placed before or after the actual objected that is being ref counted. Just note that AffixAllocator is safe to use only by a single thread.ahem... wut?! we have one copy of our struct freed half the way, and another copy has refcount of 2, so it won't be freed at all. it doesn't so innocent as it looks: we may try to use `f` in `main`... just to find out that resources was mysteriously freed, and refcount is total garbage.Hmmm. I thought it looked *too* simple. Have you any idea if there is a simple solution to this?
Jun 12 2016