digitalmars.D.learn - question about the implementation of Variant
- aki (21/21) Jan 03 2016 Following function will return the reference to a object Foo
- Jonathan M Davis via Digitalmars-d-learn (26/31) Jan 04 2016 As I understand it, the GC doesn't actually care about whether something...
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
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
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: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).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
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