JDK-8028686 : ReentrantLock calls can hang from lack of stack space
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 7u6
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2013-03-20
  • Updated: 2024-05-13
  • Resolved: 2024-05-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdResolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_06 " 
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
ReentrantLock.unlock() call consumes more stack compared to ReentrantLock.lock().
This can cause paired lock/unlocks calls to fail in locked state given a tight
stack situation.  The failing thread exits with an out of stack error, but
the rest of the application will likely hang due to the stuck lock.

  Suggested fix:

1. Increase ReentrantLock.lock() stack usage to be equal or greater than ReentrantLock.unlock().
2. Enhance ReentrantLock.lock() to detect an owner's thread death and clean up held locks.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the supplied test case and it will hang.  Failure is stack size dependent.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Paired lock / unlock calls should work.
ACTUAL -
Lock call goes through but unlock call fails.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
stack trace in log file and application hung



REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.concurrent.locks.ReentrantLock;

public class TestReentrantLock extends Thread
{

    static final ReentrantLock myLock = new ReentrantLock();
    
    public static void func1()
    {
        byte a1;
        
        // Bracketed lock and unlock.
        myLock.lock();
        try
        {
        }
        finally
        {
            myLock.unlock();
        }
        
        myLock.lock();
        try
        {
            // Simulate stack exhaustion.
            func1();
        }
        finally
        {
            myLock.unlock();
        }
    }
    
    public void run()
    {
        // try
        {
            func1();
        }
        /*
        catch (Throwable ex)
        {
            // Is the lock currently unlocked, given that
            // all locks and unlocks are bracketed?
            if (myLock.isLocked())
            {
                System.out.println( " Failed. " );
            }
            else
            {
                System.out.println( " Passed. " );
            }
        }
        */
    }
    
    public static void main(String[] args) throws Exception
    {
        Thread th = new TestReentrantLock();
        th.start();
        th.join();
        System.out.println( " Am I going to be hung? " );
        myLock.lock();
        try
        {
            System.out.println( " got lock " );
        }
        finally
        {
            myLock.unlock();
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
no reliable workarounds known
Comments
Even after https://openjdk.org/jeps/270 there are some lingering effects that are still not resolved. JDK-8318888 tracks that.
07-05-2024

It is that unlikely that JDK-7011862 would ever be backported to 7; nor would any significant change to stack handling. Any separation of the Java stack and thread stack would kill performance IMHO. This issue seems unlikely to be addressed in any form in 7u.
29-09-2023

I continue to believe that implementing dynamically growable stacks, at least for pure Java code, is the right thing to do - but it's clearly a large investment. Implementing delimited continuations for Loom looks like a very similar problem, so perhaps the Loom project will help resolve the StackOverflowError problem indirectly? Should this bug be closed as a duplicate of JDK-7011862, which introduced the @ReservedStackAccess workaround?
23-06-2019

This is a known issue with no reasonable fix at this time. The java.util.concurrent.AbstractQueuedSynchronizer (that underpins ReentrantLock and other synchronizers) can become corrupted by asynchronous exceptions (as can all Java code), in this case StackOverflowError is an asynchronous exception. I will try to find the existing issue for this.
20-11-2013

Deferring to dev team for decision on this webbug.
19-11-2013