On Sunday, 7 October 2018 at 01:59:21 UTC, Manu wrote:
So I'm working on a SMT infrastructure, and expression of
thread-safety is a core design mechanic... but I'm really struggling
to express it in terms of the type system.
I figure I'll throw some design challenges out here and see if anyone
can offer some good ideas.

The thing I'm trying to model is an attribute along the lines of
`shared`, but actually useful ;)
I'll use the attribute `threadsafe` in place of `shared`, and see
where that goes.

Consider:
struct Bob
{
  int x;
  threadsafe Atomic!int y;

  void m1();
  void m2() threadsafe;;

  void overloaded();
  void overloaded() threadsafe;
}

void func( ref Bob x, ref threadsafe Bob y)
{
  x.x = 10; // fine
  x.y = 10; // fine
  x.m1(); // fine
  x.m2(); // fine
  x.overloaded(); // fine, use the un-threadsafe overload

  y.x = 10; // ERROR, a threadsafe reference can NOT modify an
un-threadsafe member
  y.y = 10; // fine
  x.m1(); // ERROR, method not threadsafe
  x.m2(); // fine
  x.overloaded(); // fine, use the threadsafe overload

  threadsafe Bob* p = &x; // can take threadsafe reference to
thread-local object
}

This is loosely what `shared` models, but there's a few differences:
1. thread-local can NOT promote to shared
2. shared `this` applies to members

For `shared` to be useful, it should be that a shared reference to something inhibits access to it's thread-local stuff. And in that world, then I believe that thread-local promotion to shared would work like const does.

I guess I'm wondering; should `shared` be transitive? Perhaps that's what's wrong with it...?

A delta comparison with shared

void func( ref Bob x, ref threadshared /* either shared or threadsafe*/ Bob y)
{
 // threadsafe / shared
  x.x = 10; // fine / fine
  x.y = 10; // fine / fine uses atomics
  x.m1(); // fine / fine
x.m2(); // fine / error cannot call shared method on unshared object
  x.overloaded(); // fine, use the un-threadsafe overload / fine

y.x = 10; // ERROR, a threadsafe reference can NOT modify an un-threadsafe member / error
  y.y = 10; // fine / fine (using atomics)
// Assuming these are supposed to be y not x
  y.m1(); // ERROR, method not threadsafe / error
  y.m2(); // fine / fine
  y.overloaded(); // fine, use the threadsafe overload / fine

threadsafe Bob* p = &x; // can take threadsafe reference to thread-local object / error
}

Differences:
Can call threadsafe method on thread local / unshared
Can take threadsafe reference to thread-local object.

One thing that occurred to me is that _objects_ are shared, whereas _functions/methods_ (and their parameters) are thread safe .

Theadsafe is kind of like a const (as to mutable/immutable) to threading, a promise to behave correctly in the presence of threading. thread safe references therefore must not escape.

Reply via email to