On 2012-11-16 18:56:28 +0000, Dmitry Olshansky <dmitry.o...@gmail.com> said:

11/16/2012 5:17 PM, Michel Fortin пишет:
In case you want to protect two variables (or more) with the same mutex.
For instance:

     Mutex m;
     synchronized(m) int next_id;
     synchronized(m) Object[int] objects_by_id;


Wrap in a struct and it would be even much clearer and safer.
struct ObjectRepository {
        int next_id;
        Object[int] objects_by_id;
}
//or whatever that combination indicates anyway
synchronized ObjectRepository objeRepo;

I guess that'd be fine too.


If we made a tiny change in the language that would allow different syntax for passing delegates mine would shine. Such a change at the same time enables more nice way to abstract away control flow.

Imagine:

access(object_by_id){
        ...     
};

to be convertible to:

(x){with(x){
        ...
}})(access(object_by_id));

More generally speaking a lowering:

expression { ... }
-->
(x){with(x){ ... }}(expression);

AFIAK it doesn't conflict with anything.

Or wait a sec. Even simpler idiom and no extra features.
Drop the idea of 'access' taking a delegate. The other library idiom is to return a RAII proxy that locks/unlocks an object on construction/destroy.

with(lock(object_by_id))
{
        ... do what you like
}

Fine by me. And C++ can't do it ;)

Clever. But you forgot to access the variable somewhere. What's its name within the with block? Your code would be clearer this way:

        {
                auto locked_object_by_id = lock(object_by_id);
                // … do what you like
        }

And yes you can definitely do that in C++.

I maintain that the "synchronized (var)" syntax is still much clearer, and greppable too. That could be achieved with an appropriate lowering.


The key point is that Synchronized!T is otherwise an opaque type.
We could pack a few other simple primitives like 'load', 'store' etc.
All of them will go through lock-unlock.

Our proposals are pretty much identical. Your works by wrapping a
variable in a struct template, mine is done with a policy object/struct
associated with a variable. They'll produce the same code and impose the
same restrictions.

I kind of wanted to point out this disturbing thought about your proposal. That is a lot of extra syntax and rules added buys us very small gain - prettier syntax.

Sometime having something built in the language is important: it gives first-class status to some constructs. For instance: arrays. We don't need language-level arrays in D, we could just use a struct template that does the same thing. By integrating a feature into the language we're sending the message that this is *the* way to do it, as no other way can stand on equal footing, preventing infinite reimplementation of the concept within various libraries.

You might be right however than mutex-protected variables do not deserve this first class status.


Built-in shared(T) atomicity (sequential consistency) is a subject of
debate in this thread. It is not clear to me what will be the
conclusion, but the way I see things atomicity is just one of the many
policies you may want to use for keeping consistency when sharing data
between threads.

I'm not trilled by the idea of making everything atomic by default.
That'll lure users to the bug-prone expert-only path while relegating
the more generally applicable protection systems (mutexes) as a
second-class citizen.

That's why I think people shouldn't have to use mutexes at all.
Explicitly - provide folks with blocking queues, Synchronized!T, concurrent containers (e.g. hash map) and what not. Even Java has some useful incarnations of these.

I wouldn't say they shouldn't use mutexes at all, but perhaps you're right that they don't deserve first-class treatment. I still maintain that "syncronized (var)" should work, for clarity and consistency reasons, but using a template such as Synchronized!T when declaring the variable might be the best solution.


--
Michel Fortin
michel.for...@michelf.ca
http://michelf.ca/

Reply via email to