On Wednesday, 17 October 2018 at 19:25:33 UTC, Manu wrote:
On Wed, Oct 17, 2018 at 12:05 PM Stanislav Blinov via
Digitalmars-d <digitalmars-d@puremagic.com> wrote:
On Wednesday, 17 October 2018 at 18:46:18 UTC, Manu wrote:
> I've said this a bunch of times, there are 2 rules:
> 1. shared inhibits read and write access to members
> 2. `shared` methods must be threadsafe
>
>>From there, shared becomes interesting and useful.
Oh God...
void atomicInc(shared int* i) { /* ... */ }
Now what? There are no "methods" for ints, only UFCS. Those
functions can be as safe as you like, but if you allow
implicit promotion of int* to shared int*, you *allow implicit
races*.
This function is effectively an intrinsic. It's unsafe by
definition.
Only if implicit conversion is allowed. If it isn't, that's
likely @trusted, and this:
void atomicInc(ref shared int);
can even be @safe.
It's a tool for implementing threadsafe machinery.
No user can just start doing atomic operations on random ints
and say
"it's threadsafe", you must encapsulate the threadsafe
functionality
into some sort of object that aggregates all concerns and
presents an
intellectually sound api.
Threadsafety starts and ends with the programmer. By your logic
*all* functions operating on `shared` are unsafe then. As far as
compiler is concerned, there would be no difference between these
two:
struct S {}
void atomicInc(ref shared S);
and
struct S { void atomicInc() shared { /* ... */ } }
The signatures of those two functions are exactly the same. How
is that different from a function taking a shared int pointer or
reference?
Let me try one:
void free(void*) { ... }
Now what? I might have dangling pointers... it's a catastrophe!
One could argue that it should be void free(ref void* p) { /* ...
*/ p = null; }
As a matter of fact, in my own allocators memory blocks allocated
by them are passed by value and are non-copyable, they're not
just void[] as in std.experimental.allocator. One must 'move'
them to pass ownership, and that includes deallocation. But
that's another story altogether.
It's essentially the same argument.
This isn't a function that professes to do something that
people might
misunderstand and try to use in an unsafe way, it's a low-level
implementation device, which is used to build larger *useful*
constructs.
You're missing the point, again. You have an int. You pass a
pointer to it to some API that takes an int*. You continue to use
your int as just an int. The API changes, and now the function
you called previously takes a shared int*. Implicit conversion
works, everything compiles, you have a race. Now, that's of
course an extremely stupid scenario. The point is: the caller of
some API *must* assert that they indeed pass shared data. It's
insufficient for the API alone to "promise" taking shared data.
That's the difference with promotion to `const`.