On Wednesday, 21 December 2011 at 05:08:27 UTC, Jonathan M Davis wrote:
On Monday, December 19, 2011 08:54:00 Jacob Carlborg wrote:
The Tango runtime has added a new method in Object, "dispose". This method is called when "scope" is used or "delete" is explicitly called.
I don't know if that would help.

That sounds a lot like using clear, though clear doesn't free memory unless the finalizer does (and I'm not sure that managing memory with finalizers actually works right now; there were issues with that previously - but it might have only been GC memory, so perhaps malloc and free would still work). The issue with them is escaping references. You can easily end up with references to data which has been destroyed. It also makes them harder to pass around unless you essentially wrap the container in ref-counted struct.

The lifetime of the container must be managed if you really want to be able to use custom allocators or have the container be as efficient as possible with regards to its memory usage in the general case. With a struct, it manages itself. It can do whatever it wants to with memory internally, and the combination of its constructor, postblit constructor, and destructor allows it to take care of it itself and clean up its memory when it's destroyed. You also don't have issues of stuff referring to a container which doesn't exist anymore (unless you add pointers to the container into the mix and then move or destroy the container).

With classes, if they're owned by the GC, then it's the GC that manages their lifetime. It destroys them when they're no longer referenced and it runs a collection cycle. You have no control over its lifetime, and even if it tries to manage its memory internally on some level, that memory won't be cleaned up until the GC collects the container itself. So, if you want to manage the lifetime of the class, you can't use a naked reference anymore. You need a way to get the GC to collect it, or you need to have it somewhere else other than the GC heap.

If you use something like Scoped, then its lifetime will be tied to the scope that it's in, but you risk references to it escaping, and limiting the container to a particular scope could be far too limiting anyway. That being the case, if you want to use an allocator other than the GC for the class itself, then you're probably going to have to stick it in a struct. That struct could then manage the container's lifetime on some level. It would probably have to use ref-counting and then call clear on the container when the ref-count hits zero. That should then allow the container to at least clean up its internal memory, assuming that that's not on the GC heap, but unless you use emplace on non-GC heap memory, the container itself still can't be collected until the GC collects it (since clear destroys it but doesn't free its memory). Not to mention, if you're using a ref-counted struct to hold a class in order to manage that class' lifetime, why on earth did you make it a class in the first place?

If what you're suggesting with dispose is that it would act like clear except that it would actually free the container, well that pretty much goes against the direction that we've been trying to go with the GC, which is to not have the programmer explicitly freeing anything on the GC heap (which is why delete is being removed from the language).

I really think that if we want deterministic destruction of containers, they need to be structs. And if we want to use custom allocators with containers, I don't see how we could reasonably expect them to be of much use unless we're expecting that programmers will explicitly call clear on them when they're done with them. So, while the fact that containers need to be reference types does imply that classes are the way to go, I think that we're going to have to go with structs if we want their memory management to be more efficient than simply letting the GC handle it.

- Jonathan M Davis

I disagree with the above conclusion. you conflate two issues that are orthogonal: a. value vs. ref semantics which is already seemed to be decided in favor of the latter and hence classes. b. memory and lifetime management

The containers should allow for (disregard the specifics of the syntax):

Container a = new(SharedMemAllocator) LinkedList();
Container b = new(MallocAllocator) LinkedList();
Container c = new(GC) LinkedList();

When adding an item to the above containers the relevant allocator will enact its policy about intermixing with other allocators - by default the item will be copied if it comes from a separate allocator. I don't see anything here that forces the use of structs instead of classes.

Reply via email to