Are these operations on shared data all safe? Note that if these
accesses would be protected by some lock, then the 'shared' qualifier
wouldn't really be needed - compiler barriers, that make sure it all
happens while this thread holds the lock, would be enough. (even the
order of operations doesn't usually matter in that case and enforcing
one would in fact add overhead)

No, they should not be all safe, I never suggested that. It's impossible
to engineer a one-size-fits-all for accessing shared variables, because
it doesn't know what mechanism you are going to use to protect it. As
you say, once this data is protected by a lock, memory barriers aren't
needed. But requiring a lock is too heavy handed for all cases. This is
a good point to make about the current memory-barrier attempts, they
just aren't comprehensive enough, nor do they guarantee pretty much
anything except simple loads and stores.

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.

It may be a good idea. Though I half-expect reads and writes to be atomic. Yet things like this are funky trap:
shread int x; //global
...
x = x + func();
//Booom! read-modify-write and not atomic, should have used x+= func()

So a-b set of rules could be more reasonable then it seems.

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.

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?

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.

Make sense?


While I've missed a good portion of this thread I think we should explore this direction. Shared has to be connected with locks/synchronized.

--
Dmitry Olshansky

Reply via email to