www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - question about the implementation of Variant

reply aki <aki google.com> writes:
Following function will return the reference to a object Foo 
embedded in a Variant.

class Foo {}
Variant fun() {
	Variant v;
	v = new Foo();
	return v;
}

According to the source code of VariantN.opAssign,
the assignment is done by:

memcpy(&store, &rhs, rhs.sizeof);
fptr = &handler!(T);

where rhs is new Foo(), store is embedded storage in Variant,
and fptr is accessor function also work as data tag.
Very efficient implementation.
But wait, how does GC detect there still be a live reference to 
the object Foo?
Because store is just a fix sized array of bytes.
ubyte[size] store;
GC cannot be aware of the reference, right?

Thanks, aki
Jan 03 2016
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, January 04, 2016 07:30:50 aki via Digitalmars-d-learn wrote:
 But wait, how does GC detect there still be a live reference to
 the object Foo?
 Because store is just a fix sized array of bytes.
 ubyte[size] store;
 GC cannot be aware of the reference, right?
As I understand it, the GC doesn't actually care about whether something is a pointer when it tries to figure out whether something refers to something - or at least, it'll treat integers as if they were pointers so that if you tried to do something like size_t i; { auto p = new int(5); i = cast(size_t)p; } // GC will not collect p even if it runs now then the fact that i matches the value of the address that p points to is enough for the GC to not collect the memory pointed to by p, even if there are no longer any pointers referring to it. This prevents problems when you do stuff like cast pointers to integers to store their values (which normally is crazy but on rare occasions makes sense). The downside is that the GC then has to treat all integers as if they were pointers, so if you have an integer whose value happens to match that of a memory address in GC-allocated memory (a so called false pointer), then that memory won't be freed, even if nothing is really pointing to it anymore. Fortunately, however, false pointers are primarily limited to 32-bit programs, and 64-bit programs don't have that problem because of how large their address space is (but 32-bit programs which allocate most of their address space can definitely run into problems where memory that should be freed isn't thanks to false pointers). - Jonathan M Davis
Jan 04 2016
parent reply Kapps <opantm2+spam gmail.com> writes:
On Monday, 4 January 2016 at 09:13:25 UTC, Jonathan M Davis wrote:
 On Monday, January 04, 2016 07:30:50 aki via 
 Digitalmars-d-learn wrote:
 But wait, how does GC detect there still be a live reference to
 the object Foo?
 Because store is just a fix sized array of bytes.
 ubyte[size] store;
 GC cannot be aware of the reference, right?
As I understand it, the GC doesn't actually care about whether something is a pointer when it tries to figure out whether something refers to something - or at least, it'll treat integers as if they were pointers so that if you tried to do something like size_t i; { auto p = new int(5); i = cast(size_t)p; } // GC will not collect p even if it runs now then the fact that i matches the value of the address that p points to is enough for the GC to not collect the memory pointed to by p, even if there are no longer any pointers referring to it. This prevents problems when you do stuff like cast pointers to integers to store their values (which normally is crazy but on rare occasions makes sense). The downside is that the GC then has to treat all integers as if they were pointers, so if you have an integer whose value happens to match that of a memory address in GC-allocated memory (a so called false pointer), then that memory won't be freed, even if nothing is really pointing to it anymore. Fortunately, however, false pointers are primarily limited to 32-bit programs, and 64-bit programs don't have that problem because of how large their address space is (but 32-bit programs which allocate most of their address space can definitely run into problems where memory that should be freed isn't thanks to false pointers). - Jonathan M Davis
That's only because we don't have a precise garbage collector and can't be relied upon. It's more of a bug / limitation rather than something to actually use. In the case of std.variant, it's not just byte[size] store, it's actually a union: union { ubyte[size] store; // conservatively mark the region as pointers static if (size >= (void*).sizeof) void*[size / (void*).sizeof] p; } Which tells the garbage collector that it may be pointers there, making it valid even for precise garbage collectors (which would have to conservatively handle such a union).
Jan 04 2016
parent aki <aki google.com> writes:
Thank you, Jonathan. Now I understand.

On Monday, 4 January 2016 at 17:34:47 UTC, Kapps wrote:
 union
 {
     ubyte[size] store;
     // conservatively mark the region as pointers
     static if (size >= (void*).sizeof)
         void*[size / (void*).sizeof] p;
 }
Interesting to know the way to make GC detect the presence of the potential pointer. Regards, aki.
Jan 04 2016