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/