www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Thread-safe attribution

reply Manu <turkeyman gmail.com> writes:
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...?
Oct 06 2018
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Oct 06 2018
parent reply Manu <turkeyman gmail.com> writes:
On Sat, Oct 6, 2018 at 7:40 PM Nicholas Wilson via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 [...]

 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.
Right, that's kinda what I want to model... but the more I think of it, the more I think that experience can fit into `shared`, because it's almost there, and the current incarnation of shared is objectively useless. Consider shared as is today; struct Bob { int x; void f() shared { x = 10; // <- this compiles... WAT?! } } Right now, if you have a shared instance, you can read/write to the members... and that makes *absolutely no sense* no matter how you look at it. There is no reality where you have a shared thing, and accessing members un-controlled can be safe. Conventional wisdom is that when you have a shared thing, and you want to do stuff with it, you must acquire locks (or whatever) and case shared away. That should apply to f() above. struct Bob { int x; void f() shared { auto lock = getLock(); auto unshared = shared_cast(&this); unshared.x = 10; // <- this is now okay. } } If we made a change were `shared` lost the ability to access non-`shared` members, I don't think that would interfere with current or proposed uses of shared in any way whatsoever... and we would make shared useful in the process.
Oct 06 2018
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 7 October 2018 at 02:59:12 UTC, Manu wrote:
 On Sat, Oct 6, 2018 at 7:40 PM Nicholas Wilson via 
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 [...]

 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.
Right, that's kinda what I want to model... but the more I think of it, the more I think that experience can fit into `shared`, because it's almost there, and the current incarnation of shared is objectively useless. Consider shared as is today; struct Bob { int x; void f() shared { x = 10; // <- this compiles... WAT?! } } Right now, if you have a shared instance, you can read/write to the members... and that makes *absolutely no sense* no matter how you look at it. There is no reality where you have a shared thing, and accessing members un-controlled can be safe. Conventional wisdom is that when you have a shared thing, and you want to do stuff with it, you must acquire locks (or whatever) and case shared away. That should apply to f() above. struct Bob { int x; void f() shared { auto lock = getLock(); auto unshared = shared_cast(&this); unshared.x = 10; // <- this is now okay. } } If we made a change were `shared` lost the ability to access non-`shared` members, I don't think that would interfere with current or proposed uses of shared in any way whatsoever... and we would make shared useful in the process.
I think it should be more like this shared struct Bob { shared int x; void f() shared { Laquire: auto owned_bob = try_aquire(&this, pthread_self()); // type will be the same as Bob* but without shared stripped from variables // and without any functions if (owned_bob is null) { __mmPause(); goto Laquire; } *owned_bob.x = 10; } }
Oct 08 2018
prev sibling parent Kagamin <spam here.lot> writes:
struct Bob
{
   threadsafe Atomic!(string[string]) y;
}

void f(ref threadsafe Bob b)
{
   string[string] aa=b.y;
   aa["b"]="c";
}

Like this?
Oct 10 2018