class Class {}
void main() {
synchronized(new shared Class) {}
}
% clear && dmd -g -debug -preview=nosharedaccess d.d && ./d
% d.d(3): Error: direct access to shared `new shared(Class)` is not allowed, see `core.atomic`
This means that it's not actually possible to synchronize on any given mutex.
Comment #1 by razvan.nitu1305 — 2023-02-15T12:14:56Z
Actually, the issue does not seem to have anything to do with synchronized (although, looking at the code it seems that Synchronized Statements have their own issues), but rather that you cannot create shared objects:
class Class {}
void main() {
auto b = new shared Class(); // Error: direct access to shared `new shared(Class)` is not allowed, see `core.atomic`
}
This is ridiculous, so you can't create shared objects with new?
Comment #2 by atila.neves — 2023-02-15T12:20:12Z
Oh, wow. I assumed the problem was with `synchronized` because the problem being with `new shared Class` was just too fundamental to even consider!
Comment #3 by razvan.nitu1305 — 2023-02-15T12:58:48Z
This is actually a 2.095 regression.
Comment #4 by dlang-bot — 2023-02-15T13:04:54Z
@RazvanN7 created dlang/dmd pull request #14884 "Fix Issue 23709 - Cannot use synchronized on shared class with -preview=nosharedaccess" fixing this issue:
- Fix Issue 23709 - Cannot use synchronized on shared class with -preview=nosharedaccess
https://github.com/dlang/dmd/pull/14884
Comment #5 by atila.neves — 2023-02-15T13:52:24Z
This is actually two bugs: creating a shared instance isn't possible with new, but the bug description is still accurate since this doesn't compile:
class Class {}
void main() {
shared Class c;
synchronized(c) {}
}
% d.d(4): Error: direct access to shared `c` is not allowed, see `core.atomic`
Comment #7 by razvan.nitu1305 — 2023-03-21T15:15:29Z
This is not a bug. The compiler rewrites the code to:
auto tmp = c;
_d_monitorenter(tmp);
try { body } finally { _d_monitorexit(tmp); }
So the reading of c needs to be atomic. Note that the original example (synchronized(new shared Class)) currently compiles because creating a shared instance does not need to be protected by a mutex.
So just write:
import core.atomic;
class Class {}
void main()
{
shared Class c;
synchronized(atomicLoad(c)) {}
}
And the code will compile.
Arguably, the compiler could automatically wrap `c` into an `atomicLoad`, however, that's based on the assumption that druntime is available and that the users does not employ a different synchronization mechanism.
I will tentatively close this as INVALID, but please reopen if I am missing something.
Comment #8 by maxhaton — 2023-03-21T16:35:39Z
If the monitor bit is synchronized does the load of c need to be atomic?
Comment #9 by razvan.nitu1305 — 2023-03-22T08:34:01Z
(In reply to mhh from comment #8)
> If the monitor bit is synchronized does the load of c need to be atomic?
Yes, because the pointer to the class could be modified before entering the monitor function.
Comment #10 by atila.neves — 2023-03-27T21:37:29Z
It doesn't make sense from a usability point of view for the compiler to not rewrite using atomicLoad, especially if it's always going to be needed except for when creating a mutex to pass in. That's clearly not going to be common.
Comment #11 by robert.schadek — 2024-12-13T19:27:18Z