Thread overview
Deprecate synchronized classes please!
Sep 06, 2022
Loara
Sep 06, 2022
IGotD-
Sep 06, 2022
Loara
Sep 06, 2022
IGotD-
Sep 06, 2022
Loara
Sep 07, 2022
frame
September 06, 2022

Even this very simple code

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?

September 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

September 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.

September 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?

September 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.

> >

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.

September 07, 2022

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.