www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Shared, but synchronisation is not desired for multithreading

reply tcak <1ltkrs+3wyh1ow7kzn1k sharklasers.com> writes:
If you ignore the discouraged __gshared keyword, to be able to 
share a variable between threads, you need to be using "shared" 
keyword.

While designing your class with "shared" methods, the compiler 
directly assumes that objects of this class must be protected 
against threading problems.

There can be three USAGEs of a class object:

1. It will be non-shared. So, it is stored in TLS, and only one 
thread can access it.

2. It will be shared. But programmer knows that the object is 
designed as "shared" with the purpose of reading its value from 
multiple threads.

3. It will be shared. But the object must be synchronised. 
Because programmer knows that multiple threads will be reading 
from and writing to object.

Currently, in a normal coding environment (I am not talking about 
using extra parameters, increasing complexity etc), 
distinguishing between 2 and 3 does not seem like possible. You 
prepare your shared class, and its methods are designed to be 
either sycnhronised or not synchronised. There is no middle point 
unless you define the same method with different names, or use a 
flag like "bool run_this_method_synchronised_please".

So, what I did is using UDA for this with the name  ThreadSafe. 
e.g.

 ThreadSafe auto myObject = new shared MyClass();

In a method of the class, I make the declaration as following:

public void foo() shared{
	static if( std.traits.hasUDA!( this, ThreadSafe ) ){
		// lock mutex
		scope(exit){
			// unlock mutex
		}
	}

	// do your normal operations
}

This way, if the object is desired to be doing synchronisation, 
you only add an attribute to it.

There are some small problems here, those are related to D's 
implementation right now:

1. There is no standard way of saying  ThreadSafe. You are 
supposed to be defining it. If language was to be defining a 
standard attribute as  ThreadSafe, it could be used everywhere 
for this purpose.

2. If a method is defined as shared, compiler immediately warns 
the programmer to use core.atomic.atomicOp. If codes are already 
being designed as thread-safe by the programmer, normal variable 
operations could be used without any concern.

3. As far as I remember, there were some talks about synchronized 
keyword not being used much. Maybe its usage could be changed to 
support this  ThreadSafe system.
Jun 04 2016
parent reply Alex Parrill <initrd.gz gmail.com> writes:
On Saturday, 4 June 2016 at 15:11:51 UTC, tcak wrote:
 If you ignore the discouraged __gshared keyword, to be able to 
 share a variable between threads, you need to be using "shared" 
 keyword.

 While designing your class with "shared" methods, the compiler 
 directly assumes that objects of this class must be protected 
 against threading problems.

 There can be three USAGEs of a class object:

 1. It will be non-shared. So, it is stored in TLS, and only one 
 thread can access it.

 2. It will be shared. But programmer knows that the object is 
 designed as "shared" with the purpose of reading its value from 
 multiple threads.

 3. It will be shared. But the object must be synchronised. 
 Because programmer knows that multiple threads will be reading 
 from and writing to object.

 Currently, in a normal coding environment (I am not talking 
 about using extra parameters, increasing complexity etc), 
 distinguishing between 2 and 3 does not seem like possible. You 
 prepare your shared class, and its methods are designed to be 
 either sycnhronised or not synchronised. There is no middle 
 point unless you define the same method with different names, 
 or use a flag like "bool run_this_method_synchronised_please".

 So, what I did is using UDA for this with the name  ThreadSafe. 
 e.g.

  ThreadSafe auto myObject = new shared MyClass();

 In a method of the class, I make the declaration as following:

 public void foo() shared{
 	static if( std.traits.hasUDA!( this, ThreadSafe ) ){
 		// lock mutex
 		scope(exit){
 			// unlock mutex
 		}
 	}

 	// do your normal operations
 }

 This way, if the object is desired to be doing synchronisation, 
 you only add an attribute to it.

 There are some small problems here, those are related to D's 
 implementation right now:

 1. There is no standard way of saying  ThreadSafe. You are 
 supposed to be defining it. If language was to be defining a 
 standard attribute as  ThreadSafe, it could be used everywhere 
 for this purpose.

 2. If a method is defined as shared, compiler immediately warns 
 the programmer to use core.atomic.atomicOp. If codes are 
 already being designed as thread-safe by the programmer, normal 
 variable operations could be used without any concern.

 3. As far as I remember, there were some talks about 
 synchronized keyword not being used much. Maybe its usage could 
 be changed to support this  ThreadSafe system.
If the method is thread-safe, it should be marked as shared. Otherwise, it should not be shared. I think you're trying to mark functions that aren't actually thread safe but you call in a `synchronized` context as shared, when they should not be. If you've made guarantees that the shared object you are modifying can only be accessed by one thread, you can cast it to unshared, and call its thread-unsafe methods (which are now safe). I think there were plans to get `synchronized` to do this for you, but it doesn't, which makes it fairly unwieldy. (It also doesn't help that many "thread-safe" functions in D aren't marked as shared where they really ought to be, ex. all the functions in core.sync.mutex)
Jun 04 2016
parent reply tcak <1ltkrs+3wyh1ow7kzn1k sharklasers.com> writes:
On Saturday, 4 June 2016 at 15:51:22 UTC, Alex Parrill wrote:
 (It also doesn't help that many "thread-safe" functions in D 
 aren't marked as shared where they really ought to be, ex. all 
 the functions in core.sync.mutex)
And you have to be continuously casting the methods of Mutex, Thread, Condition, and others wherever you use them in a shared class which makes a mess everywhere. Only a person who uses those classes in many occasions can understand this situation. Thus, I have started this topic.
Jun 04 2016
parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Saturday, June 04, 2016 16:56:21 tcak via Digitalmars-d wrote:
 On Saturday, 4 June 2016 at 15:51:22 UTC, Alex Parrill wrote:
 (It also doesn't help that many "thread-safe" functions in D
 aren't marked as shared where they really ought to be, ex. all
 the functions in core.sync.mutex)
And you have to be continuously casting the methods of Mutex, Thread, Condition, and others wherever you use them in a shared class which makes a mess everywhere. Only a person who uses those classes in many occasions can understand this situation. Thus, I have started this topic.
Some of those methods really should be marked with shared, and it doesn't make sense that they aren't, but in the past, the primary maintainer for some of that stuff at the time didn't want to mark anything as shared, because he was afraid that shared was going to change. And no one since has come along and done it either. - Jonathan M Davis
Jun 05 2016