Bug 4419 – __gshared static in class has no effect

Status
RESOLVED
Resolution
WORKSFORME
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2010-07-03T04:11:00Z
Last change time
2016-08-09T21:38:09Z
Keywords
wrong-code
Assigned to
nobody
Creator
torhu

Comments

Comment #0 by torhu — 2010-07-03T04:11:02Z
DMD 2.047. Putting __gshared in front of static has no effect. Putting it after works. This example prints "0", instead of the expected "1": --- import core.thread; import std.stdio; class A { __gshared static int x; } void main() { A.x = 1; Thread t = new Thread({ writeln(A.x); }); t.start(); } ---
Comment #1 by hoganmeier — 2011-02-23T02:51:16Z
This isn't restricted to classes: __gshared static int a; static __gshared int b; > dmd -c -vtls foo.d foo(1): a is thread local
Comment #2 by hoganmeier — 2011-02-23T06:13:36Z
Ok, part of the magic happens in StorageClassDeclaration::semantic() in attrib.c (which btw duplicates setScope!): StorageClass scstc = sc->stc; /* These sets of storage classes are mutually exclusive, * so choose the innermost or most recent one. */ if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); ... scstc |= stc; In b's case sc->stc is 0, thus nothing gets truncated and scstc = stc, which is ok cause stc correctly was set to STCstatic | STCgshared before. In a's case however sc->stc == STCgshared, so it gets cut out by the second if statement. Then scstc is set to stc which is equal to STCstatic. So there are in fact 4 issues here: 1) Why is static and __gshared mutually exclusive, doesn't __gshared imply static? 2) Why isn't shared handled in that list? 3) Why the difference regarding to the scopes? According to my debug view they are perfectly identical except for the storage class. 4) Why the difference in stc?
Comment #3 by hoganmeier — 2011-02-23T06:50:26Z
Ok 3 and 4 are caused by Parser::parseDeclDefs' layout. If static comes first "case TOKstatic" is reached, TOKstatic is added and at label Lstc2 TOKgshared is added. Then a single StorageClassDeclaration is constructed with STCstatic|STCgshared. If gshared comes first a chain of StorageClassDeclarations is constructed: StorageClassDeclaration(gshared, StorageClassDeclaration(static, int a))
Comment #4 by fawzi — 2012-10-04T15:11:55Z
I just fell in this bug, I find it *very* ugly, initially I though __gshared was fully broken, and one had to use shared (that works).
Comment #5 by andrej.mitrovich — 2012-12-20T14:37:29Z
> 1) Why is static and __gshared mutually exclusive, doesn't __gshared imply static? It implies the field is a property of the type, not the instance. It's *like* static, but static == TLS, __gshared == global. I think maybe the OP thought __gshared was a modifier you can apply to static, but it's not, __gshared can be (and should be) used alone. Both "static __gshared" and "__gshared static" should be rejected, because you can't ask the compiler "make this field thread-local *and* global, and make it a property of the type". It's either TSL or global, not both.
Comment #6 by yebblies — 2013-07-27T22:28:04Z
I agree that __gshared should imply static. I was very surprised to find this was not how it works.
Comment #7 by puneet — 2013-08-24T07:37:52Z
(In reply to comment #5) > > 1) Why is static and __gshared mutually exclusive, doesn't __gshared imply > static? > > It implies the field is a property of the type, not the instance. It's *like* > static, but static == TLS, __gshared == global. I think maybe the OP thought > __gshared was a modifier you can apply to static, but it's not, __gshared can > be (and should be) used alone. > > Both "static __gshared" and "__gshared static" should be rejected, because you > can't ask the compiler "make this field thread-local *and* global, and make it > a property of the type". It's either TSL or global, not both. But I see both "static __gshared" and "__gshared static" used in phobos as well as druntime. Many of this usages I believe should be buggy! See.... $ find . -type f -exec grep -nH -e "__gshared static" {} + ./phobos/std/parallelism.d:1039: __gshared static size_t nextInstanceIndex = 1; ./phobos/std/parallelism.d:3271: __gshared static TaskPool pool; ./druntime/src/core/thread.d:3862: __gshared static fp_t finalHandler = null;
Comment #8 by rjmcguire — 2013-09-13T03:34:04Z
(In reply to comment #6) > I agree that __gshared should imply static. I was very surprised to find this > was not how it works. There is no way that shared and static are the same. is __gshared so different from shared? sometimes I need to make a class member shared so that any thread can read/write to it, but I need the class to have multiple instances. The exact example is a class that contains a threadsafe queue which multiple threads wait for new tasks by reading and multiple threads add new tasks. I'd hate to have to always make these static. Whats interesting about dmd.2.063.2 is that the below makes a shared instance per class: class MyClass { shared chan!bool ready; this() { ready = makeChan!bool(1); } } BUT: class MyClass { shared chan!bool ready = makeChan!bool(1); } creates a static instance which is shared amoungst all classes and sub classes.
Comment #9 by andrej.mitrovich — 2013-09-13T06:44:41Z
(In reply to comment #8) > class MyClass { > shared chan!bool ready = makeChan!bool(1); > } > > creates a static instance which is shared among all classes and sub classes. I can't reproduce this, can you paste what 'chan' is, or just paste the full example? E.g.: ----- class C { shared bool ready = false; static shared bool statReady = false; } void main() { auto a = new C; auto b = new C; a.ready = true; assert(b.ready == false); assert(&a.ready !is &b.ready); a.statReady = true; assert(b.statReady == true); assert(&a.statReady is &b.statReady); } -----
Comment #10 by rjmcguire — 2013-09-13T13:58:37Z
(In reply to comment #9) > I can't reproduce this, can you paste what 'chan' is, or just paste the full > example? E.g.: > Could you try this? class B { bool v;} class C { shared B ready = new shared B; static shared B statReady = new shared B; } void main() { auto a = new C; auto b = new C; a.ready.v = true; assert(b.ready.v == false); assert(&a.ready.v !is &b.ready.v); a.statReady.v = true; assert(b.statReady.v == true); assert(&a.statReady.v is &b.statReady.v); }
Comment #11 by andrej.mitrovich — 2013-09-13T14:01:21Z
(In reply to comment #10) > (In reply to comment #9) > > > I can't reproduce this, can you paste what 'chan' is, or just paste the full > > example? E.g.: > > > > Could you try this? > class B { bool v;} > > class C > { > shared B ready = new shared B; > static shared B statReady = new shared B; > > } > > void main() > { > auto a = new C; > auto b = new C; > a.ready.v = true; > assert(b.ready.v == false); > assert(&a.ready.v !is &b.ready.v); > > a.statReady.v = true; > assert(b.statReady.v == true); > assert(&a.statReady.v is &b.statReady.v); > } This is a slightly different issue, it's the field initializer that is tricking you into thinking it's a new instance per class, but in fact that initializer is invoked at *compile-time*. Try this: ----- class B { bool v; } class C { this() { ready = new shared B; } shared B ready; static shared B statReady = new shared B; } void main() { auto a = new C; auto b = new C; a.ready.v = true; assert(b.ready.v == false); assert(&a.ready.v !is &b.ready.v); a.statReady.v = true; assert(b.statReady.v == true); assert(&a.statReady.v is &b.statReady.v); } -----
Comment #12 by ag0aep6g — 2016-08-09T21:38:09Z
(In reply to torhu from comment #0) > This example prints "0", instead of the expected "1": Prints "1" since 2.066. (In reply to Trass3r from comment #1) > __gshared static int a; > static __gshared int b; > > > dmd -c -vtls foo.d > foo(1): a is thread local No output since 2.066. Closing as WORKSFORME.