Something needs to happen with shared, and soon.
Jonathan M Davis
jmdavisProg at gmx.com
Thu Nov 15 05:00:27 PST 2012
On Thursday, November 15, 2012 14:32:47 Manu wrote:
> On 15 November 2012 13:38, Jonathan M Davis <jmdavisProg at gmx.com> wrote:
> I don't really see the difference, other than, as you say, the cast is
> explicit.
> Obviously the possibility for the situation you describe exists, it's
> equally possible with the cast, except this way, the usage pattern is made
> more convenient, the user has a convenient way to control the locks and
> most importantly, it would work with templates.
> That said, this sounds like another perfect application of 'scope'. Perhaps
> only scope parameters can receive a locked, shared thing... that would
> mechanically protect you against escape.
You could make casting away const implicit too, which would make some code
easier, but it would be a disaster, because the programer wouldn't have a clue
that it's happening in many cases, and the code would end up being very, very
wrong. Implicitly casting away shared would put you in the same boat. _Maybe_
you could get away with it in very restricted circumstances where both pure
and scope are being used, but then it becomes so restrictive that it's nearly
useless anyway. And again, it would be hidden from the programmer, when this
is something that _needs_ to be explicit. Having implicit locks happen on you
could really screw with any code trying to do explicit locks, as would be
needed anyway in all but the most basic cases.
> 2. It's often the case that you need to lock/unlock groups of stuff together
> > such that locking specific variables is of often of limited use and would
> > just
> > introduce pointless extra locks when dealing with multiple variables. It
> > would
> > also increase the risk of deadlocks, because you wouldn't have much - if
> > any -
> > control over what order locks were acquired in when dealing with multiple
> > shared variables.
>
> Your fear is precisely the state we're in now, except it puts all the work
> on the user to create and use the synchronisation objects, and also to
> assert that things are locked when they are accessed.
> I'm just suggesting some reasonably simple change that would make the
> situation more usable and safer immediately, short of waiting for all these
> fantastic designs being discussed having time to simmer and manifest.
Except that with your suggestion, you're introducing potential deadlocks which
are outside of the programmer's control, and you're introducing extra overhead
with those locks (both in terms of memory and in terms of the runtime costs).
Not to mention, it would probably cause all kinds of issues for something like
shared int* to have a mutex with it, because then its size is completely
different from int*. It also would cause even worse problems when that shared
int* was cast to int* (aside from the size issues), because all of the locking
that was happening for the shared int* was invisible. If you want automatic
locks, then use synchronized classes. That's what they're for.
Honestly, I really don't buy into the idea that it makes sense for shared to
magically make multi-threaded code work without the programmer worrying about
locks. Making it so that it's well-defined as to what's atomic is great for
code that has any chance of being lock-free, but it's still up to the
programmer to understand when locks are and aren't needed and how to use them
correctly. I don't think that it can possibly work for it to be automatic.
It's far to easy to introduce deadlocks, and it would only work in the
simplest of cases anyway, meaning that the programmer needs to understand and
properly solve the issues anyway. And if the programmer has to understand it
all to get it right, why bother adding the extra overhead and deadlock
potential caused by automatically locking anything? D provides some great
synchronization primitives. People should use them.
I think that the only things that share really needs to be solving are:
1. Indicating to the compiler via the type system that the object is not
thread-local. This properly segregates shared and unshared code and allows the
compiler to take advantage of thread locality for optimizations and avoid
optimizations with shared code that screw up threading (e.g. double-checked
locking won't work if the compiler does certain optimizations).
2. Making it explicit and well-defined as part of the language which operations
can assumed to be atomic (even if it that set of operations is very small,
having it be well-defined is valuable).
3. Ensuring sequential consistency so that it's possible to do lock-free code
when atomic operations permit it and so that there are fewer weird issues due
to undefined behavior.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list