~/projects/jruby ➔ ruby -rthread -e "m = Mutex.new; Thread.new {
m.lock; p m.locked? }.join; p m.locked?"
true
false
Previously our implementation simply maintained a volatile reference
to the thread owning the lock, considering it unlocked if the thread
was no longer alive. However that implementation did not use any
JVM-level monitor state, so it was not possible for Hotspot to detect
deadlocks between two threads trying to mutually acquire the same
locks in different order. A modified version that uses ReentrantLock
does show up in Hotspot's deadlock detection, but does not appear to
release locks on thread death:
~/projects/jruby ➔ jruby -rthread -e "m = Mutex.new; Thread.new {
m.lock; p m.locked? }.join; p m.locked?"
true
true
Am I forced to use my own locking mechanism to get on-death lock
releasing, or is there some mechanism in java.util.* I've overlooked?
- Charlie
~~ Robert.
That'd actually be a useful stunt for something I'm working on, too.
> --
> You received this message because you are subscribed to the Google Groups "JVM Languages" group.
> To post to this group, send email to jvm-la...@googlegroups.com.
> To unsubscribe from this group, send email to jvm-language...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en.
>
>
You can copy/paste/hack the Mutex example from our
AbstractQueuedSynchronizer javadoc:
http://download.oracle.com/javase/7/docs/api/java/util/concurrent/locks/AbstractQueuedSynchronizer.html
Also pasted below. This version does not perform ownership checks
etc so you can safely unlock on finalization (or whatever).
(Disagreements about whether we should expose something so
abusable led to it being inside javadoc vs an actual exported class.
One of these days we should reconsider this.) Yet it does
still generate monitoring-and-management information when
threads actually block, so most problems are diagnosable,
although there is no automatic cyclic-wait deadlock detection.
class Mutex implements Lock, java.io.Serializable {
// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Report whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}
// Acquire the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// Release the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// Provide a Condition
Condition newCondition() { return new ConditionObject(); }
// Deserialize properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
Thanks Doug, I'll give this a try. In the interim I added a list of held locks to the Ruby thread object, to be released on termination...but it feels a little icky.
- Charlie (mobile)