www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - how does 'shared' affect member variables?

reply bitwise <bitwise.pvt gmail.com> writes:
What does 'shared' do to member variables?

It makes sense to me to put it on a global variable, but what sense does  
it make putting it on a member of a class? What happens if you try to  
access a member of a class/struct instance from another thread that is not  
marked 'shared'?

Also, I wasn't able to find any thorough documentation on shared, so if  
someone has a link, that would be helpful.

  Thanks
    Bit
May 09 2015
next sibling parent reply "Mike" <none none.com> writes:
On Saturday, 9 May 2015 at 18:41:59 UTC, bitwise wrote:

 Also, I wasn't able to find any thorough documentation on 
 shared, so if someone has a link, that would be helpful.
Here are a few interesting links: Iain Buclaw (lead developer for GDC) with his interpretation: http://forum.dlang.org/post/mailman.739.1431034764.4581.digitalmars-d puremagic.com Andrei Alexandrescu highlighting a critical flaw with `shared` http://forum.dlang.org/post/lruc3n$at1$1 digitalmars.com "The truth about shared" http://p0nce.github.io/d-idioms/#The-truth-about-shared Interesting deprecation warning in latest compiler (See compiler output): http://goo.gl/EGvK72 But I don't know what the semantics are *supposed* to be, and I get the impression noone else knows either. I'll be watching this thread myself to see if someone can provide some insight. Mike
May 09 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Sat, 09 May 2015 15:38:05 -0400, Mike <none none.com> wrote:

 On Saturday, 9 May 2015 at 18:41:59 UTC, bitwise wrote:

 Also, I wasn't able to find any thorough documentation on shared, so if  
 someone has a link, that would be helpful.
Here are a few interesting links: Iain Buclaw (lead developer for GDC) with his interpretation: http://forum.dlang.org/post/mailman.739.1431034764.4581.digitalmars-d puremagic.com Andrei Alexandrescu highlighting a critical flaw with `shared` http://forum.dlang.org/post/lruc3n$at1$1 digitalmars.com "The truth about shared" http://p0nce.github.io/d-idioms/#The-truth-about-shared Interesting deprecation warning in latest compiler (See compiler output): http://goo.gl/EGvK72 But I don't know what the semantics are *supposed* to be, and I get the impression noone else knows either. I'll be watching this thread myself to see if someone can provide some insight. Mike
So it seems that although it's not properly implemented, it's still not completely benign, right? I am trying to create a shared queue/array of delegates that run on the main thread. I don't know if D's 'shared' semantics will affect it or not, and whether or not casting to/from shared will cause problems with 'cas' or 'atomicStore' Would the following code work as expected? A simplified example: struct SpinLock { private int _lock = 0; void lock() { while(!cas(cast(shared(int)*)&_lock, 0, 1)) {} } void unlock() { atomicStore(*cast(shared(int)*)&_lock, 0); } } struct LockGuard(T) { private T* _lock; this(ref T lock) { _lock = &lock; _lock.lock(); } ~this() { _lock.unlock(); } } class App { public: property public static App instance() { return _instance; } this() { assert(!_instance); _instance = this; } ~this() { _instance = null; } void run(void delegate() dg) { auto lk = LockGuard!SpinLock(_lock); _actions.insertBack(dg); } void update() { auto lk = LockGuard!SpinLock(_lock); foreach(act; _actions) act(); _actions.clear(); } package: __gshared App _instance = null; SpinLock _lock; Array!(void delegate()) _actions; } Thread1: App.instance.run({ doSomething1(); }); Thread2: App.instance.run({ doSomething2(); }); Main Thread: App app = new MyAppType(); while(true) { app.update(); } Thanks, Bit
May 09 2015
parent reply "Mike" <none none.com> writes:
On Saturday, 9 May 2015 at 20:17:59 UTC, bitwise wrote:
 On Sat, 09 May 2015 15:38:05 -0400, Mike <none none.com> wrote:

 On Saturday, 9 May 2015 at 18:41:59 UTC, bitwise wrote:

 Also, I wasn't able to find any thorough documentation on 
 shared, so if someone has a link, that would be helpful.
Here are a few interesting links: Iain Buclaw (lead developer for GDC) with his interpretation: http://forum.dlang.org/post/mailman.739.1431034764.4581.digitalmars-d puremagic.com Andrei Alexandrescu highlighting a critical flaw with `shared` http://forum.dlang.org/post/lruc3n$at1$1 digitalmars.com "The truth about shared" http://p0nce.github.io/d-idioms/#The-truth-about-shared Interesting deprecation warning in latest compiler (See compiler output): http://goo.gl/EGvK72 But I don't know what the semantics are *supposed* to be, and I get the impression noone else knows either. I'll be watching this thread myself to see if someone can provide some insight. Mike
So it seems that although it's not properly implemented, it's still not completely benign, right? I am trying to create a shared queue/array of delegates that run on the main thread. I don't know if D's 'shared' semantics will affect it or not, and whether or not casting to/from shared will cause problems with 'cas' or 'atomicStore' Would the following code work as expected?
I'm not sure if it would or no, as I haven't used these features of D before, but it looks like what you are trying to implement is what `synchronized` already provides: http://ddili.org/ders/d.en/concurrency_shared.html#ix_concurrency_shared.synchronized Mike
May 09 2015
parent bitwise <bitwise.pvt gmail.com> writes:
On Sat, 09 May 2015 21:32:42 -0400, Mike <none none.com> wrote:

 it looks like what you are trying to implement is what `synchronized`  
 already provides:   
 http://ddili.org/ders/d.en/concurrency_shared.html#ix_concurrency_shared.synchronized

 Mike
