Article about problems & suggestions for D 2.0

dsimcha dsimcha at yahoo.com
Tue Aug 30 13:41:39 PDT 2011


== Quote from Sean Kelly (sean at invisibleduck.org)'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.


More information about the Digitalmars-d mailing list