JDK-8049843 : Lack of save / restore interrupt mechanism undermines the StampedLock
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 8u5
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2014-06-29
  • Updated: 2015-11-09
  • Resolved: 2015-09-21
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.
JDK 9
9 b88Resolved
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_05"
java<TM> SE Runtime Environment <build 1.8.0_05-b13>
Java HotSpot<TM> Client VM <build 25.5-b02, mixed mode, sharing>

A DESCRIPTION OF THE PROBLEM :
When we call the readLock method of StampedLock������If the thread is already in the interrupt status������so even before obtaining the lock, the thread will always spin, which filled the CPU:

public class BugOfInterruptedThread {
    public static void main(String[] args) throws InterruptedException{
        final StampedLock lock = new StampedLock();
        new Thread(){
            public void run(){
                long readLong = lock.writeLock();
                LockSupport.parkNanos(6100000000L);
                lock.unlockWrite(readLong);
            }
        }.start();
        Thread.sleep(100);
        for( int i = 0; i < 3; ++i)
            new Thread(new OccupiedCPUReadThread(lock)).start();
    }
    private static class OccupiedCPUReadThread implements Runnable{
        private StampedLock lock;
        public OccupiedCPUReadThread(StampedLock lock){
            this.lock = lock;
        }
        public void run(){
            Thread.currentThread().interrupt();
            long lockr = lock.readLock();
            System.out.println(Thread.currentThread().getName() + " get read lock");
            lock.unlockRead(lockr);
        }
    }
}

Execute the above code, three cores are filled in six seconds of time.
The reason is that when readLock method detects the interrupt is not cleared and saved (and later restored) interrupt.

REGRESSION.  Last worked in version 8u5

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_05"
java<TM> SE Runtime Environment <build 1.8.0_05-b13>
Java HotSpot<TM> Client VM <build 25.5-b02, mixed mode, sharing>

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Perform the following code, observe CPU usage������

public class BugOfInterruptedThread {
    public static void main(String[] args) throws InterruptedException{
        final StampedLock lock = new StampedLock();
        new Thread(){
            public void run(){
                long readLong = lock.writeLock();
                LockSupport.parkNanos(6100000000L);
                lock.unlockWrite(readLong);

            }
        }.start();
        Thread.sleep(100);
        for( int i = 0; i < 3; ++i)
            new Thread(new OccupiedCPUReadThread(lock)).start();
    }
    private static class OccupiedCPUReadThread implements Runnable{
        private StampedLock lock;
        public OccupiedCPUReadThread(StampedLock lock){
            this.lock = lock;
        }
        public void run(){
            Thread.currentThread().interrupt();
            long lockr = lock.readLock();
            System.out.println(Thread.currentThread().getName() + " get read lock");
            lock.unlockRead(lockr);
        }
    }
}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Before acquiring the lock, the thread hangs.
ACTUAL -
Before acquiring the lock, the thread always occupy CPU.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class BugOfInterruptedThread {
    public static void main(String[] args) throws InterruptedException{
        final StampedLock lock = new StampedLock();
        new Thread(){
            public void run(){
                long readLong = lock.writeLock();
                LockSupport.parkNanos(6100000000L);
                lock.unlockWrite(readLong);

            }
        }.start();
        Thread.sleep(100);
        for( int i = 0; i < 3; ++i)
            new Thread(new OccupiedCPUReadThread(lock)).start();
    }
    private static class OccupiedCPUReadThread implements Runnable{
        private StampedLock lock;
        public OccupiedCPUReadThread(StampedLock lock){
            this.lock = lock;
        }
        public void run(){
            Thread.currentThread().interrupt();
            long lockr = lock.readLock();
            System.out.println(Thread.currentThread().getName() + " get read lock");
            lock.unlockRead(lockr);
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Instead of using ReentrantReadWriteLock.


Comments
No, David is right that to guarantee preserving initial interrupt status even if reset and recleared requires save/restore along all paths. I updated accordingly
10-07-2014

Thanks. This was an order of evaluation error (in 3 places) if (interruptible && Thread.interrupted()) should be if (Thread.interrupted() && interruptible) See jsr166 CVS for diffs.
10-07-2014

deferring to Dev team for triage of this webbug.
10-07-2014

Added Doug Lea to the watcher list.
10-07-2014

My initial view is that this indeed a bug in StampedLock. The readLock is not interruptible so will keep trying to acquire the lock if interrupted; but the fact we are interrupted means the park() will return immediately so this turns into a busy spin-loop rather than a blocking wait. In principle, as per the bug synopsis, we would want to save the fact the thread was interrupted and then do the park, and finally re-assert the interrupt after the lock was acquired. Something similar to the parkAndCheckInterrupt() used in AbstractQueuedSynchronizer.acquireQueued
10-07-2014