Something needs to happen with shared, and soon.

Michel Fortin michel.fortin at michelf.ca
Wed Nov 14 20:33:20 PST 2012


On 2012-11-15 02:51:13 +0000, "Jonathan M Davis" <jmdavisProg at gmx.com> said:

> I have no idea what we want to do about this situation though. Regardless of
> what we do with memory barriers and the like, it has no impact on whether
> casts are required.

One thing I'm confused about right now is how people are using shared. 
If you're using shared with atomic operations, then you need barriers 
when accessing or mutating the variable. If you're using shared with 
mutexes, spin-locks, etc., you don't care about the barriers. But you 
can't use it with both at the same time. So which of these shared 
stands for?

In both of these cases, there's an implicit policy for accessing or 
mutating the variable. I think the language need some way to express 
that policy. I suggested some time ago a way to protect variables with 
mutexes so that the compiler can actually help you use those mutexes 
correctly[1]. The idea was to associate a mutex to the variable 
declaration. This could be extended to support an atomic access policy.

Let me restate and extend that idea to atomic operations. Declare a 
variable using the synchronized storage class and it automatically get 
a mutex:

	synchronized int i; // declaration

	i++; // error, variable shared

	synchronized (i)
		i++; // fine, variable is thread-local inside synchronized block

Synchronized here is some kind of storage class causing two things: a 
mutex is attached to the variable declaration, and the type of the 
variable is made shared. The variable being shared, you can't access it 
directly. But a synchronized statement will make the variable 
non-shared within its bounds.

Now, if you want a custom mutex class, write it like this:

	synchronized(SpinLock) int i;

	synchronized(i)
	{
		// implicit: i.mutexof.lock();
		// implicit: scope (exit) i.mutexof.unlock();
		i++;
	}

If you want to declare the mutex separately, you could do it by 
specifying a variable instead of a type in the variable declaration:

	Mutex m;
	synchronized(m) int i;
	
	synchronized(i)
	{
		// implicit: m.lock();
		// implicit: scope (exit) m.unlock();
		i++;
	}

Also, if you have a read-write mutex and only need read access, you 
could declare that you only need read access using const:

	synchronized(RWMutex) int i;

	synchronized(const i)
	{
		// implicit: i.mutexof.constLock();
		// implicit: scope (exit) i.mutexof.constUnlock();
		i++; // error, i is const
	}

And finally, if you want to use atomic operations, declare it this way:

	synchronized(Atomic) int i;

You can't really synchronize on something protected by Atomic:

	syncronized(i) // cannot make sycnronized block, no lock/unlock method 
in Atomic
	{}

But you can call operators on it while synchronized, it works for 
anything implemented by Atomic:

	synchronized(i)++; // implicit: Atomic.opUnary!"++"(i);

Because the policy object is associated with the variable declaration, 
when locking the mutex you need direct access to the original variable, 
or an alias to it. Same for performing atomic operations. You can't 
pass a reference to some function and have that function perform the 
locking. If that's a problem it can be avoided by having a way to pass 
the mutex to the function, or by passing an alias to a template.

Okay, this syntax probably still has some problems, feel free to point 
them out. I don't really care about the syntax though. The important 
thing is that you need a way to define the policy for accessing the 
shared data in a way the compiler can actually enforce it and that 
programmers can actually reuse it.

Because right now there is no policy. Having to cast things everywhere 
is equivalent to having to redefine the policy everywhere. Same for 
having to write encapsulation types that work with shared for 
everything you want to share: each type has to implement the policy. 
There's nothing worse than constantly rewriting the sharing policies. 
Concurrency error-prone because of all the subtleties; you don't want 
to encourage people to write policies of their own every time they 
invent a new type. You need to reuse existing ones, and the compiler 
can help with that.

[1]: http://michelf.ca/blog/2012/mutex-synchonization-in-d/


-- 
Michel Fortin
michel.fortin at michelf.ca
http://michelf.ca/



More information about the Digitalmars-d mailing list