www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Deprecate synchronized classes please!

reply Loara <loara noreply.com> writes:
Even this very simple code
```d
synchronized class A{
   private:
     int a;
   public:
     this() pure {
       a = 1;
     }

     int getA() pure{
       a++;
       return a;
     }
}

int main(){
   A c = new A();
   return 0;
}
```
fails to compile with `dmd v2.100.1`
```
main.d(10): Error: read-modify-write operations are not allowed 
for `shared` variables
main.d(10):        Use `core.atomic.atomicOp!"+="(this.a, 1)` 
instead
```

Now it seems that D is moving from `synchronized` approach (that 
brings a lot of deadlocks threats) to a `shared` approach 
(passing only references to integer variables and access them 
only with atomic operations). Maybe we should finally deprecate 
`synchronized` class and leave only `synchronized` functions 
inside ` system` code since they can't be safe due to deadlock 
risks?
Sep 06 2022
parent reply IGotD- <nise nise.com> writes:
On Tuesday, 6 September 2022 at 09:09:49 UTC, Loara wrote:
 Now it seems that D is moving from `synchronized` approach 
 (that brings a lot of deadlocks threats) to a `shared` approach 
 (passing only references to integer variables and access them 
 only with atomic operations). Maybe we should finally deprecate 
 `synchronized` class and leave only `synchronized` functions 
 inside ` system` code since they can't be safe due to deadlock 
 risks?
No, the fundamental principle of shared and synchronized is different. With synchronized classes the compiler automatically inserts mutex calls in order to ensure no data races. This is really good as you don't need to worry forgetting about not acquiring/releasing a mutex. Shared is a bit different that currently rely on atomic operations but this approach is flawed in my opinion. Shared should be "do anything you want, you are on your own". The reason is that you cannot always rely on atomic operations is because they are very difficult, no compiler can ensure it and it is often not even possible. Therefore mutex are often used and therefore synchronized has its place and should be the most common way to share data structures. The golden rule of shared memory is, take the lock. This means you are often wasting your time with atomic operations also suddenly when you have several atomic variables you open up for data races again. Atomic operation often only works when there is one variable, when there are several you often must take the lock. Several atomic variables are often mind twisting difficult in order to get it right. Something that I miss in D is the "user space spinlock", which in Windows are EnterCriticalSection/LeaveCriticalSection and on Linux the futex. These are not pure spinlocks but the fastest you have that are safe. Pure spinlocks are not recommended for user programs. We need a cross platform interface for these. For faster lock times use these. Take the lock
Sep 06 2022
parent reply Loara <loara noreply.com> writes:
On Tuesday, 6 September 2022 at 09:30:51 UTC, IGotD- wrote:
 The reason is that you cannot always rely on atomic operations 
 is because they are very difficult, no compiler can ensure it 
 and it is often not even possible. Therefore mutex are often 
 used and therefore synchronized has its place and should be the 
 most common way to share data structures.
You can't send a `synchronized` class to a different thread via `send` \ `receive` unless you make your class `shared`. Phobos's multithreading functions are focusing on `shared` data types at the expense of `synchronized` classes. There isn't any traits that tells you if a class is `synchronized` too. Yes you can still use `synchronized` in you code, but in that case Phobos will go against you when you'll have to share your synchronized classes and force you to cast your classes and make your code less safe. Also in a `synchronized` class each member becomes `shared`, but this will force you to use atomic operation even if you have exclusive access to data variables. All the stuff around `synchronized` seems poorly designed only as a copy of Java's synchronization and later abandoned.
Sep 06 2022
parent reply IGotD- <nise nise.com> writes:
On Tuesday, 6 September 2022 at 10:43:44 UTC, Loara wrote:
 You can't send a `synchronized` class to a different thread via 
 `send` \ `receive` unless you make your class `shared`. 
 Phobos's multithreading functions are focusing on `shared` data 
 types at the expense of `synchronized` classes. There isn't any 
 traits that tells you if a class is `synchronized` too.
I'm not sure you mean with "send". The point with with synchronized classes is that you can use them everywhere, across thread boundaries without ownership. If "send" is just a way to share the synchronized class upon thread entry as a parameter, then I'd say it's a bug and it should be rectified.
 Yes you can still use `synchronized` in you code, but in that 
 case Phobos will go against you when you'll have to share your 
 synchronized classes and force you to cast your classes and 
 make your code less safe.
Yes, it's a gap in the inconsistent D design. D shared memory model is broken in many ways.
 Also in a `synchronized` class each member becomes `shared`, 
 but this will force you to use atomic operation even if you 
 have exclusive access to data variables. All the stuff around 
 `synchronized` seems poorly designed only as a copy of Java's 
 synchronization and later abandoned.
If all members becomes atomic inside a synchronized class, then it's a design error. The whole point of a synchronized class is that the members should be normal variables and not atomics. What is going on here?
Sep 06 2022
parent reply Loara <loara noreply.com> writes:
On Tuesday, 6 September 2022 at 10:52:13 UTC, IGotD- wrote:
 I'm not sure you mean with "send".
https://dlang.org/phobos/std_concurrency.html#.send This is the standard method in D to send data to other threads. Documentation says that you can't send unshared indirections, even if they're synchronized classes.
 Also in a `synchronized` class each member becomes `shared`, 
 but this will force you to use atomic operation even if you 
 have exclusive access to data variables. All the stuff around 
 `synchronized` seems poorly designed only as a copy of Java's 
 synchronization and later abandoned.
If all members becomes atomic inside a synchronized class, then it's a design error. The whole point of a synchronized class is that the members should be normal variables and not atomics.
This is the reason behind error message I've posted. Currently synchronized classes exists only as a mirror of Java's synchronization mechanism, but without a well integration with D ecosystem. Maybe we should rethink entirely `synchronized` inside D.
Sep 06 2022
parent frame <frame86 live.com> writes:
On Tuesday, 6 September 2022 at 11:16:19 UTC, Loara wrote:

 If all members becomes atomic inside a synchronized class, 
 then it's a design error. The whole point of a synchronized 
 class is that the members should be normal variables and not 
 atomics.
This is the reason behind error message I've posted. Currently synchronized classes exists only as a mirror of Java's synchronization mechanism, but without a well integration with D ecosystem. Maybe we should rethink entirely `synchronized` inside D.
I would also like to know the reason for this. The shared inference is nothing the user expects or asked for. And It only affects methods not fields + you can instantiate the class wihtout constructor as non-shared but need shared if you want a custom constructor, making this behaviour even more inconsistent.
Sep 07 2022