shared - i need it to be useful
Steven Schveighoffer
schveiguy at gmail.com
Thu Oct 18 13:35:22 UTC 2018
On 10/17/18 10:26 PM, Manu wrote:
> On Wed, Oct 17, 2018 at 6:50 PM Steven Schveighoffer via Digitalmars-d
>>
>> The implicit cast means that you have to look at more than just your
>> method. You have to look at the entire module, and figure out all the
>> interactions, to see if the thread safe method actually is thread safe.
>> That's programming by convention, and fully trusting the programmer.
>
> I don't understand... how can the outer context affect the
> threadsafety of a properly encapsulated thing?
[snip]
> You need to take it for an intellectual spin. Show me how it's corrupt
> rather than just presenting discomfort with the idea in theory.
> You're addicted to some concepts that you've carried around for a long
> time. There is no value in requiring casts, they're just a funky
> smell, and force the user to perform potentially unsafe manual
> conversions, or interactions that they don't understand.
For example (your example):
struct NotThreadsafe
{
private int x;
void local()
{
++x; // <- invalidates the method below, you violate the other
function's `shared` promise
}
void notThreadsafe() shared
{
atomicIncrement(&x);
}
}
First, note the comment. I can't look ONLY at the implementation of
"notThreadSafe" (assuming the function name is less of a giveaway) in
order to guarantee that it's actually thread safe. I have to look at the
WHOLE MODULE. Anything could potentially do what local() does. I added
private to x to at least give the appearance of thread safety.
But on top of that, if I can't implicitly cast mutable to shared, then
this ACTUALLY IS thread safe, as long as all the casting in the module
is sound (easy to search and verify), and hopefully all the casting is
encapsulated in primitives like you have written. Because someone on the
outside would have to cast a mutable item into a shared item, and this
puts the responsibility on them to make sure it works.
I'm ALL FOR having shared be completely unusable as-is unless you cast
(thanks for confirming what I suspected in your last post). It's the
implicit casting which I think makes things way more difficult, and
completely undercuts the utility of the compiler's mechanical checking.
And on top of that, I WANT that implementation. If I know something is
not shared, why would I ever want to use atomics on it? I don't like
needlessly throwing away performance. This is how I would write it:
struct ThreadSafe
{
private int x;
void increment()
{
++x; // I know this is not shared, so no reason to use atomics
}
void increment() shared
{
atomicIncrement(&x); // use atomics, to avoid races
}
}
The beauty of shared not being implicitly castable, is it allows you to
focus on the implementation at hand, with the knowledge that nothing
else can meddle with it. The goal of mechanical checking should be to
narrow the focus of what needs to be proven correct.
-Steve
More information about the Digitalmars-d
mailing list