shared - i need it to be useful

Steven Schveighoffer schveiguy at gmail.com
Thu Oct 18 14:19:41 UTC 2018


On 10/18/18 10:11 AM, Simen Kjærås wrote:
> On Thursday, 18 October 2018 at 13:35:22 UTC, Steven Schveighoffer wrote:
>> 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
>>    }
>> }
> 
> But this isn't thread-safe, for the exact reasons described elsewhere in 
> this thread (and in fact, incorrectly leveled at Manu's proposal). 
> Someone could write this code:
> 
> void foo() {
>      ThreadSafe* a = new ThreadSafe();
>      shareAllOver(a);

Error: cannot call function shareAllOver(shared(ThreadSafe) *) with type 
ThreadSafe *

>      a.increment(); // unsafe, non-shared method call
> }
> 
> When a.increment() is being called, you have no idea if anyone else is 
> using the shared interface.

I do, because unless you have cast the type to shared, I'm certain there 
is only thread-local aliasing to it.

> This is one of the issues that MP (Manu's Proposal) tries to deal with. 
> Under MP, your code would *not* be considered thread-safe, because the 
> non-shared portion may interfere with the shared portion. You'd need to 
> write two types:
> 
> struct ThreadSafe {
>      private int x;
>      void increment() shared {
>          atomicIncrement(&x);
>      }
> }
> 
> struct NotThreadSafe {
>      private int x;
>      void increment() {
>          ++x;
>      }
> }
> 
> These two are different types with different semantics, and forcing them 
> both into the same struct is an abomination.

Why? What if I wanted to have an object that is local for a while, but 
then I want it to be shared (and I ensure carefully when I cast to 
shared that there are no other aliases to that)?

> In your case, the user of your type will need to ensure thread-safety. 

No, the contract the type provides is: if you DON'T cast unshared to 
shared or vice versa, the type is thread-safe.

If you DO cast unshared to shared, then the type is thread-safe as long 
as you no longer use the unshared reference.

This is EXACTLY how immutable works.

> You may not have any control over how he's doing things, while you *do* 
> control the code in your own type (and module, since that also affects 
> things). Under MP, the type is what needs to be thread-safe, and once it 
> is, the chance of a user mucking things up is much lower.

Under MP, the type is DEFENSIVELY thread-safe, locking or using atomics 
unnecessarily when it's thread-local.

-Steve


More information about the Digitalmars-d mailing list