Cleaning up locks on thread termination (or lazily after death?)

720 views
Skip to first unread message

Charles Oliver Nutter

unread,
Jun 12, 2011, 7:13:38 AM6/12/11
to JVM Languages
I'm trying to reimplement JRuby's version of Ruby's Mutex class, a
non-reentrant locking mechanism. One of the properties of a Mutex is
that when the thread holding its lock dies, the lock is released:

~/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 Fischer

unread,
Jun 12, 2011, 10:48:41 AM6/12/11
to jvm-la...@googlegroups.com
Yeah, you're really in trouble if a thread dies while holding a lock,
because it's illegal (throws an exception) to call
ReentrantLock#unlock if you're not the owning thread. So I'm not sure
how you could possibly "clean up" in that case, but I don't see a way
to do that automatically, either.

~~ 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.
>
>

Doug Lea

unread,
Jun 12, 2011, 11:10:07 AM6/12/11
to jvm-la...@googlegroups.com
On 06/12/11 07:13, Charles Oliver Nutter wrote:
> I'm trying to reimplement JRuby's version of Ruby's Mutex class, a
> non-reentrant locking mechanism. One of the properties of a Mutex is
> that when the thread holding its lock dies, the lock is released:
>

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));
}
}


Charles Oliver Nutter

unread,
Jun 12, 2011, 9:08:59 PM6/12/11
to jvm-la...@googlegroups.com
I was wondering if this might be possible, since I notice ReentratReadWriteLock's read lock can be unlocked by a different thread.

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)

Reply all
Reply to author
Forward
0 new messages