Thread overview
__gshared as part of alias
Jan 12, 2018
Nicholas Wilson
Jan 17, 2018
Nicholas Wilson
Jan 17, 2018
Jonathan M Davis
Jan 17, 2018
Nicholas Wilson
Jan 17, 2018
Jonathan M Davis
Jan 17, 2018
Johan Engelen
Jan 17, 2018
kinke
Jan 18, 2018
Johan Engelen
January 12, 2018
Is there a way to make __gshared part of an alias?

as in

enum AddrSpace : uint
{
    Private  = 0,
    Global   = 1,
    Shared   = 2,
    Constant = 3,
    Generic  = 4,
}

struct Variable(AddrSpace as, T)
{
    T val;
    alias val this;
}
alias Global(T)   = __gshared Variable!(AddrSpace.Global,   T);

Global!float bar1; // <- still thread local
January 16, 2018
On 1/11/18 11:25 PM, Nicholas Wilson wrote:
> Is there a way to make __gshared part of an alias?

No, __gshared is a storage class, not a type constructor, so it has no meaning as part of a type:

__gshared int x;
int y;

static assert(is(typeof(x) == typeof(y)));

> as in
> 
> enum AddrSpace : uint
> {
>      Private  = 0,
>      Global   = 1,
>      Shared   = 2,
>      Constant = 3,
>      Generic  = 4,
> }
> 
> struct Variable(AddrSpace as, T)
> {
>      T val;
>      alias val this;
> }
> alias Global(T)   = __gshared Variable!(AddrSpace.Global,   T);

dmd famously doesn't complain about attributes that do nothing, as in this case.

-Steve
January 17, 2018
On Wednesday, 17 January 2018 at 02:37:07 UTC, Steven Schveighoffer wrote:
> On 1/11/18 11:25 PM, Nicholas Wilson wrote:
>> Is there a way to make __gshared part of an alias?
>
> No, __gshared is a storage class, not a type constructor, so it has no meaning as part of a type:
>
> __gshared int x;
> int y;
>
> static assert(is(typeof(x) == typeof(y)));
>
>> as in
>> 
>> enum AddrSpace : uint
>> {
>>      Private  = 0,
>>      Global   = 1,
>>      Shared   = 2,
>>      Constant = 3,
>>      Generic  = 4,
>> }
>> 
>> struct Variable(AddrSpace as, T)
>> {
>>      T val;
>>      alias val this;
>> }
>> alias Global(T)   = __gshared Variable!(AddrSpace.Global,   T);
>
> dmd famously doesn't complain about attributes that do nothing, as in this case.
>
> -Steve

I kluged this into place in LDC https://github.com/ldc-developers/ldc/pull/2509/commits/7cf6f417f95a5bffa4b18f2d8b132ca8f0e900d3#diff-33d7d08455db33f1182e3936fd2ba3f9R896
January 17, 2018
On Wednesday, January 17, 2018 07:30:30 Nicholas Wilson via Digitalmars-d- learn wrote:
> On Wednesday, 17 January 2018 at 02:37:07 UTC, Steven
>
> Schveighoffer wrote:
> > On 1/11/18 11:25 PM, Nicholas Wilson wrote:
> >> Is there a way to make __gshared part of an alias?
> >
> > No, __gshared is a storage class, not a type constructor, so it has no meaning as part of a type:
> >
> > __gshared int x;
> > int y;
> >
> > static assert(is(typeof(x) == typeof(y)));
> >
> >> as in
> >>
> >> enum AddrSpace : uint
> >> {
> >>
> >>      Private  = 0,
> >>      Global   = 1,
> >>      Shared   = 2,
> >>      Constant = 3,
> >>      Generic  = 4,
> >>
> >> }
> >>
> >> struct Variable(AddrSpace as, T)
> >> {
> >>
> >>      T val;
> >>      alias val this;
> >>
> >> }
> >> alias Global(T)   = __gshared Variable!(AddrSpace.Global,   T);
> >
> > dmd famously doesn't complain about attributes that do nothing, as in this case.
> >
> > -Steve
>
> I kluged this into place in LDC https://github.com/ldc-developers/ldc/pull/2509/commits/7cf6f417f95a5bffa4 b18f2d8b132ca8f0e900d3#diff-33d7d08455db33f1182e3936fd2ba3f9R896

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

January 17, 2018
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.

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.
January 17, 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

January 17, 2018
On Friday, 12 January 2018 at 04:25:25 UTC, Nicholas Wilson wrote:
> Is there a way to make __gshared part of an alias?

Hi Nick, how about this?

```
struct GSharedVariable(AddrSpace as, T)
{
    static __gshared T val;
    alias val this;
}
alias Global(T)   = GSharedVariable!(AddrSpace.Global,   T);

Global!float bar1; // __gshared
```

-Johan

January 17, 2018
On Wednesday, 17 January 2018 at 22:01:57 UTC, Johan Engelen wrote:
> ```
> struct GSharedVariable(AddrSpace as, T)
> {
>     static __gshared T val;
>     alias val this;
> }
> alias Global(T)   = GSharedVariable!(AddrSpace.Global,   T);
>
> Global!float bar1; // __gshared
> ```

Only 1 value per T though. ;)
January 18, 2018
On Wednesday, 17 January 2018 at 22:56:09 UTC, kinke wrote:
> On Wednesday, 17 January 2018 at 22:01:57 UTC, Johan Engelen wrote:
>> ```
>> struct GSharedVariable(AddrSpace as, T)
>> {
>>     static __gshared T val;
>>     alias val this;
>> }
>> alias Global(T)   = GSharedVariable!(AddrSpace.Global,   T);
>>
>> Global!float bar1; // __gshared
>> ```
>
> Only 1 value per T though. ;)

Ah, haha indeed, I meant without the "static", but __gshared is always "static".

```
alias Global(T)   = shared Variable!(AddrSpace.Global,   T);
```
seems to work.
https://godbolt.org/g/iDbRX7

-Johan