== Quote from Sean Kelly ([email protected])'s article > I really need to fix this. It's a pain though, for the reasons related > to the ones you mention in "5. Shared". A tid, for example, fronts a > message queue object that has some shared interface elements and some > unshared interface elements. The shared portion, rather than labeling > functions as synchronized, uses synchronized internally to make the > mutex use as fine-grained as possible. And the logically unshared > portion only synchronizes when accessing the shared data in the object. > So to make Tid work with shared I would basically have to label the > shared methods as shared and re-evaluate the ASM output once the > compiler inserts memory barriers, and cast away shared when accessing > the message queue from within its owner thread. What gets me about all > this is that rather than helping me, the type system is working against > me. I love shared as far as its use for globals is concerned, and for > concrete variables, but not so much for classes.
Yea, shared really was a blunder at least in anything like its current form. I think it's time we just admit it and do our best to mitigate it or find a way to massively overhaul it (though I'm skeptical that it can be overhauled successfully, especially with the constraints on backwards compatibility). The default thread-local/explicit global was a great idea, as was providing a share-nothing-except-immutable message passing-based concurrency module in Phobos for people who want safe, simple, coarse-grained concurrency. Message passing is safe and good for a lot of stuff, but not everything. Once you're trying to use a threading paradigm where you need shared mutable state, though, it can't be safe and it's a waste of time for the language to try. Even if you get rid of low-level data races, you still need to worry about high-level invariants, so you've only won half the battle. Furthermore, the shared type constructor cripples shared-state multithreading so much that it's almost useless unless you do some casting, defeating its purpose. The way I see this evolving is that almost all multithreaded code in D will either: 1. Avoid sharing and just use some combination of straight message passing and immutable data. 2. Bypass shared with casts, use of core.thread, std.parallelism, etc. and just do threading the old-fashioned way. (Though shared-state multithreading can be made less dangerous by encapsulating high-level paradigms. In this respect std.parallelism represents a middle ground between the completely safe std.concurrency and the completely flexible core.thread.) Ironically, I don't see this as such a bad outcome, except for the wasted "shared" keyword and dead trees describing it. D is a systems language and there needs to be ways to do dangerous, unchecked multithreading. It's great to provide a safe but limited way to write concurrent programs (such as share-nothing message passing), it's a no-brainer to prohibit the dangerous ways in SafeD, and it's fine to require some explicitness when using the dangerous ways (such as importing a different module). However, fighting the type system every inch of the way while paying lip service to playing nice with shared is not an acceptable solution. If you need shared state then you're almost guaranteed to need to cast away shared all over the place, and if you need to do so then you may as well bypass it entirely because it's just getting in the way and not providing any safety. This is why I've been so adamant about keeping core.thread and std.parallelism the way they are unless shared massively improves in ways that I'm very skeptical are even possible.