Yes, but synchronized uses a mutex. Spin locks can perform better in situations where there won't be much contention for the lock. Bit
May 09 2015
prev sibling next sibling parent reply "tcak" <tcak gmail.com> writes:
On Saturday, 9 May 2015 at 18:41:59 UTC, bitwise wrote:
 What does 'shared' do to member variables?

 It makes sense to me to put it on a global variable, but what 
 sense does it make putting it on a member of a class? What 
 happens if you try to access a member of a class/struct 
 instance from another thread that is not marked 'shared'?

 Also, I wasn't able to find any thorough documentation on 
 shared, so if someone has a link, that would be helpful.

  Thanks
    Bit
If a variable/class/struct etc is not shared, for variables and struct, you find their initial value. For class, you get null. For first timers (I started using shared keyword more than 2 years ago), do not forget that: a shared method is all about saying that this method is defined for shared object. So, do not get confused. It happened to me a lot. Bad part of shared is that, you will be repeating it again, and again, and again, and again, everywhere. So, try to be patient if you are going to be using it for a long time. Stupidly, shared variables' value cannot be increased/decreased directly. Compiler says it is deprecated, and tells me to use core.atomic.atomicop. You will see this as well. Hey compiler! I know 100% that no other thing will be touching this variable.
May 09 2015
next sibling parent "anonymous" <anonymous example.com> writes:
On Saturday, 9 May 2015 at 19:59:58 UTC, tcak wrote:
 Stupidly, shared variables' value cannot be increased/decreased 
 directly. Compiler says it is deprecated, and tells me to use 
 core.atomic.atomicop. You will see this as well.
How's that stupid? Sounds like the compiler is doing its job guarding you from races.
 Hey compiler! I know 100% that no other thing will be touching 
 this variable.
Informing the compiler about this is done by casting shared away.
May 09 2015
prev sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Sat, 09 May 2015 15:59:57 -0400, tcak <tcak gmail.com> wrote:
 If a variable/class/struct etc is not shared, for variables and struct,  
 you find their initial value. For class, you get null.

 For first timers (I started using shared keyword more than 2 years ago),  
 do not forget that: a shared method is all about saying that this method  
 is defined for shared object. So, do not get confused. It happened to me  
 a lot.

 Bad part of shared is that, you will be repeating it again, and again,  
 and again, and again, everywhere. So, try to be patient if you are going  
 to be using it for a long time.

 Stupidly, shared variables' value cannot be increased/decreased  
 directly. Compiler says it is deprecated, and tells me to use  
 core.atomic.atomicop. You will see this as well. Hey compiler! I know  
 100% that no other thing will be touching this variable.
using the SpinLock and LockGuard from my code above, I created a working test case. It works as expected, with no shared keyword on anything. The variable "App._instance" is __gshared, but that's about all. /////////////////////////// import spinlock; import std.stdio; import std.concurrency; import std.container; import core.thread; class App { SpinLock _lock; Array!(void delegate()) _actions; __gshared App _instance = null; this() { assert(!_instance); _instance = this; } ~this() { _instance = null; } property public static App instance() { return _instance; } void run(void delegate() dg) { auto lk = LockGuard!SpinLock(_lock); _actions.insertBack(dg); writeln("queued delegate"); } void update() { writeln("updating"); auto lk = LockGuard!SpinLock(_lock); foreach(act; _actions) act(); _actions.clear(); } } void WorkerThread() { writeln("started worker, going to sleep"); Thread.sleep(500.msecs); App.instance.run({ writeln("running delegate queued from thread"); }); } void main() { App app = new App(); Thread workerThread = new Thread(&WorkerThread).start(); int ms = 0; while(ms < 1000) { app.update(); Thread.sleep(100.msecs); ms += 100; } workerThread.join(); } ////////////// OUTPUT: updating started worker, going to sleep updating updating updating updating queued delegate updating running delegate queued from thread updating updating updating updating ////////////// No null references or 'init' values. A little confused now, but at least it works. Finally, I tested to see if the lock was actually working: void WorkerThread() { writeln("worker aquiring lock"); App.instance._lock.lock(); writeln("worker aquired lock"); App.instance._lock.unlock(); } void main() { App app = new App(); writeln("main locked"); App.instance._lock.lock(); Thread workerThread = new Thread(&WorkerThread).start(); writeln("main sleeping"); Thread.sleep(2.seconds); writeln("main woke"); App.instance._lock.unlock(); writeln("main unlocked"); workerThread.join(); } As expected, output was: main locked main sleeping worker aquiring lock main woke main unlocked worker aquired lock And no 'shared' in sight. So now I'm confused as to when it has the affect that you were describing with null/init. Bit
May 09 2015
prev sibling parent "anonymous" <anonymous example.com> writes:
On Saturday, 9 May 2015 at 18:41:59 UTC, bitwise wrote:
 What does 'shared' do to member variables?
Makes them `shared`. :P
 It makes sense to me to put it on a global variable, but what 
 sense does it make putting it on a member of a class?
Globals are not the only way to pass data to other threads. E.g., you can std.concurrency.send a shared Object: ---- import core.thread: thread_joinAll; import std.concurrency; class C {int x;} void main() { auto c = new shared C; c.x = 1; auto tid = spawn(() { receive( (shared C c) {c.x = 2;} ); }); send(tid, c); thread_joinAll(); assert(c.x == 2); } ---- That shared C could come from a class/struct member, of course.
 What happens if you try to access a member of a class/struct 
 instance from another thread that is not marked 'shared'?
I think you're not supposed to be able to do that.
May 09 2015