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

Reply via email to