www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Acess variable that was set by thread

reply vc <pastaflora idexoxos.ret> writes:
Hello, i have the following code, the flora contains a boolean 
zeus
in the DerivedThread the boolean zeus was set to true; but when 
i'm trying to access it
outside the thread in main it returns me false; any thoughts ?

import flora;

class DerivedThread : Thread
{
     this()
     {
         super(&run);
     }

private:
     void run()
     {
         // Derived thread running.
         zeus = true;

         while(true)
         {

         }

     }
}




void main()
{

     auto derived = new DerivedThread().start();

     writeln(zeus);

}
Aug 07 2022
next sibling parent Emanuele Torre <torreemanuele6 gmail.com> writes:
On Monday, 8 August 2022 at 01:36:45 UTC, vc wrote:
 Hello, i have the following code, the flora contains a boolean 
 zeus
 in the DerivedThread the boolean zeus was set to true; but when 
 i'm trying to access it
 outside the thread in main it returns me false; any thoughts ?

 import flora;

 class DerivedThread : Thread
 {
     this()
     {
         super(&run);
     }

 private:
     void run()
     {
         // Derived thread running.
         zeus = true;

         while(true)
         {

         }

     }
 }




 void main()
 {

     auto derived = new DerivedThread().start();

     writeln(zeus);

 }
When `writeln(zeus)` runs, `zeus = true` probably was not evaluated yet.
Aug 07 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/7/22 9:36 PM, vc wrote:
 Hello, i have the following code, the flora contains a boolean zeus
 in the DerivedThread the boolean zeus was set to true; but when i'm 
 trying to access it
 outside the thread in main it returns me false; any thoughts ?
is zeus declared just as: ```d bool zeus; ``` Because if so, it is in *thread local storage*. This is different *per thread*. This means, each thread gets its own copy, and writing to the copy in one thread doesn't affect any other threads. Note that Emanuele is also right that you have a race condition in any case. So you likely have 2 problems going on. -Steve
Aug 07 2022
parent reply vc <pastaflora idexoxos.ret> writes:
On Monday, 8 August 2022 at 02:49:06 UTC, Steven Schveighoffer 
wrote:
 On 8/7/22 9:36 PM, vc wrote:
 Hello, i have the following code, the flora contains a boolean 
 zeus
 in the DerivedThread the boolean zeus was set to true; but 
 when i'm trying to access it
 outside the thread in main it returns me false; any thoughts ?
is zeus declared just as: ```d bool zeus; ``` Because if so, it is in *thread local storage*. This is different *per thread*. This means, each thread gets its own copy, and writing to the copy in one thread doesn't affect any other threads. Note that Emanuele is also right that you have a race condition in any case. So you likely have 2 problems going on. -Steve
yes it is declared as ```d bool zeus; ``` it seems change it to working is working ```d __gshared bool zeus; ``` but as I'm new in to D, i will like to hear thoughts even if it works for me
Aug 08 2022
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On Monday, 8 August 2022 at 07:14:33 UTC, vc wrote:
 it seems change it to working is working

 ```d
  __gshared bool zeus;
  ```

 but as I'm new in to D, i will like to hear thoughts even if it 
 works for me
Never ever use `__gshared` ever. It's a glaring safety hole. Use `shared` instead. If you're running into compilation errors with `shared`, that's the compiler trying to keep you from shooting your foot off. You're supposed to think hard about thread-safety and only then cast `shared` away in the right spot. With `__gshared`, the compiler just pretends that it doesn't see that the variable is shared. You're pretty much guaranteed to produce race conditions unless you think even harder than you would have with `shared`. By the way, is there some resource that recommends `__gshared` over `shared`? It seems that many newbies reach for `__gshared` first for some reason.
Aug 08 2022
next sibling parent reply bauss <jacobbauss gmail.com> writes:
On Monday, 8 August 2022 at 10:17:57 UTC, ag0aep6g wrote:
 Never ever use `__gshared` ever.
