8343426: ConcurrentSkipListMap.spliterator() can no longer split the stream

Co-authored-by: Doug Lea <dl@openjdk.org>
Reviewed-by: vklang
This commit is contained in:
Dr Heinz M. Kabutz 2024-11-14 09:17:02 +00:00 committed by Viktor Klang
parent a8152bdb9a
commit 2b57f402c4
2 changed files with 40 additions and 9 deletions

View file

@ -3221,14 +3221,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
} }
// factory method for KeySpliterator // factory method for KeySpliterator
final KeySpliterator<K,V> keySpliterator() { final KeySpliterator<K,V> keySpliterator() {
Index<K,V> h; Node<K,V> n; long est; Index<K,V> h; Node<K,V> hn, n; long est;
VarHandle.acquireFence(); VarHandle.acquireFence();
if ((h = head) == null) { if ((h = head) == null || (hn = h.node) == null) {
n = null; n = null;
est = 0L; est = 0L;
} }
else { else {
n = h.node; n = hn.next;
est = getAdderCount(); est = getAdderCount();
} }
return new KeySpliterator<K,V>(comparator, h, n, null, est); return new KeySpliterator<K,V>(comparator, h, n, null, est);
@ -3307,14 +3307,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
// Almost the same as keySpliterator() // Almost the same as keySpliterator()
final ValueSpliterator<K,V> valueSpliterator() { final ValueSpliterator<K,V> valueSpliterator() {
Index<K,V> h; Node<K,V> n; long est; Index<K,V> h; Node<K,V> hn, n; long est;
VarHandle.acquireFence(); VarHandle.acquireFence();
if ((h = head) == null) { if ((h = head) == null || (hn = h.node) == null) {
n = null; n = null;
est = 0L; est = 0L;
} }
else { else {
n = h.node; n = hn.next;
est = getAdderCount(); est = getAdderCount();
} }
return new ValueSpliterator<K,V>(comparator, h, n, null, est); return new ValueSpliterator<K,V>(comparator, h, n, null, est);
@ -3411,14 +3411,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
// Almost the same as keySpliterator() // Almost the same as keySpliterator()
final EntrySpliterator<K,V> entrySpliterator() { final EntrySpliterator<K,V> entrySpliterator() {
Index<K,V> h; Node<K,V> n; long est; Index<K,V> h; Node<K,V> hn, n; long est;
VarHandle.acquireFence(); VarHandle.acquireFence();
if ((h = head) == null) { if ((h = head) == null || (hn = h.node) == null) {
n = null; n = null;
est = 0L; est = 0L;
} }
else { else {
n = h.node; n = hn.next;
est = getAdderCount(); est = getAdderCount();
} }
return new EntrySpliterator<K,V>(comparator, h, n, null, est); return new EntrySpliterator<K,V>(comparator, h, n, null, est);

View file

@ -1323,6 +1323,37 @@ public class ConcurrentSkipListMapTest extends JSR166TestCase {
} }
} }
/**
* Spliterators.trySplit() returns null with an empty map and non-null when
* it is not empty.
*/
public void testSpliterators() {
// To test JDK-8343426
ConcurrentSkipListMap<Integer, Integer> map = new ConcurrentSkipListMap<>();
for (int i = 1; i <= 1000; i++) map.put(i, i);
// ensure that the splits do happen
assertNotNull(map.keySet().spliterator().trySplit());
assertNotNull(map.entrySet().spliterator().trySplit());
assertNotNull(map.values().spliterator().trySplit());
// ensure that the splits return *all* the items in the set
assertEquals(500_500, map.keySet()
.parallelStream()
.mapToInt(Integer::valueOf)
.sum());
assertEquals(500_500, map.values()
.parallelStream()
.mapToInt(Integer::valueOf)
.sum());
assertEquals(500_500 * 2, map.entrySet()
.parallelStream()
.mapToInt(entry -> entry.getKey() + entry.getValue())
.sum());
map.clear();
assertNull(map.keySet().spliterator().trySplit());
assertNull(map.entrySet().spliterator().trySplit());
assertNull(map.values().spliterator().trySplit());
}
static void assertEq(Item i, int j) { static void assertEq(Item i, int j) {
if (i == null) if (i == null)
mustEqual(j, -1); mustEqual(j, -1);