__gshared as part of alias

Jonathan M Davis newsgroup.d at jmdavisprog.com
Wed Jan 17 13:46:11 UTC 2018


On Wednesday, January 17, 2018 10:44:24 Nicholas Wilson via Digitalmars-d-
learn wrote:
> On Wednesday, 17 January 2018 at 07:53:24 UTC, Jonathan M Davis
>
> wrote:
> > I don't know what you're doing, and maybe __gshared is the
> > appropriate solution for what you're trying to do, but in
> > general, if you're not trying to bind to a C global variable,
> > you should be using shared, and using __gshared is risking bugs
> > precisely because it is not considered part of the type and
> > does not prevent you from using it a thread-local context. The
> > compiler will treat it as thread-local, risking subtle bugs
> > that shared would catch.
> >
> > - Jonathan M Davis
>
> I was under the impression that with shared you have to either
> a) atomically load it or
> b) cast away shared before loading it.

Normally, what you'd do is either use core.atomic, or you would lock a mutex
(or used a synchronized block) to protect access to the object, cast away
shared to do whatever you need to do, then make sure that no thread-local
references to the data exist, and release the lock. e.g.

synchronized(mutex)
{
    auto nonShared = cast()mySharedObject;
    // do stuff to nonShared
    // ...

    // no other references to nonShared should exist here
}
// lock now freed

If you don't care about synchronization across threads, then you can simply
cast away shared to mutate the object without bother with the locks, but you
do so with the knowledge that the object is not being protected against
accesses from multiple threads at the same time. How much that matters
depends on the type of object and what you're doing.

> As explained in
> https://github.com/ldc-developers/druntime/pull/114#discussion_r161472745
> that would very much add boilerplate to its most frequent use case.

You may have a use case where it makes sense to use __gshared, but you need
to remember that the compiler is going to assume that an object that isn't
marked as shared is thread-local, and it could do optimizations based on
that fact. So, you're running a definite risk any time that you use
__gshared. I would guess (but don't know) that that would primarily mean
risking the compiler assuming that the value hasn't updated when it actually
has, and if that doesn't matter in your case, you may be fine, but I don't
know what optimizations the compiler really does based on TLS right now, and
it could change over time. And as the compiler improves, odds are that it's
only going to become riskier to use __gshared for anything other than a C
global.

So, in general, while it can get a bit annoying to use shared because of the
need for casts, it's almost always better to use shared and not __gshared.

If you were using a D class or struct rather than a float, I would
_definitely_ say that __gshared was a huge mistake, but if you're using a
float, and you don't care whether its value is up-to-date or not (or
half-written if that's a risk on whatever architecture you're dealing with),
then you may be fine. But __gshared should always be chosen with extreme
caution. Unfortunately however, the extra casts and whatnot that you
typically need to use with shared often does seem to cause folks to use
__gshared instead and risk subtle bugs, because they just want the compiler
to shut up and program like they were in C/C++ and weren't using TLS
everywhere.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list