Why do core.atomic functions require shared
Jonathan M Davis
newsgroup.d at jmdavisprog.com
Thu Jul 5 15:14:03 UTC 2018
On Thursday, July 05, 2018 11:31:15 FeepingCreature via Digitalmars-d wrote:
> On Wednesday, 4 July 2018 at 10:47:12 UTC, Jonathan M Davis wrote:
> > At this point, to operate on anything that's shared, either
> > means using atomics or protecting the data with a mutex (be
> > that with a synchronized block / function or a mutex object)
> > and temporarily casting away shared while operating on the
> > data. Afterwards, the mutex is released, and at that point,
> > there should just be only shared references to the data. About
> > the only time that operating directly on a shared object then
> > makes sense is when it manages the atomics or mutex and
> > associated cast internally.
> >
> > ...
> > It's a very difficult problem. Synchronized classes were
> > proposed in an attempt to solve it, but even if they were fully
> > implemented, they'd only help partially, because they can only
> > strip off the outermost layer of shared. In order for the
> > compiler to cast away shared for you, it has to be able to
> > guarantee that there are no unprotected references to that
> > data, and because D doesn't have any kind of ownership system
> > in the language, we don't have a clean way to do it.
>
> Once we have DIP1000, can accessing shared class members in a
> synchronized class (optionally? implicitly?) result in scoped
> rvalues?
I'm not quite sure what you're asking exactly, but if you're wondering if
scope can help indicate that a reference hasn't escaped from a synchronized
class, then I don't think so. e.g. something like
mySynchObj.foo().func();
or
scope foo = mySynchObj.foo();
foo.bar();
would already be a problem, even though scope prevents the reference from
being kept around, because another thread could come in and access foo or
what it refers to at the same time. The mutex would have been released as
soon as the member function returned. Maybe some changes could be made to
how synchronized works so that the mutex would be left locked until the
expression terminated, and then if all you're allowed to do is call a member
function on the scope reference that's returned, then maybe it could work -
but even then, you have the problem of a reference escaping via that member
function call, and even a scoped reference is unacceptable, because then it
could escape the mutex's lock, even if it didn't last long - and that's
assuming that the mutex was made to stay locked for the duration of the
expression rather than for the duration of the member function call. scope
is designed around making sure that the lifetime of a reference, ref,
pointer, or dynamic array is not longer than the lifetime of the object it
refers to and not around the lifetime of a function call or mutex lock.
It has to be guaranteed that any time the data is accessed, the mutex
protecting it is locked. If it could be guaranteed that the data never
escapes - even temporarily - from any member function call (i.e. they never
return any references to any member variables or anything they refer to, and
all of the member functions are pure), and it's guaranteed that the data in
the member variables was all created inside the class (so any data that was
passed to a member function - including the constructor - and then assigned
to or passed to a member variable is a value type or it was a reference type
that was duped - though we don't really have a way to guarantee at this
point that data was duped), then I think that it would be possible to strip
away the other layers of shared and call free functions inside member
functions. But that would be a pretty big restriction as well as probably a
pain to code correctly in the compiler.
I don't know. Some improvements may be possible there, but it's very tricky.
You have to be able to guarantee that no data that ever has shared
implicitly removed from it is ever accessed while the mutex isn't locked,
and the more you try to allow, the harder it is to plug the holes. Much as I
agree that the casting sucks, I'm increasingly of the opinion that the extra
complications required to implicitly remove shared from even part of an
object quickly become too much to be worth it. Ultimately, I think that the
only real downside to the casting is that it requires that the programmer
get it right, whereas any time we can automate it, the compiler guarantees
it. Having the compiler guarantee it is very desirable, but the extra
restrictions required to make that guarantee can't be too restrictive, or it
quickly becomes saner to just do the cast even if it makes you verify it
yourself.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list