-
Type:
Bug
-
Resolution: Fixed
-
Priority:
P3
-
Affects Version/s: 21, 22, 23, 24, 25
-
Component/s: core-libs
-
Environment:
Happens on both Mac OS X ARM-64 and Linux. Did not try Windows.
-
b27
-
generic
When multiple threads offer() and poll() items into a LinkedTransferQueue, it can happen that poll() returns null, even though the queue is not actually empty.
Sample code:
import java.util.*;
import java.util.concurrent.*;
public class LinkedTransferQueueTest {
public static void main(String... args) throws Throwable {
test(new LinkedTransferQueue<>());
test(new LinkedBlockingQueue<>());
test(new LinkedBlockingDeque<>());
test(new ArrayBlockingQueue<>(10));
var q = new LinkedTransferQueue<Integer>();
}
private static void test(BlockingQueue<Integer> q) throws ExecutionException, InterruptedException {
System.out.println(q.getClass());
try (var pool = Executors.newCachedThreadPool()) {
var futures = new ArrayList<Future<List<Integer>>>();
var phaser = new Phaser(4);
for (var i = 0; i < 4; i++) {
futures.add(pool.submit(() -> {
var failedPolls = new ArrayList<Integer>();
for (int k = 0; k < 10; k++) {
for (int j = 0; j < 1000; j++) {
if (!q.offer(j))
throw new AssertionError("offer() failed for j=" + j);
if (q.peek() == null)
throw new AssertionError("peek() failed for j=" + j);
if (q.peek() == null)
throw new AssertionError("peek() failed for j=" + j);
while (q.poll() == null)
failedPolls.add(j);
}
phaser.arriveAndAwaitAdvance();
q.clear();
phaser.arriveAndAwaitAdvance();
}
return failedPolls;
}));
}
var futureNumber = 0;
for (var future : futures) {
var failedPolls = future.get();
if (failedPolls.size() > 0) {
System.out.println(futureNumber +
": failedPolls = " + failedPolls);
}
futureNumber++;
}
}
}
}
Expected output:
class java.util.concurrent.LinkedTransferQueue
class java.util.concurrent.LinkedBlockingQueue
class java.util.concurrent.LinkedBlockingDeque
class java.util.concurrent.ArrayBlockingQueue
Actual output:
class java.util.concurrent.LinkedTransferQueue
0: failedPolls = [590, 971, 581, 629, 652, 632, 439, 651, 820, 918, 555]
1: failedPolls = [682, 516, 14, 426, 510, 607, 658, 679, 699, 811, 812, 813, 828, 862, 876]
2: failedPolls = [151, 497, 524, 547, 609, 617, 759, 795, 826, 843, 845, 866, 988]
3: failedPolls = [43, 7, 20, 929, 963, 976, 875, 900, 982, 200]
class java.util.concurrent.LinkedBlockingQueue
class java.util.concurrent.LinkedBlockingDeque
class java.util.concurrent.ArrayBlockingQueue
Sample code:
import java.util.*;
import java.util.concurrent.*;
public class LinkedTransferQueueTest {
public static void main(String... args) throws Throwable {
test(new LinkedTransferQueue<>());
test(new LinkedBlockingQueue<>());
test(new LinkedBlockingDeque<>());
test(new ArrayBlockingQueue<>(10));
var q = new LinkedTransferQueue<Integer>();
}
private static void test(BlockingQueue<Integer> q) throws ExecutionException, InterruptedException {
System.out.println(q.getClass());
try (var pool = Executors.newCachedThreadPool()) {
var futures = new ArrayList<Future<List<Integer>>>();
var phaser = new Phaser(4);
for (var i = 0; i < 4; i++) {
futures.add(pool.submit(() -> {
var failedPolls = new ArrayList<Integer>();
for (int k = 0; k < 10; k++) {
for (int j = 0; j < 1000; j++) {
if (!q.offer(j))
throw new AssertionError("offer() failed for j=" + j);
if (q.peek() == null)
throw new AssertionError("peek() failed for j=" + j);
if (q.peek() == null)
throw new AssertionError("peek() failed for j=" + j);
while (q.poll() == null)
failedPolls.add(j);
}
phaser.arriveAndAwaitAdvance();
q.clear();
phaser.arriveAndAwaitAdvance();
}
return failedPolls;
}));
}
var futureNumber = 0;
for (var future : futures) {
var failedPolls = future.get();
if (failedPolls.size() > 0) {
System.out.println(futureNumber +
": failedPolls = " + failedPolls);
}
futureNumber++;
}
}
}
}
Expected output:
class java.util.concurrent.LinkedTransferQueue
class java.util.concurrent.LinkedBlockingQueue
class java.util.concurrent.LinkedBlockingDeque
class java.util.concurrent.ArrayBlockingQueue
Actual output:
class java.util.concurrent.LinkedTransferQueue
0: failedPolls = [590, 971, 581, 629, 652, 632, 439, 651, 820, 918, 555]
1: failedPolls = [682, 516, 14, 426, 510, 607, 658, 679, 699, 811, 812, 813, 828, 862, 876]
2: failedPolls = [151, 497, 524, 547, 609, 617, 759, 795, 826, 843, 845, 866, 988]
3: failedPolls = [43, 7, 20, 929, 963, 976, 875, 900, 982, 200]
class java.util.concurrent.LinkedBlockingQueue
class java.util.concurrent.LinkedBlockingDeque
class java.util.concurrent.ArrayBlockingQueue
- links to
-
Commit(master)
openjdk/jdk/d350158e
-
Review(master)
openjdk/jdk25u/396
-
Review(master)
openjdk/jdk/28479