I don't agree with this entirely, it just depends on how you use it. In general you should go with shared, but __gshared does have its places. It's only problematic when it can be changed from multiple threads, but if it's only changed from a single thread but read from many then it generally isn't a problem. To sum it up: Single-write/Single-read? __gshared Single-write/Multi-read? __gshared Multi-write/Single-read? shared Multi-write/Multi-read? shared
Aug 08 2022
parent reply ag0aep6g <anonymous example.com> writes:
On Monday, 8 August 2022 at 12:45:20 UTC, bauss wrote:
 On Monday, 8 August 2022 at 10:17:57 UTC, ag0aep6g wrote:
 Never ever use `__gshared` ever.
[...]
 To sum it up:

 Single-write/Single-read?
 __gshared

 Single-write/Multi-read?
 __gshared

 Multi-write/Single-read?
 shared

 Multi-write/Multi-read?
 shared
Nope. All of those can be race conditions. Here's a single-write, single-read one: ```d align(64) static struct S { align(1): ubyte[60] off; ulong x = 0; } __gshared S s; void main() { import core.thread: Thread; import std.conv: to; new Thread(() { foreach (i; 0 .. uint.max) { s.x = 0; s.x = -1; } }).start(); foreach (i; 0 .. uint.max) { auto x = s.x; assert(x == 0 || x == -1, to!string(x, 16)); } } ``` If you know how to access the variable safely, you can do it with `shared`. I maintain: Never ever use `__gshared` ever.
Aug 08 2022
parent reply bauss <jacobbauss gmail.com> writes:
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.
Aug 08 2022
next sibling parent kdevel <kdevel vogtner.de> writes:
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.
???
Aug 08 2022
prev sibling parent ag0aep6g <anonymous example.com> writes:
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`.
Aug 08 2022
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/8/22 6:17 AM, ag0aep6g wrote:
 On Monday, 8 August 2022 at 07:14:33 UTC, vc wrote:
 it seems change it to working is working

 ```d
  __gshared bool zeus;
  ```

 but as I'm new in to D, i will like to hear thoughts even if it works 
 for me
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. -Steve
Aug 08 2022
parent reply ag0aep6g <anonymous example.com> writes:
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.
Aug 08 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 08 2022
parent reply ag0aep6g <anonymous example.com> writes:
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.
Aug 08 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 08 2022
parent reply ag0aep6g <anonymous example.com> writes:
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.
Aug 08 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Aug 08 2022
parent ag0aep6g <anonymous example.com> writes:
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`: ```d 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`): ```d 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: ```d 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: ```d 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.
Aug 08 2022
prev sibling parent frame <frame86 live.com> writes:
On Monday, 8 August 2022 at 10:17:57 UTC, ag0aep6g wrote:

 By the way, is there some resource that recommends `__gshared` 
 over `shared`? It seems that many newbies reach for `__gshared` 
 first for some reason.
Would be also good if the specs would tell more about those "guards":
 Unlike the shared attribute, __gshared provides no safe-guards 
 against data races or other multi-threaded synchronization 
 issues.
The only thing I see is that the compiler bails about type incompatibilities but how does it help in case of synchronization/locking issues?
Aug 08 2022
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/8/22 00:14, vc wrote:

 i will like to hear thoughts even if it works
 for me
__gshared would work as well but I would consider std.concurrency first. Just a simple example: import std.stdio; import std.concurrency; import core.thread; struct Result { int value; } struct Done { } void run() { bool done = false; while (!done) { writeln("Derived thread running."); receiveTimeout(1.seconds, (Done msg) { done = true; }); } // Send the result to the owner // (I made assumptions; the thread may produce // results inside the while loop above.) ownerTid.send(Result(42)); } void main() { auto worker = spawn(&run); Thread.sleep(5.seconds); worker.send(Done()); auto result = receiveOnly!Result(); writeln("Here is the result: ", result); } Related, Roy Margalit's DConf 2022 presentation was based on traps related to sequential consistency. The video will be moved to a better place but the following link should work for now: https://youtu.be/04gJXpJ1i8M?t=5658 Ali
Aug 08 2022