On Mon, 11 Jun 2012 15:23:56 -0400, Artur Skawina <art.08...@gmail.com> wrote:

On 06/11/12 19:27, Steven Schveighoffer wrote:

Perhaps the correct way to implement shared semantics is to not allow access *whatsoever* (except taking the address of a shared piece of data), unless you:

a) lock the block that contains it
b) use some library feature that uses casting-away of shared to accomplish the correct thing. For example, atomicOp.

Exactly; this is what I'm after the whole time. And I think it can be done in most cases without casting away shared. For example by allowing the safe conversions from/to shared of results of expression involving shared data,
but only under certain circumstances. Eg in methods with a shared 'this'.

Good, I'm glad we are starting to come together.

None of this can prevent deadlocks, but it does create a way to prevent deadlocks.

If this was the case, stack data would be able to be marked shared, and you'd have to use option b (it would not be in a block). Perhaps for simple data types, when memory barriers truly are enough, and a shared(int) is on the stack (and not part of a container), straight loads and stores would be allowed.

Why? Consider the case of function that directly or indirectly launches a few threads and gives them the address of some local shared object. If the current thread also accesses this object, which has to be possible, then it must obey
the same rules.

I think this is possible for what I prescribed. You need a special construct for locking and using shared data on the stack (for instance Lockable!S).

Another possible option is to consider the stack frame as the "container", and if it contains any shared data, put in a hidden mutex.

In order to do this correctly, we need a way to hook synchronized properly from library code.

Now, would you agree that:

auto v1 = synchronized p.i;

might be a valid mechanism? In other words, assuming p is lockable, synchronized p.i locks p, then reads i, then unlocks p, and the result type is unshared?

I think I would prefer

   auto v1 = synchronized(p).i;

This kind of makes synchronized a type constructor, which it is not.

ie for the synchronized expression to lock the object, return an unshared
reference, and the object be unlocked once this ref goes away. RLII. ;)

Which would then also allow for

   {
      auto unshared_p = synchronized(p);
      auto v1 = unshared_p.i;
      auto v2 = unshared_p.p;
      // etc
   }

I think this can be done, but I would not want to use synchronized. One of the main benefits of synchronized is it's a block attribute, not a type attribute. So you can't actually abuse it.

The locked type I specify below might fit the bill. But it would have to be hard-tied to the block. In other words, we would have to make *very* certain it would not escape the block. Kind of like inout.

Also, inside synchronized(p), p becomes tail-shared, meaning all data contained in p is unshared, all data referred to by p remains shared.

In this case, we'd need a new type constructor (e.g. locked) to formalize the type.

I should have read to the end i guess. :)

You mean something like I described above, only done by mutating
the type of 'p'? That might work too.

Right, any accesses to p *inside* the block "magically" become locked(S) instead of shared(S). We have to make certain locked(S) instances cannot escape, and we already do something like this with inout -- just don't allow members or static variables to be typed as locked(T).

I like replacing the symbol because then it doesn't allow you access to the outer symbol (although you can get around this, it should be made difficult). As long as the locks are reentrant, it shouldn't pose a large problem, but obviously you should try and avoid locking the same data over and over again.

One interesting thing: synchronized methods now would mark this as locked(typeof(this)) instead of typeof(this). So you can *avoid* the locking and unlocking code while calling member functions, while preserving it for the first call.

This is important -- you don't want to escape a reference to the unlocked type somewhere.

-Steve

Reply via email to