On 30-05-2012 18:03, Regan Heath wrote:
On Wed, 30 May 2012 16:46:54 +0100, Andrei Alexandrescu
<seewebsiteforem...@erdani.org> wrote:

On 5/30/12 2:34 AM, deadalnix wrote:
Le 29/05/2012 23:33, Andrei Alexandrescu a écrit :
On 5/29/12 1:37 AM, deadalnix wrote:
I would say that breaking things here, with the right deprecation
process, is the way to go.

So what should we use for mutex-based synchronization if we deprecate
synchronized classes?

Andrei

I think something similar to range design here is the way to go.

It is easy to define something like

template isLockable(T) {
enum isLockable = isShared!T && is(typeof(T.init.lock())) &&
is(typeof(T.init.release()));
}

And allow locking only if(isLockable!Type) .

Now we can create SyncObject or any structure we want. The point is that
we lock explicit stuff.

But in this design anyone can lock such an object, which was something
you advocated against.

I think there is some confusion here as to what the "problem" is and is
not.

The problem is /not/ that you can lock any object.
The problem is /not/ that we have synchronized(object) {}
The problem is /not/ that we have synchronized classes/methods.

The problem /is/ that synchronized classes/methods use a mutex which is
exposed publicly, and it the same mutex as used by synchronized(object)
{}. This exposure/re-use makes deadlocks more likely to happen, and
harder to spot.

Thanks. I think the discussion was getting a bit derailed, myself being guilty of going off on tangential issues. Yes, the issue is exactly what you point out here: Exposing a locking resource publicly. The issue with a monitor on every object can probably be solved completely separately.


Removal of this "problem" will not stop deadlocks from happening as
they're a problem multi-threaded/lock based code will always have (*).
It will however make them far less likely to happen accidentally. It
will make programmers think about what/where/when and how to lock things
rather than blithely using synchronized everywhere. It will mean using
locks is not as easy as currently, though I think we can make it fairly
nice with good library code and/or some co-operation between the runtime
and synchronized() statements i.e. requiring the class implement a
common Lockable interface for example.

One gripe I have with using interfaces is speed. David Simcha showed that virtual mutex calls actually *do* impact speed significantly, particularly in the GC in his case.

As I pointed out earlier in this thread, core.sync.mutex.Mutex allows both composition and inheritance, and works with the synchronized statement. I still think it satisfies what everyone wants, given those two facts, and I have yet to see any counter-arguments to that (but would love to hear some to get a better understanding of why some people are against mutexes).


The fact that every object can be locked, and this means they all use
more memory is a side issue - arguably as important for some.

R

(*) the best you can do to "solve" the deadlock problem is to impose
lock acquisition ordering on your code, as in all locks must be acquired
in a defined order, and if not an assertion/error/exception is thrown.
This requires some sort of static/singelton/overlord class which is
aware of all mutexes and is involved in all acquisitions/releases.


--
Alex Rønne Petersen
a...@lycus.org
http://lycus.org

Reply via email to