Odi's astoundingly incomplete notes
New entries | CodeStackoverflow can break your locking scheme
Simple Java locking code, looking innocent, right?
Here is a little demo:
The output is something like this:
private ReentrantLock lock = new ReentrantLock(); private void doIt() { boolean locked = lock.tryLock(); try { ... } finally { if (locked) lock.unlock(); } }Well, only until you realize that a call to unlock() causes 4 nested method calls. So unlock() requires a little bit of free stack. But what happens when stack is tight? Well... unlock() will simply fail with a StackOverflowError of course and your lock is leaked.
Here is a little demo:
public class StackOfl implements Runnable { public static void main(String[] args) { new StackOfl().run(); } @Override public void run() { try { test(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("still locked? "+ lock.isLocked()); System.out.println(lock.toString()); } private void test() { overflowStack(); } private ReentrantLock lock = new ReentrantLock(); private void overflowStack() { boolean locked = lock.tryLock(); try { overflowStack(); // 1. StackOverflowError occurs here } finally { if (locked) lock.unlock(); // 2. StackOverflowError occurs within here } // avoid tail recursion optimization in compiler System.out.println("unreachable"); } }This code overflows the stack on purpose within the try/finally block. Java dutifully executes the finally block on the first StackOverflowError just to run into another StackOverflowError during unlock(), which is the one that propagates up.
The output is something like this:
java.lang.StackOverflowError at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:149) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at StackOfl.overflowStack(StackOfl.java:39) at StackOfl.overflowStack(StackOfl.java:37) at StackOfl.overflowStack(StackOfl.java:37) ... still locked? true java.util.concurrent.locks.ReentrantLock@7852e922[Locked by thread main]You can clearly see the lock is leaked.
Add comment