On 16.10.2018 20:07, Manu wrote:
On Tue, Oct 16, 2018 at 6:25 AM Timon Gehr via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
On 16.10.2018 13:04, Dominikus Dittes Scherkl wrote:
On Tuesday, 16 October 2018 at 10:15:51 UTC, Timon Gehr wrote:
On 15.10.2018 20:46, Manu wrote:
Assuming the rules above: "can't read or write to members", and the
understanding that `shared` methods are expected to have threadsafe
implementations (because that's the whole point), what are the risks
from allowing T* -> shared(T)* conversion?
Unshared becomes useless, and in turn, shared becomes useless.
why is unshared useless?
Unshared means you can read an write to it.
If you give it to a function that expect something shared,
the function you had given it to can't read or write it, so it
can't do any harm.
It can do harm to others who hold an unshared alias to the same data and
are operating on it concurrently.
Nobody else holds an unshared alias.
How so? If you allow implicit conversions from unshared to shared, then
you immediately get this situation.
If you pass a value as const, you don't fear that it will become mutable.
...
No, but as I already explained last time, mutable -> const is not at all
like unshared -> shared.
const only takes away capabilities, shared adds new capabilities, such
as sending a reference to another thread. If you have two threads that
share data, you need cooperation from both to properly synchronize accesses.
Of course it can handle it threadsave, but as it is local,
that is only overhead - reading or changing the value can't do
any harm either. I like the idea.
But useless, because there is no way to ensure thread safety of reads
and writes if only one party to the shared state knows about the sharing.
Of course there is.
Please do enlighten me. You have two processors operating
(reading/writing) on the same address space on a modern computer
architecture with a weak memory model, and you are using an optimizing
compiler. How do you ensure sensible results without cooperation from
both of them? (Hint: you don't.)
What? This is a weird statement.
So, you're saying that nobody has successfully written any threadsafe
code, ever... we should stop trying, and we should admit that
threadsafe queues and atomics, and mutexes and stuff all don't exist?
Obviously I am not saying that.
without cooperation from both of them?
Perhaps this is the key to your statement?
Yes.
Yes. 'cooperation from both of them' in this case means, they are both
interacting with a threadsafe api, and they are blocked from accessing
members, or any non-threadsafe api.
...
Yes. Your proposal only enforces this for the shared alias.
Giving an unshared value to a function that
even can handle shared values may create some overhead, but is
indeed threadsave.
Yes, if you give it to one function only, that is the case. However, as
you may know, concurrency means that there may be multiple functions
operating on the data _at the same time_. If one of them operates on the
data as if it was not shared, you will run into trouble.
Who's doing this,
Anyone, it really does not matter. One major point of the type system is
to ensure that _all_ @safe code has defined behavior. You can convert
between shared and unshared, just not in @safe code.
and how?
...
They create a mutable instance of a class, they create a shared alias
using one of your proposed holes, then send the shared alias to another
thread, call some methods on it in both threads and get race conditions.
You are arguing as if there was either no concurrency or no mutable
aliasing.
If a class has no shared methods, there's no possibility for mutable aliasing.
If the class has shared methods, then the class was carefully designed
to be threadsafe.
Not necessarily. Counterexample:
@safe:
class C{
int x;
void foo(){
x+=1; // this can still race with atomicIncrement
}
void bar()shared{
atomicIncrement(x); // presumably you want to allow this
}
}
void main(){
auto c=new C();
shared s=c; // depending on your exact proposed rules, this step
may be more cumbersome
spawn!(()=>s.bar());
s.foo(); // race
}
Now, if a class has only shared members, that is another story. In this
case, all references should implicitly convert to shared. There's a DIP
I meant to write about this. (For all qualifiers, not just shared).