More synchronized ideas

Steven Schveighoffer schveiguy at yahoo.com
Wed Jun 6 05:12:42 PDT 2012


On Mon, 04 Jun 2012 18:26:50 -0400, Michel Fortin  
<michel.fortin at michelf.com> wrote:

> On 2012-06-04 18:22:22 +0000, "Steven Schveighoffer"  
> <schveiguy at yahoo.com> said:
>
>> On Mon, 04 Jun 2012 07:17:45 -0400, Michel Fortin   
>> <michel.fortin at michelf.com> wrote:
>>
>>> After trying to make sense of the thread "synchronized   
>>> (this[.classinfo]) in druntime and phobos", I had to write my opinion  
>>> on  all this somewhere that wouldn't be instantly lost in a bazilion  
>>> of  posts. It turned out into something quite elaborate.
>>>  <http://michelf.com/weblog/2012/mutex-synchonization-in-d/>
>>  I like this.  But it needs a lot of work.
>>  A few comments:
>>  1. This does not handle shared *at all*.  Presumably, there is no  
>> reason  to lock unshared data, so this has to be handled somewhere.  If  
>> you say  "synchronized implies shared", well, then how do you have a  
>> shared int  inside an unshared class? My instinct is that all the  
>> methods that need  to used synchronized need to be declared shared  
>> (meaning the whole class  data is shared).  But that sucks, because  
>> what if you have a thread-local  instance?
>
> To the type system, the synchronized variable is thread-local with one  
> restriction: you can only access it inside a synchronized block. So  
> without the synchronized block you can't read or write to it, and you  
> can't take its address. Inside the synchronized block, any expression  
> making use of that variable is tainted by the current scope and must be  
> pure (weakly). Except if the only result of an expression contains no  
> indirection (is entirely copied), or is immutable or shared (no  
> synchronization needed), then the expression does not get tainted and  
> its result can be sent anywhere.
>
> Note that this taint thing is only done locally inside the synchronized  
> block: called functions are simply treated as an expression with inputs  
> and outputs to check whether they are tainted or not.

This feels like the most significant part of this proposal, and I think is  
definitely the right track.

>> I have an idea to solve this.  Since the mutexes are implicit, we can   
>> declare space for them, but only allocate them when the class instance  
>> is  shared (allocated on construction).  Then when synchronized goes to  
>> lock  them, if they are null, do nothing.
>
> Or they could simply work like the monitors objects currently have.

Some have stated there is a performance hit for lazy initialization of  
monitors.  I think we should try and fix that.

>
> As I wrote, I think we need support for shared mutexes too (integrated  
> with const if you will).

I don't disagree with that approach, except the usage of shared as a  
keyword to mean something completely different than it does now.

> Ideally, there'd be a way to choose your own mutex implementations,  
> perhaps with "synchronized(Spinlock) int x".

I like this better, perhaps a synchronized(ReadWriteMutex) would suffice  
instead of synchronized(shared)

>> But what if some data is not marked synchronized?
>
> You might actually want to restrict synchronized variables to shared  
> classes and shared structs. In that case, variables not marked as  
> synchronized will be shared and accessible using atomic operations.

I kind of like the idea of having to implement a class/structure once, and  
then being able to use it penalty-free in a thread-local context.

Perhaps that's asking too much -- a class that is meant to be shared may  
have not a lot in common with one that was meant to be thread-local.  It  
sure feels like I should be able to (for instance) implement a container  
that can be shared or unshared, and change small pieces of it, or add a  
few synchronized calls and get it working.

>> 2. As far as determining a mutex to protect multiple items of data,  
>> what  about:
>>  synchronized(symbolname) int x, int y;
>>  or
>>  synchronized(symbolname)
>> {
>>     int x;
>>     int y;
>> }
>>  where you cannot do synchronized(x) or synchronized(y), and cannot  
>> read or  write x or y without doing synchronized(symbolname).
>
> Or we could be less original: use a struct. It's just a minor cosmetic  
> problem actually.
>
> 	struct XY { int x, y; }
> 	synchronized XY xy;

True.  But sometimes the cosmetic things make all the difference!  You  
could implement synchronized with structs also instead of having a keyword.

One thing I don't like about this is the xy noise when referring to x and  
y.

-Steve


More information about the Digitalmars-d mailing list