shared - i need it to be useful
Stanislav Blinov
stanislav.blinov at gmail.com
Wed Oct 17 21:12:19 UTC 2018
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 at 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`.
More information about the Digitalmars-d
mailing list