August 08, 2022

On Monday, 8 August 2022 at 13:31:04 UTC, Steven Schveighoffer wrote:

>

On 8/8/22 6:17 AM, ag0aep6g wrote:
[...]

>

Never ever use __gshared ever. It's a glaring safety hole. Use shared instead.

If you are interfacing with C, you need __gshared. But yeah, you should use shared in this case.

A quick test suggests that extern(C) extern shared works fine.

As far as I can tell, __gshared is only ever ok-ish when you want to access a shared C variable in a single-threaded program. And then you're still setting yourself up for failure if you later add more threads.

So, never ever use __gshared (in multi-threaded code) ever.

August 08, 2022

On 8/8/22 10:12 AM, ag0aep6g wrote:

>

On Monday, 8 August 2022 at 13:31:04 UTC, Steven Schveighoffer wrote:

>

On 8/8/22 6:17 AM, ag0aep6g wrote:
[...]

>

Never ever use __gshared ever. It's a glaring safety hole. Use shared instead.

If you are interfacing with C, you need __gshared. But yeah, you should use shared in this case.

A quick test suggests that extern(C) extern shared works fine.

As far as I can tell, __gshared is only ever ok-ish when you want to access a shared C variable in a single-threaded program. And then you're still setting yourself up for failure if you later add more threads.

So, never ever use __gshared (in multi-threaded code) ever.

C has no notion of shared, so it's not the right type. Putting shared on it is kind of lying, and can lead to trouble. Better to be explicit about what it is.

I'm not saying you should use __gshared liberally, or for cases where you are using this only in D. But to say you should never use it is incorrect.

-Steve

August 08, 2022

On Monday, 8 August 2022 at 14:29:43 UTC, Steven Schveighoffer wrote:

>

C has no notion of shared, so it's not the right type. Putting shared on it is kind of lying, and can lead to trouble. Better to be explicit about what it is.

Nonsense. Putting shared on a shared variable is not "lying". It doesn't matter if C makes the distinction. D does.

>

I'm not saying you should use __gshared liberally, or for cases where you are using this only in D. But to say you should never use it is incorrect.

If you're clever enough to identify a valid use case for __gshared and write correct code with it, then you're clever enough to figure out when not to listen to me.

Everyone else, don't ever use __gshared ever.

__gshared is about as bad as -boundscheck=off. They're both glaring safety holes. But people want to be propper hackers (TM). And propper hackers know how to handle these foot-guns, of course. And then they shoot their feet off.

August 08, 2022

On Monday, 8 August 2022 at 13:55:02 UTC, ag0aep6g wrote:

>
    auto x = s.x;

Your problem is here and not because it was __gshared.

You're copying the value and obviously it can be changed in the meantime, that's common sense.

You shouldn't use it like that. You should access s.x directly instead.

And in the case of shared it can leave the same result if the reading thread locks first then it will read and process the value before it's changed.

August 08, 2022

On Monday, 8 August 2022 at 17:45:03 UTC, bauss wrote:

>

On Monday, 8 August 2022 at 13:55:02 UTC, ag0aep6g wrote:

>
    auto x = s.x;

[...]

Your problem is here and not because it was __gshared.

You're copying the value and obviously it can be changed in the meantime, that's common sense.

The value of x changes while x is being read.

>

You shouldn't use it like that. You should access s.x directly instead.

//        auto x = s.x;
        assert(s.x == 0 || s.x == -1, to!string(s.x, 16));

this replaces one race by three races which even prevents spotting the reason for the triggered assertion:

$ > ./race
core.exception.AssertError@race.d(40): FFFFFFFFFFFFFFFF
>

And in the case of shared it can leave the same result if the reading thread locks first then it will read and process the value before it's changed.

???

August 08, 2022

On Monday, 8 August 2022 at 17:45:03 UTC, bauss wrote:

>

On Monday, 8 August 2022 at 13:55:02 UTC, ag0aep6g wrote:

>
    auto x = s.x;

Your problem is here and not because it was __gshared.

You're copying the value and obviously it can be changed in the meantime, that's common sense.

You shouldn't use it like that. You should access s.x directly instead.

kdevel has already addressed this.

>

And in the case of shared it can leave the same result if the reading thread locks first then it will read and process the value before it's changed.

You're right that shared doesn't fix the race condition. Without -preview=nosharedaccess, there is no difference at all. So you might as well use shared ;)

But with -preview=nosharedaccess, the code no longer compiles, and you're forced to think about how to access the shared data safely. Which is good.

So: Never ever use __gshared, and always use -preview=nosharedaccess.

August 08, 2022

On 8/8/22 10:54 AM, ag0aep6g wrote:

>

On Monday, 8 August 2022 at 14:29:43 UTC, Steven Schveighoffer wrote:

>

C has no notion of shared, so it's not the right type. Putting shared on it is kind of lying, and can lead to trouble. Better to be explicit about what it is.

Nonsense. Putting shared on a shared variable is not "lying". It doesn't matter if C makes the distinction. D does.

If you have all these nice abstractions and careful locking around accessing the data, but C doesn't, how is this better? Do you feel safer because of this?

> >

I'm not saying you should use __gshared liberally, or for cases where you are using this only in D. But to say you should never use it is incorrect.

