-
Bug
-
Resolution: Unresolved
-
P4
-
24
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
All versions
A DESCRIPTION OF THE PROBLEM :
The LinkedBlockingDeque does not behave consistently with other concurrency components. If we call putFirst(), putLast(), takeFirst(), or takeLast() with a thread that is interrupted, it does not immediately throw an InterruptedException, the way that ArrayBlockingQueue and LInkedBlockingQueue does, because instead of lockInterruptibly(), we call lock(). It will only throw an InterruptedException if the queue is full (on put) or empty (on take). Since interruptions are frequently used as a shutdown mechanism, this might prevent code from ever shutting down.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Call putFirst() on a LinkedBlockingDeque with an interrupted thread.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected an InterruptedException
ACTUAL -
The methods succeeed.
---------- BEGIN SOURCE ----------
import java.util.concurrent.*;
public class InterruptionsIgnoredOnPutLBD {
public static void main(String... args) throws InterruptedException {
// Ensure that putFirst(), putLast(), takeFirst(), and takeLast()
// immediately throw an InterruptedException if the thread is
// interrupted, to be consistent with other blocking queues such as
// ArrayBlockingQueue and LinkedBlockingQueue
try (var pool = Executors.newSingleThreadExecutor()) {
Future<Void> success = pool.submit(() -> {
var queue = new LinkedBlockingDeque<>();
Thread.currentThread().interrupt();
try {
queue.putFirst(42);
fail("Expected InterruptedException in putFirst()");
} catch (InterruptedException expected) {
// good that's what we want
}
Thread.currentThread().interrupt();
try {
queue.putLast(42);
fail("Expected InterruptedException in putLast()");
} catch (InterruptedException expected) {
// good that's what we want
}
queue.add(42);
Thread.currentThread().interrupt();
try {
queue.takeFirst();
fail("Expected InterruptedException in takeFirst()");
} catch (InterruptedException expected) {
// good that's what we want
}
queue.add(42);
Thread.currentThread().interrupt();
try {
queue.takeLast();
fail("Expected InterruptedException in takeLast()");
} catch (InterruptedException expected) {
// good that's what we want
}
return null;
});
try {
success.get();
} catch (ExecutionException e) {
try {
throw e.getCause();
} catch (Error | RuntimeException unchecked) {
throw unchecked;
} catch (Throwable cause) {
throw new AssertionError(cause);
}
}
}
}
private static void fail(String message) {
throw new AssertionError(message);
}
}
All versions
A DESCRIPTION OF THE PROBLEM :
The LinkedBlockingDeque does not behave consistently with other concurrency components. If we call putFirst(), putLast(), takeFirst(), or takeLast() with a thread that is interrupted, it does not immediately throw an InterruptedException, the way that ArrayBlockingQueue and LInkedBlockingQueue does, because instead of lockInterruptibly(), we call lock(). It will only throw an InterruptedException if the queue is full (on put) or empty (on take). Since interruptions are frequently used as a shutdown mechanism, this might prevent code from ever shutting down.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Call putFirst() on a LinkedBlockingDeque with an interrupted thread.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected an InterruptedException
ACTUAL -
The methods succeeed.
---------- BEGIN SOURCE ----------
import java.util.concurrent.*;
public class InterruptionsIgnoredOnPutLBD {
public static void main(String... args) throws InterruptedException {
// Ensure that putFirst(), putLast(), takeFirst(), and takeLast()
// immediately throw an InterruptedException if the thread is
// interrupted, to be consistent with other blocking queues such as
// ArrayBlockingQueue and LinkedBlockingQueue
try (var pool = Executors.newSingleThreadExecutor()) {
Future<Void> success = pool.submit(() -> {
var queue = new LinkedBlockingDeque<>();
Thread.currentThread().interrupt();
try {
queue.putFirst(42);
fail("Expected InterruptedException in putFirst()");
} catch (InterruptedException expected) {
// good that's what we want
}
Thread.currentThread().interrupt();
try {
queue.putLast(42);
fail("Expected InterruptedException in putLast()");
} catch (InterruptedException expected) {
// good that's what we want
}
queue.add(42);
Thread.currentThread().interrupt();
try {
queue.takeFirst();
fail("Expected InterruptedException in takeFirst()");
} catch (InterruptedException expected) {
// good that's what we want
}
queue.add(42);
Thread.currentThread().interrupt();
try {
queue.takeLast();
fail("Expected InterruptedException in takeLast()");
} catch (InterruptedException expected) {
// good that's what we want
}
return null;
});
try {
success.get();
} catch (ExecutionException e) {
try {
throw e.getCause();
} catch (Error | RuntimeException unchecked) {
throw unchecked;
} catch (Throwable cause) {
throw new AssertionError(cause);
}
}
}
}
private static void fail(String message) {
throw new AssertionError(message);
}
}
- links to
-
Review(master) openjdk/jdk/23464