digitalmars.D - Synchronized Classes and Struct Members
- Andrew Wiley (28/28) Oct 24 2011 Geez, I try to write some multithreaded code and I just keep hitting the...
- Benjamin Thaut (13/41) Oct 24 2011 It can't be garantueed that a pointer to _dat is not given away at some
- Andrew Wiley (2/44) Oct 24 2011 Alright, but why can I access _dat.i without synchronization?
- Timon Gehr (5/50) Oct 25 2011 _dat.i is shared by transitivity. You can access shared variables
- Gor Gyolchanyan (20/81) Oct 25 2011 Yes. The shared-ness, const-ness or immutable-ness of the _this_
- Timon Gehr (6/15) Oct 25 2011 Yes, indeed: One that is efficient and correct in an unshared context
- Gor Gyolchanyan (7/24) Oct 25 2011 And so you can have both thread-safe synchronized heavy-duty container
- Andrew Wiley (3/9) Oct 25 2011 Except that overloading shared and non-shared methods is not allowed.
- Timon Gehr (12/21) Oct 25 2011 Yes it is.
- Andrew Wiley (3/31) Oct 25 2011 Then it's been silently fixed since this was written:
- Gor Gyolchanyan (5/16) Oct 25 2011 What? Are you sure? It's supposed to be! Sharedness of the method is
- simendsjo (16/35) Oct 25 2011 It works for simple cases at least:
Geez, I try to write some multithreaded code and I just keep hitting these: -------- module test3; struct SomeData { int i; int iPlus2() { return i + 2; } } synchronized class Bob { private: SomeData _dat; public: this() { _dat.i = 3; } property int i() { return _dat.iPlus2(); // test3.d(22): Error: function test3.SomeData.iPlus2 () is not callable using argument types () shared } } -------- This seems like it should be legal because SomeData is a value type. Accessing _dat.i directly is legal, and _dat can't possibly be shared. If I'm understanding things correctly, transitive shared shouldn't apply to value types like this, so the type of "this" when calling iPlus2 should just be SomeData.
Oct 24 2011
Am 25.10.2011 08:06, schrieb Andrew Wiley:Geez, I try to write some multithreaded code and I just keep hitting these: -------- module test3; struct SomeData { int i; int iPlus2() { return i + 2; } } synchronized class Bob { private: SomeData _dat; public: this() { _dat.i = 3; } property int i() { return _dat.iPlus2(); // test3.d(22): Error: function test3.SomeData.iPlus2 () is not callable using argument types () shared } } -------- This seems like it should be legal because SomeData is a value type. Accessing _dat.i directly is legal, and _dat can't possibly be shared. If I'm understanding things correctly, transitive shared shouldn't apply to value types like this, so the type of "this" when calling iPlus2 should just be SomeData.It can't be garantueed that a pointer to _dat is not given away at some point in the program. For example you could have a clas Foo that inherits from Bob and has a getter for _dat. Then it wouldn't be correct anymore to sasume _dat is unshared. Because of that it has been decided that trying to figure out if _dat has to be shared or not will not happen, thus its just shared always. I've already reported this some time ago and essentially it makes synchronized classes useless for me. I usually just write a normal class and but the synchronized blocks in myself. Also I use __gshared ;-) -- Kind Regards Benjamin Thaut
Oct 24 2011
On Tue, Oct 25, 2011 at 1:26 AM, Benjamin Thaut <code benjamin-thaut.de>wrote:Am 25.10.2011 08:06, schrieb Andrew Wiley: Geez, I try to write some multithreaded code and I just keep hittingAlright, but why can I access _dat.i without synchronization?these: -------- module test3; struct SomeData { int i; int iPlus2() { return i + 2; } } synchronized class Bob { private: SomeData _dat; public: this() { _dat.i = 3; } property int i() { return _dat.iPlus2(); // test3.d(22): Error: function test3.SomeData.iPlus2 () is not callable using argument types () shared } } -------- This seems like it should be legal because SomeData is a value type. Accessing _dat.i directly is legal, and _dat can't possibly be shared. If I'm understanding things correctly, transitive shared shouldn't apply to value types like this, so the type of "this" when calling iPlus2 should just be SomeData.It can't be garantueed that a pointer to _dat is not given away at some point in the program. For example you could have a clas Foo that inherits from Bob and has a getter for _dat. Then it wouldn't be correct anymore to sasume _dat is unshared. Because of that it has been decided that trying to figure out if _dat has to be shared or not will not happen, thus its just shared always. I've already reported this some time ago and essentially it makes synchronized classes useless for me. I usually just write a normal class and but the synchronized blocks in myself. Also I use __gshared ;-)
Oct 24 2011
On 10/25/2011 08:36 AM, Andrew Wiley wrote:On Tue, Oct 25, 2011 at 1:26 AM, Benjamin Thaut <code benjamin-thaut.de <mailto:code benjamin-thaut.de>> wrote: Am 25.10.2011 08:06, schrieb Andrew Wiley: Geez, I try to write some multithreaded code and I just keep hitting these: -------- module test3; struct SomeData { int i; int iPlus2() { return i + 2; } } synchronized class Bob { private: SomeData _dat; public: this() { _dat.i = 3; } property int i() { return _dat.iPlus2(); // test3.d(22): Error: function test3.SomeData.iPlus2 () is not callable using argument types () shared } } -------- This seems like it should be legal because SomeData is a value type. Accessing _dat.i directly is legal, and _dat can't possibly be shared. If I'm understanding things correctly, transitive shared shouldn't apply to value types like this, so the type of "this" when calling iPlus2 should just be SomeData. It can't be garantueed that a pointer to _dat is not given away at some point in the program. For example you could have a clas Foo that inherits from Bob and has a getter for _dat. Then it wouldn't be correct anymore to sasume _dat is unshared. Because of that it has been decided that trying to figure out if _dat has to be shared or not will not happen, thus its just shared always. I've already reported this some time ago and essentially it makes synchronized classes useless for me. I usually just write a normal class and but the synchronized blocks in myself. Also I use __gshared ;-) Alright, but why can I access _dat.i without synchronization?_dat.i is shared by transitivity. You can access shared variables without synchronization (according to TDPL, memory barriers are inserted automatically, but I don't know if DMD implements this). SomeData.iPlus2's implicit 'this' parameter is not shared.
Oct 25 2011
Yes. The shared-ness, const-ness or immutable-ness of the _this_ parameter is defined by marking the method itself shared, const or immutable respectively. But marking the method shared or immutable makes that method callable _ONLY_ for shared or immutable objects of that class or struct respectively. In order to make that struct usable from both shared and non-shared contexts, you need to have 2 overloads of that method: shared and non-shared. On Tue, Oct 25, 2011 at 4:22 PM, Timon Gehr <timon.gehr gmx.ch> wrote:On 10/25/2011 08:36 AM, Andrew Wiley wrote:eepOn Tue, Oct 25, 2011 at 1:26 AM, Benjamin Thaut <code benjamin-thaut.de <mailto:code benjamin-thaut.de>> wrote: =A0 =A0Am 25.10.2011 08:06, schrieb Andrew Wiley: =A0 =A0 =A0 =A0Geez, I try to write some multithreaded code and I just k=ypes ()=A0 =A0 =A0 =A0hitting these: =A0 =A0 =A0 =A0-------- =A0 =A0 =A0 =A0module test3; =A0 =A0 =A0 =A0struct SomeData { =A0 =A0 =A0 =A0int i; =A0 =A0 =A0 =A0int iPlus2() { =A0 =A0 =A0 =A0return i + 2; =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0synchronized class Bob { =A0 =A0 =A0 =A0private: =A0 =A0 =A0 =A0SomeData _dat; =A0 =A0 =A0 =A0public: =A0 =A0 =A0 =A0this() { =A0 =A0 =A0 =A0_dat.i =3D 3; =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0 property =A0 =A0 =A0 =A0int i() { =A0 =A0 =A0 =A0return _dat.iPlus2(); // test3.d(22): Error: function =A0 =A0 =A0 =A0test3.SomeData.iPlus2 () is not callable using argument t=value=A0 =A0 =A0 =A0shared =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0} =A0 =A0 =A0 =A0-------- =A0 =A0 =A0 =A0This seems like it should be legal because SomeData is a =ly betype. =A0 =A0 =A0 =A0Accessing _dat.i directly is legal, and _dat can't possib=ing iPlus2=A0 =A0 =A0 =A0shared. =A0 =A0 =A0 =A0If I'm understanding things correctly, transitive shared =A0 =A0 =A0 =A0shouldn't apply =A0 =A0 =A0 =A0to value types like this, so the type of "this" when call=t=A0 =A0 =A0 =A0should just be SomeData. =A0 =A0It can't be garantueed that a pointer to _dat is not given away a=t=A0 =A0some point in the program. For example you could have a clas Foo =A0 =A0that inherits from Bob and has a getter for _dat. Then it wouldn'=ed=A0 =A0be correct anymore to sasume _dat is unshared. Because of that it =A0 =A0has been decided that trying to figure out if _dat has to be shar=ut=A0 =A0or not will not happen, thus its just shared always. I've already =A0 =A0reported this some time ago and essentially it makes synchronized =A0 =A0classes useless for me. I usually just write a normal class and b=s=A0 =A0the synchronized blocks in myself. Also I use __gshared ;-) Alright, but why can I access _dat.i without synchronization?_dat.i is shared by transitivity. You can access shared variables without synchronization (according to TDPL, memory barriers are inserted automatically, but I don't know if DMD implements this). SomeData.iPlus2'=implicit 'this' parameter is not shared.
Oct 25 2011
On 10/25/2011 02:33 PM, Gor Gyolchanyan wrote:Yes. The shared-ness, const-ness or immutable-ness of the _this_ parameter is defined by marking the method itself shared, const or immutable respectively. But marking the method shared or immutable makes that method callable _ONLY_ for shared or immutable objects of that class or struct respectively. In order to make that struct usable from both shared and non-shared contexts, you need to have 2 overloads of that method: shared and non-shared.Yes, indeed: One that is efficient and correct in an unshared context and one that _actually works_ if sharing is going on. The two D implementations are the same only in toy examples, and since memory barriers have to be inserted for the shared one, the two methods necessarily compile to different machine code.
Oct 25 2011
And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier. On Tue, Oct 25, 2011 at 4:44 PM, Timon Gehr <timon.gehr gmx.ch> wrote:On 10/25/2011 02:33 PM, Gor Gyolchanyan wrote:Yes. The shared-ness, const-ness or immutable-ness of the _this_ parameter is defined by marking the method itself shared, const or immutable respectively. But marking the method shared or immutable makes that method callable _ONLY_ for shared or immutable objects of that class or struct respectively. In order to make that struct usable from both shared and non-shared contexts, you need to have 2 overloads of that method: shared and non-shared.Yes, indeed: One that is efficient and correct in an unshared context and one that _actually works_ if sharing is going on. The two D implementations are the same only in toy examples, and since memory barriers have to be inserted for the shared one, the two methods necessarily compile to different machine code.
Oct 25 2011
On Tue, Oct 25, 2011 at 8:06 AM, Gor Gyolchanyan < gor.f.gyolchanyan gmail.com> wrote:And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier.Except that overloading shared and non-shared methods is not allowed.
Oct 25 2011
On 10/25/2011 06:24 PM, Andrew Wiley wrote:On Tue, Oct 25, 2011 at 8:06 AM, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com <mailto:gor.f.gyolchanyan gmail.com>> wrote: And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier. Except that overloading shared and non-shared methods is not allowed.Yes it is. struct X{ string foo(){ return "unshared"; } string foo()shared{ return "shared"; } } void main(){ X x; shared X y; assert(x.foo() == "unshared"); assert(y.foo() == "shared"); }
Oct 25 2011
On Tue, Oct 25, 2011 at 11:59 AM, Timon Gehr <timon.gehr gmx.ch> wrote:On 10/25/2011 06:24 PM, Andrew Wiley wrote:Then it's been silently fixed since this was written: http://3d.benjamin-thaut.de/?p=18On Tue, Oct 25, 2011 at 8:06 AM, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com <mailto:gor.f.gyolchanyan **gmail.com<gor.f.gyolchanyan gmail.com>>> wrote: And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier. Except that overloading shared and non-shared methods is not allowed.Yes it is. struct X{ string foo(){ return "unshared"; } string foo()shared{ return "shared"; } } void main(){ X x; shared X y; assert(x.foo() == "unshared"); assert(y.foo() == "shared"); }
Oct 25 2011
What? Are you sure? It's supposed to be! Sharedness of the method is the sharedness of the _this_ parameter, which must cause overloading, since shared-ness of a type is part of the type. If so, it's definitely a bug. On Tue, Oct 25, 2011 at 8:24 PM, Andrew Wiley <wiley.andrew.j gmail.com> wrote:On Tue, Oct 25, 2011 at 8:06 AM, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier.Except that overloading shared and non-shared methods is not allowed.
Oct 25 2011
On 25.10.2011 18:35, Gor Gyolchanyan wrote:What? Are you sure? It's supposed to be! Sharedness of the method is the sharedness of the _this_ parameter, which must cause overloading, since shared-ness of a type is part of the type. If so, it's definitely a bug. On Tue, Oct 25, 2011 at 8:24 PM, Andrew Wiley<wiley.andrew.j gmail.com> wrote:It works for simple cases at least: class C { int i() shared { return 1; } int i() { return 2; } } void main() { auto c1 = new C(); assert(c1.i() == 2); auto c2 = cast(shared)new C(); assert(c2.i() == 1); }On Tue, Oct 25, 2011 at 8:06 AM, Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> wrote:And so you can have both thread-safe synchronized heavy-duty container and a fast and small container all in one just by overloading the appropriate methods and adding appropriate synchronization blocks in the shared ones. This is one of those "little" advantages of D over C++, that make my life _SO_ much easier.Except that overloading shared and non-shared methods is not allowed.
Oct 25 2011