Thread-safe attribution

Manu turkeyman at gmail.com
Sun Oct 7 02:59:47 UTC 2018


On Sat, Oct 6, 2018 at 7:01 PM Manu <turkeyman at gmail.com> wrote:
>
> On Sat, Oct 6, 2018 at 6:59 PM Manu <turkeyman at gmail.com> 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(Bob x, 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...?
>
> *** the function arguments should be `ref`!

Thinking on this more... perhaps it's the case that shared is
transitive, but the effect shared has, is to inhibit read/write to
data members.

In my example above, there is an atomic data member:
struct Bob
{
  int x;
  Atomic!int y; // not marked `shared` in this example
}

void fun(ref Bob a, ref shared Bob b)
{
  a.x = 10; // fine
  a.y = 10; // fine
  b.x = 10; // error! b is shared, and member 'x' is NOT shared, no access!
  b.y = 10; // this gets interesting...
}

So, `b` can't access `y` because it's not shared... but consider this
possibility:

struct Atomic(T)
{
  T data;
  void opAssign(T val) shared
  {
    // implement assignment using atomic operations
  }
}

`b.y.data` is not accessible, but the assignment operator has been
attributed shared, which means `b.y = 10` becomes a legal function
call.

At this point, shared is now useful.

So, continue the thought experiment that goes:
1. `shared` instances can NOT access non-shared members (opposite of
current behaviour)
2. non-shared can promote to `shared` (like const)

I think this restriction in access corrects the issues associated with
allowing non-shared -> shared promotion.


More information about the Digitalmars-d mailing list