If you're clever enough to identify a valid use case for __gshared and write correct code with it, then you're clever enough to figure out when not to listen to me.

There's nothing clever. If you want to access C globals, you should use __gshared, because that's what it is. Using shared, isn't going to save you at all.

>

__gshared is about as bad as -boundscheck=off. They're both glaring safety holes. But people want to be propper hackers (TM). And propper hackers know how to handle these foot-guns, of course. And then they shoot their feet off.

Using __gshared to share data with C is as safe as using -boundscheck=on and sending the array into C which has no such restrictions.

The conclusion here really should just be, don't use C.

-Steve

August 08, 2022

On Monday, 8 August 2022 at 19:33:14 UTC, Steven Schveighoffer wrote:

>

There's nothing clever. If you want to access C globals, you should use __gshared, because that's what it is. Using shared, isn't going to save you at all.

Yes, using shared does save you.

C might not have a shared qualifier, but C programmers still have to think about thread-safety. Calling a C function or accessing a C global always comes with some (possibly implied) contract on how to do it safely from multiple threads (the contract might be: "don't").

shared (with -preview=nosharedaccess) forces you to think about what the contract is. __gshared doesn't.

[...]

>

Using __gshared to share data with C is as safe as using -boundscheck=on and sending the array into C which has no such restrictions.

No it's not. C always being unsafe is true but irrelevant. The point is what you can/can't do on the D side.

-boundscheck=on - Can't easily mess up on the D side. C side can still mess up.
-boundscheck=off - Can easily mess up on the D side.
shared - Can't easily mess up on the D side. C side can still mess up.
__gshared - Can easily mess up on the D side.

August 08, 2022

On 8/8/22 4:04 PM, ag0aep6g wrote:

>

On Monday, 8 August 2022 at 19:33:14 UTC, Steven Schveighoffer wrote:

>

There's nothing clever. If you want to access C globals, you should use __gshared, because that's what it is. Using shared, isn't going to save you at all.

Yes, using shared does save you.

C might not have a shared qualifier, but C programmers still have to think about thread-safety. Calling a C function or accessing a C global always comes with some (possibly implied) contract on how to do it safely from multiple threads (the contract might be: "don't").

shared (with -preview=nosharedaccess) forces you to think about what the contract is. __gshared doesn't.

shared gives you a sense that the language is helping you prevent problems. Again, if C isn't playing ball, this is a lie.

>

[...]

>

Using __gshared to share data with C is as safe as using -boundscheck=on and sending the array into C which has no such restrictions.

No it's not. C always being unsafe is true but irrelevant. The point is what you can/can't do on the D side.

-boundscheck=on - Can't easily mess up on the D side. C side can still mess up.
-boundscheck=off - Can easily mess up on the D side.
shared - Can't easily mess up on the D side. C side can still mess up.
__gshared - Can easily mess up on the D side.

Bounds are defined the same in both C and D -- you have a pointer and a size, and you can't exceed that size. Yes, the data is conveyed differently, but this is trivial to understand and use.

shared doesn't fix anything on the D side. All sides must use the same mechanism to synchronize data. And there is no standard for synchronizing data.

Consider if the proper way to use such a variable is to call properlyUse(int *), it won't accept a shared int *. Now you are doubly-sure to mess up using it specifically because it's marked shared.

-Steve

August 08, 2022

On Monday, 8 August 2022 at 20:36:34 UTC, Steven Schveighoffer wrote:
[...]

>

shared gives you a sense that the language is helping you prevent problems. Again, if C isn't playing ball, this is a lie.

The C side is playing ball, by whatever rules the C library chooses.

shared (with -preview=nosharedaccess) prevents you from going on the field. Can't unwittingly commit a foul. Can't hurt yourself. You can tell the compiler with a cast that (1) you're sure you want to play, and (2) you're going to play by the rules of the C side (whatever they are).

__gshared just lets you run on the field. Don't know the rules? The compiler doesn't care. Have fun breaking your legs.

[...]

>

Consider if the proper way to use such a variable is to call properlyUse(int *), it won't accept a shared int *. Now you are doubly-sure to mess up using it specifically because it's marked shared.

With __gshared:

extern(C) extern __gshared int x;
void fun() { x = 42; } /* compiles, race condition */

I never even realize that I'm doing something dangerous, because my first naive attempt passes compilation and seems to work fine.

With shared (and -preview=nosharedaccess):

extern(C) extern shared int x;
void fun() { x = 42; } /* error */

If I remember to check the documentation, I might find out about properlyUse. As you suggest, I come up with this:

extern(C) extern shared int x;
void fun() { properlyUse(&x, 42); } /* still error because `shared` */

I'm forced to think more about thread-safety. I figure that it's ok to cast away shared in this case, because I'm calling the thread-safe properlyUse function. So:

extern(C) extern shared int x;
void fun() { properlyUse(cast(int*) &x, 42); } /* compiles, is correct */

I don't believe that people are more likely to get that right with __gshared. The call to properlyUse might look nicer without the cast, but I'm not buying that people remember to use the function without the compiler yelling at them.

Even if they get it right the first time, they're bound to slip up as time progresses. When simple, incorrect code compiles, it will surely make its way into the source files.

Thread-safety is hard to get right. We need every help we can get from the compiler. __gshared provides zero help. shared at least highlights the interesting spots.

1 2
Next ›   Last »