Should we have primitive fill-once variables?
david at well-typed.com
Sun Jul 1 20:14:36 UTC 2018
Thinking this through some more, I see real trade-offs between the two approaches. The names I've assigned them are for reference only, and not intended to claim or assign any credit.
Approach 1 (Feuer style)
The IVar is pretty much just a stripped-down MVar.
* Should be pretty simple to implement.
* We get object identity (equality, stable names, weak pointers) without any fuss.
* We can have mutation if we want it (i.e., QVar rather than true IVar).
* We can unsafeCoerce our way into sticking a QVar# directly into a QVar# if that's useful.
* An IVar is always an indirection.
Approach 2 (Breitner/Van Laarhoven style)
Writing to an IVar turns it into an indirection to its value.
* Long-lived filled IVars are compacted, removing indirections.
* Mutation isn't an option at all.
* Object identity looks quite tricky. Options:
- Don't have object identity for IVars.
- Require values to be forced to WHNF before being written to IVars and pull some trickery.
- Make thunks noDuplicate# when they're written to IVars and pull some trickery.
* We'll have to make sure the garbage collector doesn't goof things up. In particular, if a value has been written to an IVar, but the waiting threads haven't been woken up yet, we need to be sure not to lose the threads. Do we already have some similar logic for threads waiting on blackholes?
* We can't ever stick an IVar# directly inside an IVar#, because there's then no way for tryReadIVar to tell whether it's at the right nesting depth. I doubt this is the biggest deal, but I figured I should mention it.
All in all, I'm leaning toward proposing the Feuer style because of its relative simplicity. Someone familiar enough with GHC internals could probably implement it in a day or two, while the Breitner/Van Laarhoven approach would take much more serious thinking. But perhaps the indirection-removal is worth the price; I dunno.
More information about the ghc-devs