On Tuesday, 10 April 2018 at 06:47:53 UTC, Jonathan M Davis wrote:
As it stands, it's impossible to have thread-local memory pools. It's quite legal to construct an object as shared or thread-local and cast it to the other. In fact, it's _highly_ likely that that's how any shared object of any complexity is going to be constructed. Similarly, it's extremely common to allocate an object as mutable and then cast it to immutable (either using assumeUnique or by using a pure function where the compiler does the cast implicitly for you if it can guarantee that the return value is unique), and immutable objects are implicitly shared.


(Honest question:) Do people really cast from local to shared/immutable and expect it to work? (when ever I cast something more complex then a size_t I almost expect it to blow up... or break sometime in the future)

That said, I can understanding building a shared object from parts of local data... though I try to keep my thread barriers as thin as possible myself. (meaning I tend to copy stuff to the shared and have as few shared's as possible)


At minimum, there would have to be runtime hooks to do something like move an object between pools when it is cast to shared or immutable (or back) in order to ensure that an object was in the right pool, but if that requires copying the object rather than just moving the memory block, then it can't be done, because every pointer or reference pointing to that object would have to be rewritten (which isn't supported by the language).


A hook for local to cast(shared) could work... but would require a DIP I guess. I was hoping to make a more incremental improvement the the GC.


Also, it would be a disaster for shared, because the typical way to use shared is to protect the shared object with a mutex, cast away shared so that it can be operated on as thread-local within that section of code, and then before the mutex is released, all thread-local references then need to be gone. e.g.


synchronized(mutex)
{
    auto threadLocal = cast(MyType)mySharedObject;

    // do something with threadLocal...

// threadLocal leaves scope and is gone without being cast back
}

// all references to the shared object should now be shared


Yeah thats why I was still scanning all thread stacks and pages when marking global data.
So a shared -> local is a no op but the other way needs thought.


You really _don't_ want the shared object to move between pools
because of that cast (since it would hurt performance), and in such a situation, you don't usually cast back to shared. Rather, you have a shared reference, cast it to get a thread-local reference, and then let the thread-local reference leave scope. So, the same object temporarily has both a thread-local and a shared reference to it, and if it were moved to the thread-local pool with the cast, it would never be moved back when the
thread-local references left scope and the mutex was released.

Having synchronized classes as described in TDPL would make the above code cleaner in the cases where a synchronized class would work, but the basic concept is the same. It would still be doing a cast underneath the hood, and it would still have the same problems. It just wouldn't involve explicit casting. shared's design inherently requires casting away shared, so it just plain isn't going to play well with anything that doesn't play well with such casts - such as having thread-local heaps.


I would think a shared class would never be marked as a THREAD_LOCAL as it has a shared member.


Also, IIRC, at one point, Daniel Murphy explained to me some problem with classes with regards to the virtual table or the TypeInfo that inherently wouldn't work with trying to move it between threads. Unfortunately, I don't remember the details now, but I do remember that there's _something_ there that wouldn't work with thread-local heaps. And if anyone were to seriously try it, I expect that he could probably come up with the reasons again.

Regardless, I think that it's clear that in order to do anything with thread-local pools, we'd have to lock down the type system even further to disallow casts to or from shared or immutable, and that would really be a big problem given the inherent restrictions on those types and how shared is intended to be used. So, while it's a common idea as to how the GC could be improved, and it would be great if we could do it, I think that it goes right along with all of the other ideas that require stuff like read and write barriers everywhere and thus will never be in D's GC.

- Jonathan M Davis

Yeah I thought it would have issues, thanks for your feedback!

I'll see if I can come up with a better idea that doesn't break as much stuff.

Reply via email to