digitalmars.D - Deprecate synchronized classes please!
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
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
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
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
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.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.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.
Sep 06 2022
On Tuesday, 6 September 2022 at 11:16:19 UTC, Loara wrote: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.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 07 2022