Merge TP1A.211018.001
Change-Id: Iaab673e0cf2e34f5c7098eb87f2530b052b772e2
diff --git a/api/current.txt b/api/current.txt
index 604b708..416cc76 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -1307,6 +1307,8 @@
method public int available();
method public int read();
method public int read(byte[], int, int);
+ method public byte[] readAllBytes();
+ method public int readNBytes(byte[], int, int);
method public void reset();
method public long skip(long);
field protected byte[] buf;
@@ -1322,9 +1324,11 @@
method public int size();
method @NonNull public byte[] toByteArray();
method @NonNull public String toString(@NonNull String) throws java.io.UnsupportedEncodingException;
+ method @NonNull public String toString(@NonNull java.nio.charset.Charset);
method @Deprecated @NonNull public String toString(int);
method public void write(int);
method public void write(@NonNull byte[], int, int);
+ method public void writeBytes(byte[]);
method public void writeTo(@NonNull java.io.OutputStream) throws java.io.IOException;
field @NonNull protected byte[] buf;
field protected int count;
@@ -1635,11 +1639,16 @@
method public void close() throws java.io.IOException;
method public void mark(int);
method public boolean markSupported();
+ method public static java.io.InputStream nullInputStream();
method public abstract int read() throws java.io.IOException;
method public int read(byte[]) throws java.io.IOException;
method public int read(byte[], int, int) throws java.io.IOException;
+ method public byte[] readAllBytes() throws java.io.IOException;
+ method public byte[] readNBytes(int) throws java.io.IOException;
+ method public int readNBytes(byte[], int, int) throws java.io.IOException;
method public void reset() throws java.io.IOException;
method public long skip(long) throws java.io.IOException;
+ method public long transferTo(java.io.OutputStream) throws java.io.IOException;
}
public class InputStreamReader extends java.io.Reader {
@@ -1876,6 +1885,7 @@
ctor public OutputStream();
method public void close() throws java.io.IOException;
method public void flush() throws java.io.IOException;
+ method public static java.io.OutputStream nullOutputStream();
method public abstract void write(int) throws java.io.IOException;
method public void write(byte[]) throws java.io.IOException;
method public void write(byte[], int, int) throws java.io.IOException;
@@ -14223,7 +14233,6 @@
ctor public ArrayBlockingQueue(int, boolean, java.util.Collection<? extends E>);
method public int drainTo(java.util.Collection<? super E>);
method public int drainTo(java.util.Collection<? super E>, int);
- method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
method public boolean offer(E);
method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
@@ -14464,7 +14473,7 @@
method public final boolean isEmpty();
method @NonNull public java.util.Iterator<K> iterator();
method public boolean remove(@NonNull Object);
- method public boolean removeAll(@NonNull java.util.Collection<?>);
+ method public final boolean removeAll(@NonNull java.util.Collection<?>);
method public final boolean retainAll(@NonNull java.util.Collection<?>);
method public final int size();
method @NonNull public java.util.Spliterator<K> spliterator();
@@ -14480,7 +14489,6 @@
method public void addLast(E);
method public java.util.Iterator<E> descendingIterator();
method public E element();
- method public void forEach(java.util.function.Consumer<? super E>);
method public E getFirst();
method public E getLast();
method public java.util.Iterator<E> iterator();
@@ -14506,7 +14514,6 @@
public class ConcurrentLinkedQueue<E> extends java.util.AbstractQueue<E> implements java.util.Queue<E> java.io.Serializable {
ctor public ConcurrentLinkedQueue();
ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>);
- method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
method public boolean offer(E);
method public E peek();
@@ -14796,7 +14803,6 @@
ctor public ForkJoinPool();
ctor public ForkJoinPool(int);
ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
- ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean, int, int, int, java.util.function.Predicate<? super java.util.concurrent.ForkJoinPool>, long, java.util.concurrent.TimeUnit);
method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
method public static java.util.concurrent.ForkJoinPool commonPool();
@@ -14923,7 +14929,6 @@
method public java.util.Iterator<E> descendingIterator();
method public int drainTo(java.util.Collection<? super E>);
method public int drainTo(java.util.Collection<? super E>, int);
- method public void forEach(java.util.function.Consumer<? super E>);
method public E getFirst();
method public E getLast();
method public java.util.Iterator<E> iterator();
@@ -14964,7 +14969,6 @@
ctor public LinkedBlockingQueue(java.util.Collection<? extends E>);
method public int drainTo(java.util.Collection<? super E>);
method public int drainTo(java.util.Collection<? super E>, int);
- method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
method public boolean offer(E);
@@ -14982,7 +14986,6 @@
ctor public LinkedTransferQueue(java.util.Collection<? extends E>);
method public int drainTo(java.util.Collection<? super E>);
method public int drainTo(java.util.Collection<? super E>, int);
- method public void forEach(java.util.function.Consumer<? super E>);
method public int getWaitingConsumerCount();
method public boolean hasWaitingConsumer();
method public java.util.Iterator<E> iterator();
@@ -15032,7 +15035,6 @@
method public java.util.Comparator<? super E> comparator();
method public int drainTo(java.util.Collection<? super E>);
method public int drainTo(java.util.Collection<? super E>, int);
- method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
method public boolean offer(E);
method public boolean offer(E, long, java.util.concurrent.TimeUnit);
@@ -15130,29 +15132,6 @@
method public boolean tryAcquire(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
}
- public class SubmissionPublisher<T> implements java.lang.AutoCloseable java.util.concurrent.Flow.Publisher<T> {
- ctor public SubmissionPublisher(java.util.concurrent.Executor, int, java.util.function.BiConsumer<? super java.util.concurrent.Flow.Subscriber<? super T>,? super java.lang.Throwable>);
- ctor public SubmissionPublisher(java.util.concurrent.Executor, int);
- ctor public SubmissionPublisher();
- method public void close();
- method public void closeExceptionally(Throwable);
- method public java.util.concurrent.CompletableFuture<java.lang.Void> consume(java.util.function.Consumer<? super T>);
- method public int estimateMaximumLag();
- method public long estimateMinimumDemand();
- method public Throwable getClosedException();
- method public java.util.concurrent.Executor getExecutor();
- method public int getMaxBufferCapacity();
- method public int getNumberOfSubscribers();
- method public java.util.List<java.util.concurrent.Flow.Subscriber<? super T>> getSubscribers();
- method public boolean hasSubscribers();
- method public boolean isClosed();
- method public boolean isSubscribed(java.util.concurrent.Flow.Subscriber<? super T>);
- method public int offer(T, java.util.function.BiPredicate<java.util.concurrent.Flow.Subscriber<? super T>,? super T>);
- method public int offer(T, long, java.util.concurrent.TimeUnit, java.util.function.BiPredicate<java.util.concurrent.Flow.Subscriber<? super T>,? super T>);
- method public int submit(T);
- method public void subscribe(java.util.concurrent.Flow.Subscriber<? super T>);
- }
-
public class SynchronousQueue<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> java.io.Serializable {
ctor public SynchronousQueue();
ctor public SynchronousQueue(boolean);
@@ -15194,7 +15173,7 @@
method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
method protected void beforeExecute(Thread, Runnable);
method public void execute(Runnable);
- method @Deprecated protected void finalize();
+ method protected void finalize();
method public int getActiveCount();
method public long getCompletedTaskCount();
method public int getCorePoolSize();
@@ -15245,12 +15224,9 @@
public enum TimeUnit {
method public long convert(long, java.util.concurrent.TimeUnit);
- method public long convert(java.time.Duration);
- method public static java.util.concurrent.TimeUnit of(java.time.temporal.ChronoUnit);
method public void sleep(long) throws java.lang.InterruptedException;
method public void timedJoin(Thread, long) throws java.lang.InterruptedException;
method public void timedWait(Object, long) throws java.lang.InterruptedException;
- method public java.time.temporal.ChronoUnit toChronoUnit();
method public long toDays(long);
method public long toHours(long);
method public long toMicros(long);
diff --git a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java
index 53e0398..28517aa 100644
--- a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java
@@ -147,7 +147,7 @@
assertFalse(f.isCancelled());
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
@@ -202,7 +202,7 @@
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
assertTrue(f.isCancelled());
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
/**
@@ -349,12 +349,12 @@
f = new CompletableFuture<String>();
assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
f = new CompletableFuture<String>();
assertTrue(f.cancel(mayInterruptIfRunning));
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
index 194dd58..e763ae4 100644
--- a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
@@ -834,7 +834,9 @@
1, 1, MILLISECONDS));
periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
1, 1, MILLISECONDS));
- delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ // Android-changed: Use a longer delay to ensure task does not expire
+ // delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.getQueue().containsAll(periodics));
assertTrue(p.getQueue().containsAll(delayeds));
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
index 81f7370..8f65c84 100644
--- a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
@@ -783,7 +783,9 @@
1, 1, MILLISECONDS));
periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
1, 1, MILLISECONDS));
- delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ // Android-changed: Use a longer delay to ensure task does not expire
+ // delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.getQueue().containsAll(periodics));
assertTrue(p.getQueue().containsAll(delayeds));
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ArrayBlockingQueueTest.java b/luni/src/test/java/libcore/java/util/concurrent/ArrayBlockingQueueTest.java
deleted file mode 100644
index 12ec722..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/ArrayBlockingQueueTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ArrayBlockingQueueTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(capacity);
- for (int i = 0; i < capacity; ++i) {
- queue.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- queue.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(queue.size() * (queue.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedDequeTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedDequeTest.java
deleted file mode 100644
index 6a6f7ea..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedDequeTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ConcurrentLinkedDequeTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- ConcurrentLinkedDeque<Integer> deque = new ConcurrentLinkedDeque<>();
- for (int i = 0; i < capacity; ++i) {
- deque.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- deque.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(deque.size() * (deque.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedQueueTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedQueueTest.java
deleted file mode 100644
index 6a1bffa..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentLinkedQueueTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ConcurrentLinkedQueueTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
- for (int i = 0; i < capacity; ++i) {
- queue.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- queue.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(queue.size() * (queue.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java
index 48d4170..578970e 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java
@@ -70,94 +70,4 @@
fail("Unexpected exception: " + t.getMessage());
}
}
-
- @Test
- public void testConstructor_withKeepAliveTime() {
-
- try {
- ForkJoinPool pool = new ForkJoinPool(0,
- ForkJoinPool.defaultForkJoinWorkerThreadFactory,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 10,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 60,
- /*unit*/ TimeUnit.SECONDS);
- fail("Expected IllegalArgumentException when parallelism is 0");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- ForkJoinPool pool = new ForkJoinPool(-1,
- ForkJoinPool.defaultForkJoinWorkerThreadFactory,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 10,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 60,
- /*unit*/ TimeUnit.SECONDS);
- fail("Expected IllegalArgumentException when parallelism is less than 0");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- ForkJoinPool pool = new ForkJoinPool(8,
- ForkJoinPool.defaultForkJoinWorkerThreadFactory,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 5,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 60,
- /*unit*/ TimeUnit.SECONDS);
- fail("Expected IllegalArgumentException when maximumPoolSize is less than parallelism");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- ForkJoinPool pool = new ForkJoinPool(8,
- ForkJoinPool.defaultForkJoinWorkerThreadFactory,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 10,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 0,
- /*unit*/ TimeUnit.SECONDS);
- fail("Expected IllegalArgumentException when keepAlivetime is 0");
- } catch (IllegalArgumentException e) {
- }
-
- try {
- ForkJoinPool pool = new ForkJoinPool(8,
- null,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 10,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 60,
- /*unit*/ TimeUnit.SECONDS);
- fail("Expected NullPointerException when factory is null");
- } catch (NullPointerException e) {
- }
-
- ForkJoinPool pool = new ForkJoinPool(8,
- ForkJoinPool.defaultForkJoinWorkerThreadFactory,
- /*handler*/ null,
- /*asyncMode*/ false,
- /*corePoolSize*/ 10,
- /*maximumPoolSize*/ 10,
- /*minimumRunnable*/ 1,
- /*saturate*/ null,
- /*keepAliveTime*/ 60,
- /*unit*/ TimeUnit.SECONDS);
- }
}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingDequeTest.java b/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingDequeTest.java
deleted file mode 100644
index 66dd942..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingDequeTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class LinkedBlockingDequeTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
- for (int i = 0; i < capacity; ++i) {
- deque.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- deque.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(deque.size() * (deque.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingQueueTest.java b/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingQueueTest.java
deleted file mode 100644
index 4363988..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/LinkedBlockingQueueTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class LinkedBlockingQueueTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
- for (int i = 0; i < capacity; ++i) {
- queue.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- queue.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(queue.size() * (queue.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/LinkedTransferQueueTest.java b/luni/src/test/java/libcore/java/util/concurrent/LinkedTransferQueueTest.java
deleted file mode 100644
index 5b09284..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/LinkedTransferQueueTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.LinkedTransferQueue;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class LinkedTransferQueueTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- LinkedTransferQueue<Integer> queue = new LinkedTransferQueue<>();
- for (int i = 0; i < capacity; ++i) {
- queue.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- queue.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(queue.size() * (queue.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/PriorityBlockingQueueTest.java b/luni/src/test/java/libcore/java/util/concurrent/PriorityBlockingQueueTest.java
deleted file mode 100644
index ba7bc0c..0000000
--- a/luni/src/test/java/libcore/java/util/concurrent/PriorityBlockingQueueTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util.concurrent;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.concurrent.PriorityBlockingQueue;
-import java.util.concurrent.atomic.LongAdder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class PriorityBlockingQueueTest {
-
- @Test
- public void testForEach() {
- int capacity = 10;
- PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(capacity);
- for (int i = 0; i < capacity; ++i) {
- queue.add(Integer.valueOf(i+1));
- }
- LongAdder adder = new LongAdder();
- queue.forEach((Integer x) -> adder.add(x.longValue()));
- // The size is small enough for the sum not to overflow
- assertEquals(queue.size() * (queue.size() + 1) / 2, adder.sum());
- }
-}
diff --git a/ojluni/annotations/sdk/nullability/java/io/ByteArrayOutputStream.annotated.java b/ojluni/annotations/sdk/nullability/java/io/ByteArrayOutputStream.annotated.java
index 3fdca11..a642f85 100644
--- a/ojluni/annotations/sdk/nullability/java/io/ByteArrayOutputStream.annotated.java
+++ b/ojluni/annotations/sdk/nullability/java/io/ByteArrayOutputStream.annotated.java
@@ -50,6 +50,8 @@
@libcore.util.NonNull public synchronized java.lang.String toString(@libcore.util.NonNull java.lang.String charsetName) throws java.io.UnsupportedEncodingException { throw new RuntimeException("Stub!"); }
+@libcore.util.NonNull public synchronized java.lang.String toString(@libcore.util.NonNull java.nio.charset.Charset charset) { throw new RuntimeException("Stub!"); }
+
@Deprecated
@libcore.util.NonNull public synchronized java.lang.String toString(int hibyte) { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/src/main/java/java/io/BufferedOutputStream.java b/ojluni/src/main/java/java/io/BufferedOutputStream.java
index 0579704..f114485 100644
--- a/ojluni/src/main/java/java/io/BufferedOutputStream.java
+++ b/ojluni/src/main/java/java/io/BufferedOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,10 +32,9 @@
* system for each byte written.
*
* @author Arthur van Hoff
- * @since JDK1.0
+ * @since 1.0
*/
-public
-class BufferedOutputStream extends FilterOutputStream {
+public class BufferedOutputStream extends FilterOutputStream {
/**
* The internal buffer where data is stored.
*/
@@ -43,8 +42,8 @@
/**
* The number of valid bytes in the buffer. This value is always
- * in the range <tt>0</tt> through <tt>buf.length</tt>; elements
- * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid
+ * in the range {@code 0} through {@code buf.length}; elements
+ * {@code buf[0]} through {@code buf[count-1]} contain valid
* byte data.
*/
protected int count;
@@ -90,6 +89,7 @@
* @param b the byte to be written.
* @exception IOException if an I/O error occurs.
*/
+ @Override
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
diff --git a/ojluni/src/main/java/java/io/BufferedReader.java b/ojluni/src/main/java/java/io/BufferedReader.java
index fb814b1..20a240a 100644
--- a/ojluni/src/main/java/java/io/BufferedReader.java
+++ b/ojluni/src/main/java/java/io/BufferedReader.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -65,7 +65,7 @@
* @see java.nio.file.Files#newBufferedReader
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class BufferedReader extends Reader {
@@ -283,6 +283,7 @@
* stream has been reached
*
* @exception IOException If an I/O error occurs
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
@@ -307,14 +308,15 @@
/**
* Reads a line of text. A line is considered to be terminated by any one
- * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
- * followed immediately by a linefeed.
+ * of a line feed ('\n'), a carriage return ('\r'), a carriage return
+ * followed immediately by a line feed, or by reaching the end-of-file
+ * (EOF).
*
* @param ignoreLF If true, the next '\n' will be skipped
*
* @return A String containing the contents of the line, not including
* any line-termination characters, or null if the end of the
- * stream has been reached
+ * stream has been reached without reading any characters
*
* @see java.io.LineNumberReader#readLine()
*
@@ -385,12 +387,13 @@
/**
* Reads a line of text. A line is considered to be terminated by any one
- * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
- * followed immediately by a linefeed.
+ * of a line feed ('\n'), a carriage return ('\r'), a carriage return
+ * followed immediately by a line feed, or by reaching the end-of-file
+ * (EOF).
*
* @return A String containing the contents of the line, not including
* any line-termination characters, or null if the end of the
- * stream has been reached
+ * stream has been reached without reading any characters
*
* @exception IOException If an I/O error occurs
*
@@ -570,7 +573,7 @@
* @since 1.8
*/
public Stream<String> lines() {
- Iterator<String> iter = new Iterator<String>() {
+ Iterator<String> iter = new Iterator<>() {
String nextLine = null;
@Override
diff --git a/ojluni/src/main/java/java/io/BufferedWriter.java b/ojluni/src/main/java/java/io/BufferedWriter.java
index a5d810a..3d8e1ee 100644
--- a/ojluni/src/main/java/java/io/BufferedWriter.java
+++ b/ojluni/src/main/java/java/io/BufferedWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,7 @@
* The default is large enough for most purposes.
*
* <p> A newLine() method is provided, which uses the platform's own notion of
- * line separator as defined by the system property <tt>line.separator</tt>.
+ * line separator as defined by the system property {@code line.separator}.
* Not all platforms use the newline character ('\n') to terminate lines.
* Calling this method to terminate each output line is therefore preferred to
* writing a newline character directly.
@@ -60,7 +60,7 @@
* @see java.nio.file.Files#newBufferedWriter
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class BufferedWriter extends Writer {
@@ -73,12 +73,6 @@
private static int defaultCharBufferSize = 8192;
/**
- * Line separator string. This is the value of the line.separator
- * property at the moment that the stream was created.
- */
- private String lineSeparator;
-
- /**
* Creates a buffered character-output stream that uses a default-sized
* output buffer.
*
@@ -105,9 +99,6 @@
cb = new char[sz];
nChars = sz;
nextChar = 0;
-
- lineSeparator = java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("line.separator"));
}
/** Checks to make sure that the stream has not been closed */
@@ -162,13 +153,18 @@
* needed. If the requested length is at least as large as the buffer,
* however, then this method will flush the buffer and write the characters
* directly to the underlying stream. Thus redundant
- * <code>BufferedWriter</code>s will not copy data unnecessarily.
+ * {@code BufferedWriter}s will not copy data unnecessarily.
*
* @param cbuf A character array
* @param off Offset from which to start reading characters
* @param len Number of characters to write
*
- * @exception IOException If an I/O error occurs
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given array
+ *
+ * @throws IOException If an I/O error occurs
*/
public void write(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
@@ -204,17 +200,24 @@
/**
* Writes a portion of a String.
*
- * <p> If the value of the <tt>len</tt> parameter is negative then no
- * characters are written. This is contrary to the specification of this
- * method in the {@linkplain java.io.Writer#write(java.lang.String,int,int)
- * superclass}, which requires that an {@link IndexOutOfBoundsException} be
- * thrown.
+ * @implSpec
+ * While the specification of this method in the
+ * {@linkplain java.io.Writer#write(java.lang.String,int,int) superclass}
+ * recommends that an {@link IndexOutOfBoundsException} be thrown
+ * if {@code len} is negative or {@code off + len} is negative,
+ * the implementation in this class does not throw such an exception in
+ * these cases but instead simply writes no characters.
*
* @param s String to be written
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
*
- * @exception IOException If an I/O error occurs
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative,
+ * or {@code off + len} is greater than the length
+ * of the given string
+ *
+ * @throws IOException If an I/O error occurs
*/
public void write(String s, int off, int len) throws IOException {
synchronized (lock) {
@@ -234,13 +237,13 @@
/**
* Writes a line separator. The line separator string is defined by the
- * system property <tt>line.separator</tt>, and is not necessarily a single
+ * system property {@code line.separator}, and is not necessarily a single
* newline ('\n') character.
*
* @exception IOException If an I/O error occurs
*/
public void newLine() throws IOException {
- write(lineSeparator);
+ write(System.lineSeparator());
}
/**
diff --git a/ojluni/src/main/java/java/io/ByteArrayInputStream.java b/ojluni/src/main/java/java/io/ByteArrayInputStream.java
index d07f074..cb20f8c 100644
--- a/ojluni/src/main/java/java/io/ByteArrayInputStream.java
+++ b/ojluni/src/main/java/java/io/ByteArrayInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,30 +25,32 @@
package java.io;
+import java.util.Arrays;
+import java.util.Objects;
+
/**
- * A <code>ByteArrayInputStream</code> contains
+ * A {@code ByteArrayInputStream} contains
* an internal buffer that contains bytes that
* may be read from the stream. An internal
* counter keeps track of the next byte to
- * be supplied by the <code>read</code> method.
+ * be supplied by the {@code read} method.
* <p>
- * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
+ * Closing a {@code ByteArrayInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
- * generating an <tt>IOException</tt>.
+ * generating an {@code IOException}.
*
* @author Arthur van Hoff
* @see java.io.StringBufferInputStream
- * @since JDK1.0
+ * @since 1.0
*/
-public
-class ByteArrayInputStream extends InputStream {
+public class ByteArrayInputStream extends InputStream {
/**
* An array of bytes that was provided
- * by the creator of the stream. Elements <code>buf[0]</code>
- * through <code>buf[count-1]</code> are the
+ * by the creator of the stream. Elements {@code buf[0]}
+ * through {@code buf[count-1]} are the
* only bytes that can ever be read from the
- * stream; element <code>buf[pos]</code> is
+ * stream; element {@code buf[pos]} is
* the next byte to be read.
*/
protected byte buf[];
@@ -56,9 +58,9 @@
/**
* The index of the next character to read from the input stream buffer.
* This value should always be nonnegative
- * and not larger than the value of <code>count</code>.
+ * and not larger than the value of {@code count}.
* The next byte to be read from the input stream buffer
- * will be <code>buf[pos]</code>.
+ * will be {@code buf[pos]}.
*/
protected int pos;
@@ -66,14 +68,14 @@
* The currently marked position in the stream.
* ByteArrayInputStream objects are marked at position zero by
* default when constructed. They may be marked at another
- * position within the buffer by the <code>mark()</code> method.
+ * position within the buffer by the {@code mark()} method.
* The current buffer position is set to this point by the
- * <code>reset()</code> method.
+ * {@code reset()} method.
* <p>
* If no mark has been set, then the value of mark is the offset
* passed to the constructor (or 0 if the offset was not supplied).
*
- * @since JDK1.1
+ * @since 1.1
*/
protected int mark = 0;
@@ -81,22 +83,22 @@
* The index one greater than the last valid character in the input
* stream buffer.
* This value should always be nonnegative
- * and not larger than the length of <code>buf</code>.
+ * and not larger than the length of {@code buf}.
* It is one greater than the position of
- * the last byte within <code>buf</code> that
+ * the last byte within {@code buf} that
* can ever be read from the input stream buffer.
*/
protected int count;
/**
- * Creates a <code>ByteArrayInputStream</code>
- * so that it uses <code>buf</code> as its
+ * Creates a {@code ByteArrayInputStream}
+ * so that it uses {@code buf} as its
* buffer array.
* The buffer array is not copied.
- * The initial value of <code>pos</code>
- * is <code>0</code> and the initial value
- * of <code>count</code> is the length of
- * <code>buf</code>.
+ * The initial value of {@code pos}
+ * is {@code 0} and the initial value
+ * of {@code count} is the length of
+ * {@code buf}.
*
* @param buf the input buffer.
*/
@@ -107,12 +109,12 @@
}
/**
- * Creates <code>ByteArrayInputStream</code>
- * that uses <code>buf</code> as its
- * buffer array. The initial value of <code>pos</code>
- * is <code>offset</code> and the initial value
- * of <code>count</code> is the minimum of <code>offset+length</code>
- * and <code>buf.length</code>.
+ * Creates {@code ByteArrayInputStream}
+ * that uses {@code buf} as its
+ * buffer array. The initial value of {@code pos}
+ * is {@code offset} and the initial value
+ * of {@code count} is the minimum of {@code offset+length}
+ * and {@code buf.length}.
* The buffer array is not copied. The buffer's mark is
* set to the specified offset.
*
@@ -129,15 +131,15 @@
/**
* Reads the next byte of data from this input stream. The value
- * byte is returned as an <code>int</code> in the range
- * <code>0</code> to <code>255</code>. If no byte is available
+ * byte is returned as an {@code int} in the range
+ * {@code 0} to {@code 255}. If no byte is available
* because the end of the stream has been reached, the value
- * <code>-1</code> is returned.
+ * {@code -1} is returned.
* <p>
- * This <code>read</code> method
+ * This {@code read} method
* cannot block.
*
- * @return the next byte of data, or <code>-1</code> if the end of the
+ * @return the next byte of data, or {@code -1} if the end of the
* stream has been reached.
*/
public synchronized int read() {
@@ -145,40 +147,30 @@
}
/**
- * Reads up to <code>len</code> bytes of data into an array of bytes
- * from this input stream.
- * If <code>pos</code> equals <code>count</code>,
- * then <code>-1</code> is returned to indicate
- * end of file. Otherwise, the number <code>k</code>
- * of bytes read is equal to the smaller of
- * <code>len</code> and <code>count-pos</code>.
- * If <code>k</code> is positive, then bytes
- * <code>buf[pos]</code> through <code>buf[pos+k-1]</code>
- * are copied into <code>b[off]</code> through
- * <code>b[off+k-1]</code> in the manner performed
- * by <code>System.arraycopy</code>. The
- * value <code>k</code> is added into <code>pos</code>
- * and <code>k</code> is returned.
+ * Reads up to {@code len} bytes of data into an array of bytes from this
+ * input stream. If {@code pos} equals {@code count}, then {@code -1} is
+ * returned to indicate end of file. Otherwise, the number {@code k} of
+ * bytes read is equal to the smaller of {@code len} and {@code count-pos}.
+ * If {@code k} is positive, then bytes {@code buf[pos]} through
+ * {@code buf[pos+k-1]} are copied into {@code b[off]} through
+ * {@code b[off+k-1]} in the manner performed by {@code System.arraycopy}.
+ * The value {@code k} is added into {@code pos} and {@code k} is returned.
* <p>
- * This <code>read</code> method cannot block.
+ * This {@code read} method cannot block.
*
* @param b the buffer into which the data is read.
- * @param off the start offset in the destination array <code>b</code>
+ * @param off the start offset in the destination array {@code b}
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
- * <code>-1</code> if there is no more data because the end of
+ * {@code -1} if there is no more data because the end of
* the stream has been reached.
- * @exception NullPointerException If <code>b</code> is <code>null</code>.
- * @exception IndexOutOfBoundsException If <code>off</code> is negative,
- * <code>len</code> is negative, or <code>len</code> is greater than
- * <code>b.length - off</code>
+ * @throws NullPointerException If {@code b} is {@code null}.
+ * @throws IndexOutOfBoundsException If {@code off} is negative,
+ * {@code len} is negative, or {@code len} is greater than
+ * {@code b.length - off}
*/
public synchronized int read(byte b[], int off, int len) {
- if (b == null) {
- throw new NullPointerException();
- } else if (off < 0 || len < 0 || len > b.length - off) {
- throw new IndexOutOfBoundsException();
- }
+ Objects.checkFromIndexSize(off, len, b.length);
if (pos >= count) {
return -1;
@@ -196,14 +188,32 @@
return len;
}
+ public synchronized byte[] readAllBytes() {
+ byte[] result = Arrays.copyOfRange(buf, pos, count);
+ pos = count;
+ return result;
+ }
+
+ public int readNBytes(byte[] b, int off, int len) {
+ int n = read(b, off, len);
+ return n == -1 ? 0 : n;
+ }
+
+ public synchronized long transferTo(OutputStream out) throws IOException {
+ int len = count - pos;
+ out.write(buf, pos, len);
+ pos = count;
+ return len;
+ }
+
/**
- * Skips <code>n</code> bytes of input from this input stream. Fewer
+ * Skips {@code n} bytes of input from this input stream. Fewer
* bytes might be skipped if the end of the input stream is reached.
- * The actual number <code>k</code>
+ * The actual number {@code k}
* of bytes to be skipped is equal to the smaller
- * of <code>n</code> and <code>count-pos</code>.
- * The value <code>k</code> is added into <code>pos</code>
- * and <code>k</code> is returned.
+ * of {@code n} and {@code count-pos}.
+ * The value {@code k} is added into {@code pos}
+ * and {@code k} is returned.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
@@ -222,7 +232,7 @@
* Returns the number of remaining bytes that can be read (or skipped over)
* from this input stream.
* <p>
- * The value returned is <code>count - pos</code>,
+ * The value returned is {@code count - pos},
* which is the number of bytes remaining to be read from the input buffer.
*
* @return the number of remaining bytes that can be read (or skipped
@@ -233,11 +243,11 @@
}
/**
- * Tests if this <code>InputStream</code> supports mark/reset. The
- * <code>markSupported</code> method of <code>ByteArrayInputStream</code>
- * always returns <code>true</code>.
+ * Tests if this {@code InputStream} supports mark/reset. The
+ * {@code markSupported} method of {@code ByteArrayInputStream}
+ * always returns {@code true}.
*
- * @since JDK1.1
+ * @since 1.1
*/
public boolean markSupported() {
return true;
@@ -253,10 +263,10 @@
* offset passed to the constructor (or 0 if the offset was not
* supplied).
*
- * <p> Note: The <code>readAheadLimit</code> for this class
+ * <p> Note: The {@code readAheadLimit} for this class
* has no meaning.
*
- * @since JDK1.1
+ * @since 1.1
*/
public void mark(int readAheadLimit) {
mark = pos;
@@ -272,9 +282,9 @@
}
/**
- * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
+ * Closing a {@code ByteArrayInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
- * generating an <tt>IOException</tt>.
+ * generating an {@code IOException}.
*/
public void close() throws IOException {
}
diff --git a/ojluni/src/main/java/java/io/ByteArrayOutputStream.java b/ojluni/src/main/java/java/io/ByteArrayOutputStream.java
index f1d429b..df2fdd0 100644
--- a/ojluni/src/main/java/java/io/ByteArrayOutputStream.java
+++ b/ojluni/src/main/java/java/io/ByteArrayOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,21 +25,23 @@
package java.io;
+import java.nio.charset.Charset;
import java.util.Arrays;
+import java.util.Objects;
/**
* This class implements an output stream in which the data is
* written into a byte array. The buffer automatically grows as data
* is written to it.
- * The data can be retrieved using <code>toByteArray()</code> and
- * <code>toString()</code>.
+ * The data can be retrieved using {@code toByteArray()} and
+ * {@code toString()}.
* <p>
- * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+ * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
- * generating an <tt>IOException</tt>.
+ * generating an {@code IOException}.
*
* @author Arthur van Hoff
- * @since JDK1.0
+ * @since 1.0
*/
public class ByteArrayOutputStream extends OutputStream {
@@ -55,7 +57,7 @@
protected int count;
/**
- * Creates a new byte array output stream. The buffer capacity is
+ * Creates a new {@code ByteArrayOutputStream}. The buffer capacity is
* initially 32 bytes, though its size increases if necessary.
*/
public ByteArrayOutputStream() {
@@ -63,11 +65,11 @@
}
/**
- * Creates a new byte array output stream, with a buffer capacity of
+ * Creates a new {@code ByteArrayOutputStream}, with a buffer capacity of
* the specified size, in bytes.
*
- * @param size the initial size.
- * @exception IllegalArgumentException if size is negative.
+ * @param size the initial size.
+ * @throws IllegalArgumentException if size is negative.
*/
public ByteArrayOutputStream(int size) {
if (size < 0) {
@@ -82,7 +84,7 @@
* at least the number of elements specified by the minimum
* capacity argument.
*
- * @param minCapacity the desired minimum capacity
+ * @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if {@code minCapacity < 0}. This is
* interpreted as a request for the unsatisfiably large capacity
* {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
@@ -127,7 +129,7 @@
}
/**
- * Writes the specified byte to this byte array output stream.
+ * Writes the specified byte to this {@code ByteArrayOutputStream}.
*
* @param b the byte to be written.
*/
@@ -138,38 +140,56 @@
}
/**
- * Writes <code>len</code> bytes from the specified byte array
- * starting at offset <code>off</code> to this byte array output stream.
+ * Writes {@code len} bytes from the specified byte array
+ * starting at offset {@code off} to this {@code ByteArrayOutputStream}.
*
* @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
+ * @throws NullPointerException if {@code b} is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} is negative,
+ * {@code len} is negative, or {@code len} is greater than
+ * {@code b.length - off}
*/
public synchronized void write(byte b[], int off, int len) {
- if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) - b.length > 0)) {
- throw new IndexOutOfBoundsException();
- }
+ Objects.checkFromIndexSize(off, len, b.length);
ensureCapacity(count + len);
System.arraycopy(b, off, buf, count, len);
count += len;
}
/**
- * Writes the complete contents of this byte array output stream to
- * the specified output stream argument, as if by calling the output
- * stream's write method using <code>out.write(buf, 0, count)</code>.
+ * Writes the complete contents of the specified byte array
+ * to this {@code ByteArrayOutputStream}.
*
- * @param out the output stream to which to write the data.
- * @exception IOException if an I/O error occurs.
+ * @apiNote
+ * This method is equivalent to {@link #write(byte[],int,int)
+ * write(b, 0, b.length)}.
+ *
+ * @param b the data.
+ * @throws NullPointerException if {@code b} is {@code null}.
+ * @since 11
+ */
+ public void writeBytes(byte b[]) {
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Writes the complete contents of this {@code ByteArrayOutputStream} to
+ * the specified output stream argument, as if by calling the output
+ * stream's write method using {@code out.write(buf, 0, count)}.
+ *
+ * @param out the output stream to which to write the data.
+ * @throws NullPointerException if {@code out} is {@code null}.
+ * @throws IOException if an I/O error occurs.
*/
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(buf, 0, count);
}
/**
- * Resets the <code>count</code> field of this byte array output
- * stream to zero, so that all currently accumulated output in the
+ * Resets the {@code count} field of this {@code ByteArrayOutputStream}
+ * to zero, so that all currently accumulated output in the
* output stream is discarded. The output stream can be used again,
* reusing the already allocated buffer space.
*
@@ -187,14 +207,14 @@
* @return the current contents of this output stream, as a byte array.
* @see java.io.ByteArrayOutputStream#size()
*/
- public synchronized byte toByteArray()[] {
+ public synchronized byte[] toByteArray() {
return Arrays.copyOf(buf, count);
}
/**
* Returns the current size of the buffer.
*
- * @return the value of the <code>count</code> field, which is the number
+ * @return the value of the {@code count} field, which is the number
* of valid bytes in this output stream.
* @see java.io.ByteArrayOutputStream#count
*/
@@ -204,7 +224,7 @@
/**
* Converts the buffer's contents into a string decoding bytes using the
- * platform's default character set. The length of the new <tt>String</tt>
+ * platform's default character set. The length of the new {@code String}
* is a function of the character set, and hence may not be equal to the
* size of the buffer.
*
@@ -215,7 +235,7 @@
* required.
*
* @return String decoded from the buffer's contents.
- * @since JDK1.1
+ * @since 1.1
*/
public synchronized String toString() {
return new String(buf, 0, count);
@@ -223,21 +243,34 @@
/**
* Converts the buffer's contents into a string by decoding the bytes using
- * the named {@link java.nio.charset.Charset charset}. The length of the new
- * <tt>String</tt> is a function of the charset, and hence may not be equal
- * to the length of the byte array.
+ * the named {@link java.nio.charset.Charset charset}.
*
- * <p> This method always replaces malformed-input and unmappable-character
- * sequences with this charset's default replacement string. The {@link
- * java.nio.charset.CharsetDecoder} class should be used when more control
- * over the decoding process is required.
+ * <p> This method is equivalent to {@code #toString(charset)} that takes a
+ * {@link java.nio.charset.Charset charset}.
*
- * @param charsetName the name of a supported
- * {@link java.nio.charset.Charset charset}
- * @return String decoded from the buffer's contents.
- * @exception UnsupportedEncodingException
- * If the named charset is not supported
- * @since JDK1.1
+ * <p> An invocation of this method of the form
+ *
+ * <pre> {@code
+ * ByteArrayOutputStream b = ...
+ * b.toString("UTF-8")
+ * }
+ * </pre>
+ *
+ * behaves in exactly the same way as the expression
+ *
+ * <pre> {@code
+ * ByteArrayOutputStream b = ...
+ * b.toString(StandardCharsets.UTF_8)
+ * }
+ * </pre>
+ *
+ *
+ * @param charsetName the name of a supported
+ * {@link java.nio.charset.Charset charset}
+ * @return String decoded from the buffer's contents.
+ * @throws UnsupportedEncodingException
+ * If the named charset is not supported
+ * @since 1.1
*/
public synchronized String toString(String charsetName)
throws UnsupportedEncodingException
@@ -246,20 +279,41 @@
}
/**
+ * Converts the buffer's contents into a string by decoding the bytes using
+ * the specified {@link java.nio.charset.Charset charset}. The length of the new
+ * {@code String} is a function of the charset, and hence may not be equal
+ * to the length of the byte array.
+ *
+ * <p> This method always replaces malformed-input and unmappable-character
+ * sequences with the charset's default replacement string. The {@link
+ * java.nio.charset.CharsetDecoder} class should be used when more control
+ * over the decoding process is required.
+ *
+ * @param charset the {@linkplain java.nio.charset.Charset charset}
+ * to be used to decode the {@code bytes}
+ * @return String decoded from the buffer's contents.
+ * @since 10
+ */
+ public synchronized String toString(Charset charset) {
+ return new String(buf, 0, count, charset);
+ }
+
+ /**
* Creates a newly allocated string. Its size is the current size of
* the output stream and the valid contents of the buffer have been
* copied into it. Each character <i>c</i> in the resulting string is
* constructed from the corresponding element <i>b</i> in the byte
* array such that:
- * <blockquote><pre>
- * c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
- * </pre></blockquote>
+ * <blockquote><pre>{@code
+ * c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
+ * }</pre></blockquote>
*
* @deprecated This method does not properly convert bytes into characters.
* As of JDK 1.1, the preferred way to do this is via the
- * <code>toString(String enc)</code> method, which takes an encoding-name
- * argument, or the <code>toString()</code> method, which uses the
- * platform's default character encoding.
+ * {@link #toString(String charsetName)} or {@link #toString(Charset charset)}
+ * method, which takes an encoding-name or charset argument,
+ * or the {@code toString()} method, which uses the platform's default
+ * character encoding.
*
* @param hibyte the high byte of each resulting Unicode character.
* @return the current contents of the output stream, as a string.
@@ -273,9 +327,9 @@
}
/**
- * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
+ * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
- * generating an <tt>IOException</tt>.
+ * generating an {@code IOException}.
*/
public void close() throws IOException {
}
diff --git a/ojluni/src/main/java/java/io/CharArrayReader.java b/ojluni/src/main/java/java/io/CharArrayReader.java
index 4140976..08b1381 100644
--- a/ojluni/src/main/java/java/io/CharArrayReader.java
+++ b/ojluni/src/main/java/java/io/CharArrayReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,7 @@
* character-input stream.
*
* @author Herb Jellinek
- * @since JDK1.1
+ * @since 1.1
*/
public class CharArrayReader extends Reader {
/** The character buffer. */
@@ -62,13 +62,13 @@
* Creates a CharArrayReader from the specified array of chars.
*
* <p> The resulting reader will start reading at the given
- * <tt>offset</tt>. The total number of <tt>char</tt> values that can be
- * read from this reader will be either <tt>length</tt> or
- * <tt>buf.length-offset</tt>, whichever is smaller.
+ * {@code offset}. The total number of {@code char} values that can be
+ * read from this reader will be either {@code length} or
+ * {@code buf.length-offset}, whichever is smaller.
*
* @throws IllegalArgumentException
- * If <tt>offset</tt> is negative or greater than
- * <tt>buf.length</tt>, or if <tt>length</tt> is negative, or if
+ * If {@code offset} is negative or greater than
+ * {@code buf.length}, or if {@code length} is negative, or if
* the sum of these two values is negative.
*
* @param buf Input buffer (not copied)
@@ -116,6 +116,7 @@
* the end of the stream has been reached
*
* @exception IOException If an I/O error occurs
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public int read(char b[], int off, int len) throws IOException {
synchronized (lock) {
@@ -130,12 +131,11 @@
if (pos >= count) {
return -1;
}
- // BEGIN Android-changed: Backport of OpenJDK 9b132 fix to avoid integer overflow.
+
int avail = count - pos;
if (len > avail) {
len = avail;
}
- // END Android-changed: Backport of OpenJDK 9b132 fix to avoid integer overflow.
if (len <= 0) {
return 0;
}
@@ -160,12 +160,11 @@
public long skip(long n) throws IOException {
synchronized (lock) {
ensureOpen();
- // BEGIN Android-changed: Backport of OpenJDK 9b132 fix to avoid integer overflow.
+
long avail = count - pos;
if (n > avail) {
n = avail;
}
- // END Android-changed: Backport of OpenJDK 9b132 fix to avoid integer overflow.
if (n < 0) {
return 0;
}
@@ -230,9 +229,12 @@
* Closes the stream and releases any system resources associated with
* it. Once the stream has been closed, further read(), ready(),
* mark(), reset(), or skip() invocations will throw an IOException.
- * Closing a previously closed stream has no effect.
+ * Closing a previously closed stream has no effect. This method will block
+ * while there is another thread blocking on the reader.
*/
public void close() {
- buf = null;
+ synchronized (lock) {
+ buf = null;
+ }
}
}
diff --git a/ojluni/src/main/java/java/io/CharArrayWriter.java b/ojluni/src/main/java/java/io/CharArrayWriter.java
index 56a5821..e8bf2c3 100644
--- a/ojluni/src/main/java/java/io/CharArrayWriter.java
+++ b/ojluni/src/main/java/java/io/CharArrayWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,7 +37,7 @@
* without generating an IOException.
*
* @author Herb Jellinek
- * @since JDK1.1
+ * @since 1.1
*/
public
class CharArrayWriter extends Writer {
@@ -91,6 +91,11 @@
* @param c the data to be written
* @param off the start offset in the data
* @param len the number of chars that are written
+ *
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given array
*/
public void write(char c[], int off, int len) {
if ((off < 0) || (off > c.length) || (len < 0) ||
@@ -114,6 +119,11 @@
* @param str String to be written from
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
+ *
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given string
*/
public void write(String str, int off, int len) {
synchronized (lock) {
@@ -141,21 +151,21 @@
/**
* Appends the specified character sequence to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(csq)</tt>
+ * <p> An invocation of this method of the form {@code out.append(csq)}
* behaves in exactly the same way as the invocation
*
* <pre>
* out.write(csq.toString()) </pre>
*
- * <p> Depending on the specification of <tt>toString</tt> for the
- * character sequence <tt>csq</tt>, the entire sequence may not be
- * appended. For instance, invoking the <tt>toString</tt> method of a
+ * <p> Depending on the specification of {@code toString} for the
+ * character sequence {@code csq}, the entire sequence may not be
+ * appended. For instance, invoking the {@code toString} method of a
* character buffer will return a subsequence whose content depends upon
* the buffer's position and limit.
*
* @param csq
- * The character sequence to append. If <tt>csq</tt> is
- * <tt>null</tt>, then the four characters <tt>"null"</tt> are
+ * The character sequence to append. If {@code csq} is
+ * {@code null}, then the four characters {@code "null"} are
* appended to this writer.
*
* @return This writer
@@ -163,7 +173,7 @@
* @since 1.5
*/
public CharArrayWriter append(CharSequence csq) {
- String s = (csq == null ? "null" : csq.toString());
+ String s = String.valueOf(csq);
write(s, 0, s.length());
return this;
}
@@ -171,8 +181,9 @@
/**
* Appends a subsequence of the specified character sequence to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(csq, start,
- * end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
+ * <p> An invocation of this method of the form
+ * {@code out.append(csq, start, end)} when
+ * {@code csq} is not {@code null}, behaves in
* exactly the same way as the invocation
*
* <pre>
@@ -180,9 +191,9 @@
*
* @param csq
* The character sequence from which a subsequence will be
- * appended. If <tt>csq</tt> is <tt>null</tt>, then characters
- * will be appended as if <tt>csq</tt> contained the four
- * characters <tt>"null"</tt>.
+ * appended. If {@code csq} is {@code null}, then characters
+ * will be appended as if {@code csq} contained the four
+ * characters {@code "null"}.
*
* @param start
* The index of the first character in the subsequence
@@ -194,22 +205,21 @@
* @return This writer
*
* @throws IndexOutOfBoundsException
- * If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
- * is greater than <tt>end</tt>, or <tt>end</tt> is greater than
- * <tt>csq.length()</tt>
+ * If {@code start} or {@code end} are negative, {@code start}
+ * is greater than {@code end}, or {@code end} is greater than
+ * {@code csq.length()}
*
* @since 1.5
*/
public CharArrayWriter append(CharSequence csq, int start, int end) {
- String s = (csq == null ? "null" : csq).subSequence(start, end).toString();
- write(s, 0, s.length());
- return this;
+ if (csq == null) csq = "null";
+ return append(csq.subSequence(start, end));
}
/**
* Appends the specified character to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(c)</tt>
+ * <p> An invocation of this method of the form {@code out.append(c)}
* behaves in exactly the same way as the invocation
*
* <pre>
@@ -240,7 +250,7 @@
*
* @return an array of chars copied from the input data.
*/
- public char toCharArray()[] {
+ public char[] toCharArray() {
synchronized (lock) {
return Arrays.copyOf(buf, count);
}
diff --git a/ojluni/src/main/java/java/io/CharConversionException.java b/ojluni/src/main/java/java/io/CharConversionException.java
index ba79fe3..832dc49 100644
--- a/ojluni/src/main/java/java/io/CharConversionException.java
+++ b/ojluni/src/main/java/java/io/CharConversionException.java
@@ -28,7 +28,7 @@
* Base class for character conversion exceptions.
*
* @author Asmus Freytag
- * @since JDK1.1
+ * @since 1.1
*/
public class CharConversionException
extends java.io.IOException
diff --git a/ojluni/src/main/java/java/io/DataInput.java b/ojluni/src/main/java/java/io/DataInput.java
index 3e0f0dd..86d8d3e 100644
--- a/ojluni/src/main/java/java/io/DataInput.java
+++ b/ojluni/src/main/java/java/io/DataInput.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,87 +48,96 @@
* may be thrown if the input stream has been
* closed.
*
- * <h3><a name="modified-utf-8">Modified UTF-8</a></h3>
+ * <h3><a id="modified-utf-8">Modified UTF-8</a></h3>
* <p>
* Implementations of the DataInput and DataOutput interfaces represent
* Unicode strings in a format that is a slight modification of UTF-8.
* (For information regarding the standard UTF-8 format, see section
* <i>3.9 Unicode Encoding Forms</i> of <i>The Unicode Standard, Version
- * 4.0</i>).
- * Note that in the following table, the most significant bit appears in the
- * far left-hand column.
+ * 4.0</i>)
*
- * <blockquote>
- * <table border="1" cellspacing="0" cellpadding="8"
- * summary="Bit values and bytes">
- * <tr>
- * <th colspan="9"><span style="font-weight:normal">
- * All characters in the range {@code '\u005Cu0001'} to
- * {@code '\u005Cu007F'} are represented by a single byte:</span></th>
- * </tr>
- * <tr>
- * <td></td>
- * <th colspan="8" id="bit_a">Bit Values</th>
- * </tr>
- * <tr>
- * <th id="byte1_a">Byte 1</th>
- * <td><center>0</center>
- * <td colspan="7"><center>bits 6-0</center>
- * </tr>
- * <tr>
- * <th colspan="9"><span style="font-weight:normal">
- * The null character {@code '\u005Cu0000'} and characters
+ * <ul>
+ * <li>Characters in the range {@code '\u005Cu0001'} to
+ * {@code '\u005Cu007F'} are represented by a single byte.
+ * <li>The null character {@code '\u005Cu0000'} and characters
* in the range {@code '\u005Cu0080'} to {@code '\u005Cu07FF'} are
- * represented by a pair of bytes:</span></th>
+ * represented by a pair of bytes.
+ * <li>Characters in the range {@code '\u005Cu0800'}
+ * to {@code '\u005CuFFFF'} are represented by three bytes.
+ * </ul>
+ *
+ * <table class="plain" style="margin-left:2em;">
+ * <caption>Encoding of UTF-8 values</caption>
+ * <thead>
+ * <tr>
+ * <th scope="col" rowspan="2">Value</th>
+ * <th scope="col" rowspan="2">Byte</th>
+ * <th scope="col" colspan="8" id="bit_a">Bit Values</th>
* </tr>
* <tr>
- * <td></td>
- * <th colspan="8" id="bit_b">Bit Values</th>
+ * <!-- Value -->
+ * <!-- Byte -->
+ * <th scope="col" style="width:3em"> 7 </th>
+ * <th scope="col" style="width:3em"> 6 </th>
+ * <th scope="col" style="width:3em"> 5 </th>
+ * <th scope="col" style="width:3em"> 4 </th>
+ * <th scope="col" style="width:3em"> 3 </th>
+ * <th scope="col" style="width:3em"> 2 </th>
+ * <th scope="col" style="width:3em"> 1 </th>
+ * <th scope="col" style="width:3em"> 0 </th>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row" style="text-align:left; font-weight:normal">
+ * {@code \u005Cu0001} to {@code \u005Cu007F} </th>
+ * <th scope="row" style="font-weight:normal; text-align:center"> 1 </th>
+ * <td style="text-align:center">0
+ * <td colspan="7" style="text-align:right; padding-right:6em">bits 6-0
* </tr>
* <tr>
- * <th id="byte1_b">Byte 1</th>
- * <td><center>1</center>
- * <td><center>1</center>
- * <td><center>0</center>
- * <td colspan="5"><center>bits 10-6</center>
+ * <th scope="row" rowspan="2" style="text-align:left; font-weight:normal">
+ * {@code \u005Cu0000},<br>
+ * {@code \u005Cu0080} to {@code \u005Cu07FF} </th>
+ * <th scope="row" style="font-weight:normal; text-align:center"> 1 </th>
+ * <td style="text-align:center">1
+ * <td style="text-align:center">1
+ * <td style="text-align:center">0
+ * <td colspan="5" style="text-align:right; padding-right:6em">bits 10-6
* </tr>
* <tr>
- * <th id="byte2_a">Byte 2</th>
- * <td><center>1</center>
- * <td><center>0</center>
- * <td colspan="6"><center>bits 5-0</center>
+ * <!-- (value) -->
+ * <th scope="row" style="font-weight:normal; text-align:center"> 2 </th>
+ * <td style="text-align:center">1
+ * <td style="text-align:center">0
+ * <td colspan="6" style="text-align:right; padding-right:6em">bits 5-0
* </tr>
* <tr>
- * <th colspan="9"><span style="font-weight:normal">
- * {@code char} values in the range {@code '\u005Cu0800'}
- * to {@code '\u005CuFFFF'} are represented by three bytes:</span></th>
+ * <th scope="row" rowspan="3" style="text-align:left; font-weight:normal">
+ * {@code \u005Cu0800} to {@code \u005CuFFFF} </th>
+ * <th scope="row" style="font-weight:normal; text-align:center"> 1 </th>
+ * <td style="text-align:center">1
+ * <td style="text-align:center">1
+ * <td style="text-align:center">1
+ * <td style="text-align:center">0
+ * <td colspan="4" style="text-align:right; padding-right:6em">bits 15-12
* </tr>
* <tr>
- * <td></td>
- * <th colspan="8"id="bit_c">Bit Values</th>
+ * <!-- (value) -->
+ * <th scope="row" style="font-weight:normal; text-align:center"> 2 </th>
+ * <td style="text-align:center">1
+ * <td style="text-align:center">0
+ * <td colspan="6" style="text-align:right; padding-right:6em">bits 11-6
* </tr>
* <tr>
- * <th id="byte1_c">Byte 1</th>
- * <td><center>1</center>
- * <td><center>1</center>
- * <td><center>1</center>
- * <td><center>0</center>
- * <td colspan="4"><center>bits 15-12</center>
+ * <!-- (value) -->
+ * <th scope="row" style="font-weight:normal; text-align:center"> 3 </th>
+ * <td style="text-align:center">1
+ * <td style="text-align:center">0
+ * <td colspan="6" style="text-align:right; padding-right:6em">bits 5-0
* </tr>
- * <tr>
- * <th id="byte2_b">Byte 2</th>
- * <td><center>1</center>
- * <td><center>0</center>
- * <td colspan="6"><center>bits 11-6</center>
- * </tr>
- * <tr>
- * <th id="byte3">Byte 3</th>
- * <td><center>1</center>
- * <td><center>0</center>
- * <td colspan="6"><center>bits 5-0</center>
- * </tr>
+ * </tbody>
* </table>
- * </blockquote>
+ *
* <p>
* The differences between this format and the
* standard UTF-8 format are the following:
@@ -143,7 +152,7 @@
* @author Frank Yellin
* @see java.io.DataInputStream
* @see java.io.DataOutput
- * @since JDK1.0
+ * @since 1.0
*/
public
interface DataInput {
@@ -182,10 +191,11 @@
* not all bytes of {@code b} have been
* updated with data from the input stream.
*
- * @param b the buffer into which the data is read.
- * @exception EOFException if this stream reaches the end before reading
- * all the bytes.
- * @exception IOException if an I/O error occurs.
+ * @param b the buffer into which the data is read.
+ * @throws NullPointerException if {@code b} is {@code null}.
+ * @throws EOFException if this stream reaches the end before reading
+ * all the bytes.
+ * @throws IOException if an I/O error occurs.
*/
void readFully(byte b[]) throws IOException;
@@ -226,12 +236,16 @@
* and so on. The number of bytes read is,
* at most, equal to {@code len}.
*
- * @param b the buffer into which the data is read.
- * @param off an int specifying the offset into the data.
- * @param len an int specifying the number of bytes to read.
- * @exception EOFException if this stream reaches the end before reading
- * all the bytes.
- * @exception IOException if an I/O error occurs.
+ * @param b the buffer into which the data is read.
+ * @param off an int specifying the offset in the data array {@code b}.
+ * @param len an int specifying the number of bytes to read.
+ * @throws NullPointerException if {@code b} is {@code null}.
+ * @throws IndexOutOfBoundsException if {@code off} is negative,
+ * {@code len} is negative, or {@code len} is greater than
+ * {@code b.length - off}.
+ * @throws EOFException if this stream reaches the end before reading
+ * all the bytes.
+ * @throws IOException if an I/O error occurs.
*/
void readFully(byte b[], int off, int len) throws IOException;
diff --git a/ojluni/src/main/java/java/io/DataInputStream.java b/ojluni/src/main/java/java/io/DataInputStream.java
index 19d4067..2098344 100644
--- a/ojluni/src/main/java/java/io/DataInputStream.java
+++ b/ojluni/src/main/java/java/io/DataInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,7 +40,7 @@
*
* @author Arthur van Hoff
* @see java.io.DataOutputStream
- * @since JDK1.0
+ * @since 1.0
*/
public
class DataInputStream extends FilterInputStream implements DataInput {
@@ -153,38 +153,43 @@
}
/**
- * See the general contract of the <code>readFully</code>
- * method of <code>DataInput</code>.
+ * See the general contract of the {@code readFully}
+ * method of {@code DataInput}.
* <p>
* Bytes
* for this operation are read from the contained
* input stream.
*
- * @param b the buffer into which the data is read.
- * @exception EOFException if this input stream reaches the end before
- * reading all the bytes.
- * @exception IOException the stream has been closed and the contained
- * input stream does not support reading after close, or
- * another I/O error occurs.
- * @see java.io.FilterInputStream#in
+ * @param b the buffer into which the data is read.
+ * @throws NullPointerException if {@code b} is {@code null}.
+ * @throws EOFException if this input stream reaches the end before
+ * reading all the bytes.
+ * @throws IOException the stream has been closed and the contained
+ * input stream does not support reading after close, or
+ * another I/O error occurs.
+ * @see java.io.FilterInputStream#in
*/
public final void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
/**
- * See the general contract of the <code>readFully</code>
- * method of <code>DataInput</code>.
+ * See the general contract of the {@code readFully}
+ * method of {@code DataInput}.
* <p>
* Bytes
* for this operation are read from the contained
* input stream.
*
* @param b the buffer into which the data is read.
- * @param off the start offset of the data.
+ * @param off the start offset in the data array {@code b}.
* @param len the number of bytes to read.
+ * @exception NullPointerException if {@code b} is {@code null}.
+ * @exception IndexOutOfBoundsException if {@code off} is negative,
+ * {@code len} is negative, or {@code len} is greater than
+ * {@code b.length - off}.
* @exception EOFException if this input stream reaches the end before
- * reading all the bytes.
+ * reading all the bytes.
* @exception IOException the stream has been closed and the contained
* input stream does not support reading after close, or
* another I/O error occurs.
@@ -582,7 +587,7 @@
* valid modified UTF-8 encoding of a Unicode string.
* @see java.io.DataInputStream#readUnsignedShort()
*/
- public final static String readUTF(DataInput in) throws IOException {
+ public static final String readUTF(DataInput in) throws IOException {
int utflen = in.readUnsignedShort();
byte[] bytearr = null;
char[] chararr = null;
diff --git a/ojluni/src/main/java/java/io/DataOutput.java b/ojluni/src/main/java/java/io/DataOutput.java
index c6692a6..50de041 100644
--- a/ojluni/src/main/java/java/io/DataOutput.java
+++ b/ojluni/src/main/java/java/io/DataOutput.java
@@ -44,7 +44,7 @@
* @author Frank Yellin
* @see java.io.DataInput
* @see java.io.DataOutputStream
- * @since JDK1.0
+ * @since 1.0
*/
public
interface DataOutput {
diff --git a/ojluni/src/main/java/java/io/DataOutputStream.java b/ojluni/src/main/java/java/io/DataOutputStream.java
index 99fafed..392abba 100644
--- a/ojluni/src/main/java/java/io/DataOutputStream.java
+++ b/ojluni/src/main/java/java/io/DataOutputStream.java
@@ -32,7 +32,7 @@
*
* @author unascribed
* @see java.io.DataInputStream
- * @since JDK1.0
+ * @since 1.0
*/
public
class DataOutputStream extends FilterOutputStream implements DataOutput {
diff --git a/ojluni/src/main/java/java/io/EOFException.java b/ojluni/src/main/java/java/io/EOFException.java
index 536669f..446373e 100644
--- a/ojluni/src/main/java/java/io/EOFException.java
+++ b/ojluni/src/main/java/java/io/EOFException.java
@@ -36,7 +36,7 @@
* @author Frank Yellin
* @see java.io.DataInputStream
* @see java.io.IOException
- * @since JDK1.0
+ * @since 1.0
*/
public
class EOFException extends IOException {
diff --git a/ojluni/src/main/java/java/io/Externalizable.java b/ojluni/src/main/java/java/io/Externalizable.java
index f9e88fe..f5f3f9a 100644
--- a/ojluni/src/main/java/java/io/Externalizable.java
+++ b/ojluni/src/main/java/java/io/Externalizable.java
@@ -61,7 +61,7 @@
* @see java.io.ObjectOutput
* @see java.io.ObjectInput
* @see java.io.Serializable
- * @since JDK1.1
+ * @since 1.1
*/
public interface Externalizable extends java.io.Serializable {
/**
diff --git a/ojluni/src/main/java/java/io/FileNotFoundException.java b/ojluni/src/main/java/java/io/FileNotFoundException.java
index 278fa1d..898a9b79 100644
--- a/ojluni/src/main/java/java/io/FileNotFoundException.java
+++ b/ojluni/src/main/java/java/io/FileNotFoundException.java
@@ -37,7 +37,7 @@
* example when an attempt is made to open a read-only file for writing.
*
* @author unascribed
- * @since JDK1.0
+ * @since 1.0
*/
public class FileNotFoundException extends IOException {
diff --git a/ojluni/src/main/java/java/io/FilenameFilter.java b/ojluni/src/main/java/java/io/FilenameFilter.java
index 25d866b..2af02e8 100644
--- a/ojluni/src/main/java/java/io/FilenameFilter.java
+++ b/ojluni/src/main/java/java/io/FilenameFilter.java
@@ -38,7 +38,7 @@
* @author Jonathan Payne
* @see java.io.File
* @see java.io.File#list(java.io.FilenameFilter)
- * @since JDK1.0
+ * @since 1.0
*/
@FunctionalInterface
public interface FilenameFilter {
diff --git a/ojluni/src/main/java/java/io/FilterInputStream.java b/ojluni/src/main/java/java/io/FilterInputStream.java
index 10beaea..05ab5fa 100644
--- a/ojluni/src/main/java/java/io/FilterInputStream.java
+++ b/ojluni/src/main/java/java/io/FilterInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,7 +40,7 @@
* and fields.
*
* @author Jonathan Payne
- * @since JDK1.0
+ * @since 1.0
*/
public
class FilterInputStream extends InputStream {
@@ -84,7 +84,7 @@
}
/**
- * Reads up to <code>byte.length</code> bytes of data from this
+ * Reads up to <code>b.length</code> bytes of data from this
* input stream into an array of bytes. This method blocks until some
* input is available.
* <p>
@@ -144,8 +144,7 @@
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
- * @exception IOException if the stream does not support seek,
- * or if some other I/O error occurs.
+ * @throws IOException if {@code in.skip(n)} throws an IOException.
*/
public long skip(long n) throws IOException {
return in.skip(n);
diff --git a/ojluni/src/main/java/java/io/FilterOutputStream.java b/ojluni/src/main/java/java/io/FilterOutputStream.java
index 89f5ef5..55abe73 100644
--- a/ojluni/src/main/java/java/io/FilterOutputStream.java
+++ b/ojluni/src/main/java/java/io/FilterOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,27 +39,30 @@
* methods as well as provide additional methods and fields.
*
* @author Jonathan Payne
- * @since JDK1.0
+ * @since 1.0
*/
-public
-class FilterOutputStream extends OutputStream {
+public class FilterOutputStream extends OutputStream {
/**
* The underlying output stream to be filtered.
*/
protected OutputStream out;
- // Android-added: Integrate OpenJDK 9 fix for double-close. http://b/122733269.
/**
* Whether the stream is closed; implicitly initialized to false.
*/
- private boolean closed;
+ private volatile boolean closed;
+
+ /**
+ * Object used to prevent a race on the 'closed' instance variable.
+ */
+ private final Object closeLock = new Object();
/**
* Creates an output stream filter built on top of the specified
* underlying output stream.
*
* @param out the underlying output stream to be assigned to
- * the field <tt>this.out</tt> for later use, or
+ * the field {@code this.out} for later use, or
* <code>null</code> if this instance is to be
* created without an underlying stream.
*/
@@ -72,13 +75,14 @@
* <p>
* The <code>write</code> method of <code>FilterOutputStream</code>
* calls the <code>write</code> method of its underlying output stream,
- * that is, it performs <tt>out.write(b)</tt>.
+ * that is, it performs {@code out.write(b)}.
* <p>
- * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>.
+ * Implements the abstract {@code write} method of {@code OutputStream}.
*
* @param b the <code>byte</code>.
* @exception IOException if an I/O error occurs.
*/
+ @Override
public void write(int b) throws IOException {
out.write(b);
}
@@ -92,13 +96,14 @@
* <code>b.length</code>.
* <p>
* Note that this method does not call the one-argument
- * <code>write</code> method of its underlying stream with the single
- * argument <code>b</code>.
+ * <code>write</code> method of its underlying output stream with
+ * the single argument <code>b</code>.
*
* @param b the data to be written.
* @exception IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#write(byte[], int, int)
*/
+ @Override
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
@@ -113,7 +118,7 @@
* <code>byte</code> to output.
* <p>
* Note that this method does not call the <code>write</code> method
- * of its underlying input stream with the same arguments. Subclasses
+ * of its underlying output stream with the same arguments. Subclasses
* of <code>FilterOutputStream</code> should provide a more efficient
* implementation of this method.
*
@@ -123,6 +128,7 @@
* @exception IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#write(int)
*/
+ @Override
public void write(byte b[], int off, int len) throws IOException {
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
throw new IndexOutOfBoundsException();
@@ -142,6 +148,7 @@
* @exception IOException if an I/O error occurs.
* @see java.io.FilterOutputStream#out
*/
+ @Override
public void flush() throws IOException {
out.flush();
}
@@ -158,21 +165,17 @@
* @see java.io.FilterOutputStream#flush()
* @see java.io.FilterOutputStream#out
*/
- // BEGIN Android-changed: Integrate OpenJDK 9 fix for double-close. http://b/122733269.
- /*
- @SuppressWarnings("try")
- public void close() throws IOException {
- try (OutputStream ostream = out) {
- flush();
- }
- }
- */
@Override
public void close() throws IOException {
if (closed) {
return;
}
- closed = true;
+ synchronized (closeLock) {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ }
Throwable flushException = null;
try {
@@ -203,5 +206,4 @@
}
}
}
- // END Android-changed: Integrate OpenJDK 9 fix for double-close. http://b/122733269.
}
diff --git a/ojluni/src/main/java/java/io/FilterReader.java b/ojluni/src/main/java/java/io/FilterReader.java
index 0826230..60f748f 100644
--- a/ojluni/src/main/java/java/io/FilterReader.java
+++ b/ojluni/src/main/java/java/io/FilterReader.java
@@ -35,7 +35,7 @@
* additional methods and fields.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public abstract class FilterReader extends Reader {
@@ -69,6 +69,7 @@
* Reads characters into a portion of an array.
*
* @exception IOException If an I/O error occurs
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public int read(char cbuf[], int off, int len) throws IOException {
return in.read(cbuf, off, len);
diff --git a/ojluni/src/main/java/java/io/FilterWriter.java b/ojluni/src/main/java/java/io/FilterWriter.java
index fb307d5..8272b3c 100644
--- a/ojluni/src/main/java/java/io/FilterWriter.java
+++ b/ojluni/src/main/java/java/io/FilterWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
* provide additional methods and fields.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public abstract class FilterWriter extends Writer {
@@ -72,7 +72,12 @@
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
*
- * @exception IOException If an I/O error occurs
+ * @throws IndexOutOfBoundsException
+ * If the values of the {@code off} and {@code len} parameters
+ * cause the corresponding method of the underlying {@code Writer}
+ * to throw an {@code IndexOutOfBoundsException}
+ *
+ * @throws IOException If an I/O error occurs
*/
public void write(char cbuf[], int off, int len) throws IOException {
out.write(cbuf, off, len);
@@ -85,7 +90,12 @@
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
*
- * @exception IOException If an I/O error occurs
+ * @throws IndexOutOfBoundsException
+ * If the values of the {@code off} and {@code len} parameters
+ * cause the corresponding method of the underlying {@code Writer}
+ * to throw an {@code IndexOutOfBoundsException}
+ *
+ * @throws IOException If an I/O error occurs
*/
public void write(String str, int off, int len) throws IOException {
out.write(str, off, len);
diff --git a/ojluni/src/main/java/java/io/Flushable.java b/ojluni/src/main/java/java/io/Flushable.java
index fe90fbd..9bdc56e 100644
--- a/ojluni/src/main/java/java/io/Flushable.java
+++ b/ojluni/src/main/java/java/io/Flushable.java
@@ -28,7 +28,7 @@
import java.io.IOException;
/**
- * A <tt>Flushable</tt> is a destination of data that can be flushed. The
+ * A {@code Flushable} is a destination of data that can be flushed. The
* flush method is invoked to write any buffered output to the underlying
* stream.
*
diff --git a/ojluni/src/main/java/java/io/IOError.java b/ojluni/src/main/java/java/io/IOError.java
index f9626cd..ea9ec0f 100644
--- a/ojluni/src/main/java/java/io/IOError.java
+++ b/ojluni/src/main/java/java/io/IOError.java
@@ -35,11 +35,11 @@
/**
* Constructs a new instance of IOError with the specified cause. The
* IOError is created with the detail message of
- * <tt>(cause==null ? null : cause.toString())</tt> (which typically
+ * {@code (cause==null ? null : cause.toString())} (which typically
* contains the class and detail message of cause).
*
* @param cause
- * The cause of this error, or <tt>null</tt> if the cause
+ * The cause of this error, or {@code null} if the cause
* is not known
*/
public IOError(Throwable cause) {
diff --git a/ojluni/src/main/java/java/io/IOException.java b/ojluni/src/main/java/java/io/IOException.java
index 745b579..c0d6ea5 100644
--- a/ojluni/src/main/java/java/io/IOException.java
+++ b/ojluni/src/main/java/java/io/IOException.java
@@ -33,7 +33,7 @@
* @author unascribed
* @see java.io.InputStream
* @see java.io.OutputStream
- * @since JDK1.0
+ * @since 1.0
*/
public
class IOException extends Exception {
diff --git a/ojluni/src/main/java/java/io/InputStream.java b/ojluni/src/main/java/java/io/InputStream.java
index 6c46a40..7bccfaf 100644
--- a/ojluni/src/main/java/java/io/InputStream.java
+++ b/ojluni/src/main/java/java/io/InputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,11 @@
package java.io;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
/**
* This abstract class is the superclass of all classes representing
* an input stream of bytes.
@@ -40,7 +45,7 @@
* @see java.io.InputStream#read()
* @see java.io.OutputStream
* @see java.io.PushbackInputStream
- * @since JDK1.0
+ * @since 1.0
*/
public abstract class InputStream implements Closeable {
@@ -48,6 +53,105 @@
// use when skipping.
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ /**
+ * Returns a new {@code InputStream} that reads no bytes. The returned
+ * stream is initially open. The stream is closed by calling the
+ * {@code close()} method. Subsequent calls to {@code close()} have no
+ * effect.
+ *
+ * <p> While the stream is open, the {@code available()}, {@code read()},
+ * {@code read(byte[])}, {@code read(byte[], int, int)},
+ * {@code readAllBytes()}, {@code readNBytes(byte[], int, int)},
+ * {@code readNBytes(int)}, {@code skip(long)}, and
+ * {@code transferTo()} methods all behave as if end of stream has been
+ * reached. After the stream has been closed, these methods all throw
+ * {@code IOException}.
+ *
+ * <p> The {@code markSupported()} method returns {@code false}. The
+ * {@code mark()} method does nothing, and the {@code reset()} method
+ * throws {@code IOException}.
+ *
+ * @return an {@code InputStream} which contains no bytes
+ *
+ * @since 11
+ */
+ public static InputStream nullInputStream() {
+ return new InputStream() {
+ private volatile boolean closed;
+
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ @Override
+ public int available () throws IOException {
+ ensureOpen();
+ return 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ ensureOpen();
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ if (len == 0) {
+ return 0;
+ }
+ ensureOpen();
+ return -1;
+ }
+
+ @Override
+ public byte[] readAllBytes() throws IOException {
+ ensureOpen();
+ return new byte[0];
+ }
+
+ @Override
+ public int readNBytes(byte[] b, int off, int len)
+ throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ ensureOpen();
+ return 0;
+ }
+
+ @Override
+ public byte[] readNBytes(int len) throws IOException {
+ if (len < 0) {
+ throw new IllegalArgumentException("len < 0");
+ }
+ ensureOpen();
+ return new byte[0];
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ ensureOpen();
+ return 0L;
+ }
+
+ @Override
+ public long transferTo(OutputStream out) throws IOException {
+ Objects.requireNonNull(out);
+ ensureOpen();
+ return 0L;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ }
+ };
+ }
+
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
@@ -139,8 +243,8 @@
* <code>b</code> and the number of bytes read before the exception
* occurred is returned. The default implementation of this method blocks
* until the requested amount of input data <code>len</code> has been read,
- * end of file is detected, or an exception is thrown. Subclasses are encouraged
- * to provide a more efficient implementation of this method.
+ * end of file is detected, or an exception is thrown. Subclasses are
+ * encouraged to provide a more efficient implementation of this method.
*
* @param b the buffer into which the data is read.
* @param off the start offset in array <code>b</code>
@@ -159,11 +263,8 @@
* @see java.io.InputStream#read()
*/
public int read(byte b[], int off, int len) throws IOException {
- if (b == null) {
- throw new NullPointerException();
- } else if (off < 0 || len < 0 || len > b.length - off) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
+ Objects.checkFromIndexSize(off, len, b.length);
+ if (len == 0) {
return 0;
}
@@ -188,6 +289,213 @@
}
/**
+ * The maximum size of array to allocate.
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * OutOfMemoryError: Requested array size exceeds VM limit
+ */
+ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * Reads all remaining bytes from the input stream. This method blocks until
+ * all remaining bytes have been read and end of stream is detected, or an
+ * exception is thrown. This method does not close the input stream.
+ *
+ * <p> When this stream reaches end of stream, further invocations of this
+ * method will return an empty byte array.
+ *
+ * <p> Note that this method is intended for simple cases where it is
+ * convenient to read all bytes into a byte array. It is not intended for
+ * reading input streams with large amounts of data.
+ *
+ * <p> The behavior for the case where the input stream is <i>asynchronously
+ * closed</i>, or the thread interrupted during the read, is highly input
+ * stream specific, and therefore not specified.
+ *
+ * <p> If an I/O error occurs reading from the input stream, then it may do
+ * so after some, but not all, bytes have been read. Consequently the input
+ * stream may not be at end of stream and may be in an inconsistent state.
+ * It is strongly recommended that the stream be promptly closed if an I/O
+ * error occurs.
+ *
+ * @implSpec
+ * This method invokes {@link #readNBytes(int)} with a length of
+ * {@link Integer#MAX_VALUE}.
+ *
+ * @return a byte array containing the bytes read from this input stream
+ * @throws IOException if an I/O error occurs
+ * @throws OutOfMemoryError if an array of the required size cannot be
+ * allocated.
+ *
+ * @since 9
+ */
+ public byte[] readAllBytes() throws IOException {
+ return readNBytes(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Reads up to a specified number of bytes from the input stream. This
+ * method blocks until the requested number of bytes have been read, end
+ * of stream is detected, or an exception is thrown. This method does not
+ * close the input stream.
+ *
+ * <p> The length of the returned array equals the number of bytes read
+ * from the stream. If {@code len} is zero, then no bytes are read and
+ * an empty byte array is returned. Otherwise, up to {@code len} bytes
+ * are read from the stream. Fewer than {@code len} bytes may be read if
+ * end of stream is encountered.
+ *
+ * <p> When this stream reaches end of stream, further invocations of this
+ * method will return an empty byte array.
+ *
+ * <p> Note that this method is intended for simple cases where it is
+ * convenient to read the specified number of bytes into a byte array. The
+ * total amount of memory allocated by this method is proportional to the
+ * number of bytes read from the stream which is bounded by {@code len}.
+ * Therefore, the method may be safely called with very large values of
+ * {@code len} provided sufficient memory is available.
+ *
+ * <p> The behavior for the case where the input stream is <i>asynchronously
+ * closed</i>, or the thread interrupted during the read, is highly input
+ * stream specific, and therefore not specified.
+ *
+ * <p> If an I/O error occurs reading from the input stream, then it may do
+ * so after some, but not all, bytes have been read. Consequently the input
+ * stream may not be at end of stream and may be in an inconsistent state.
+ * It is strongly recommended that the stream be promptly closed if an I/O
+ * error occurs.
+ *
+ * @implNote
+ * The number of bytes allocated to read data from this stream and return
+ * the result is bounded by {@code 2*(long)len}, inclusive.
+ *
+ * @param len the maximum number of bytes to read
+ * @return a byte array containing the bytes read from this input stream
+ * @throws IllegalArgumentException if {@code length} is negative
+ * @throws IOException if an I/O error occurs
+ * @throws OutOfMemoryError if an array of the required size cannot be
+ * allocated.
+ *
+ * @since 11
+ */
+ public byte[] readNBytes(int len) throws IOException {
+ if (len < 0) {
+ throw new IllegalArgumentException("len < 0");
+ }
+
+ List<byte[]> bufs = null;
+ byte[] result = null;
+ int total = 0;
+ int remaining = len;
+ int n;
+ do {
+ byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
+ int nread = 0;
+
+ // read to EOF which may read more or less than buffer size
+ while ((n = read(buf, nread,
+ Math.min(buf.length - nread, remaining))) > 0) {
+ nread += n;
+ remaining -= n;
+ }
+
+ if (nread > 0) {
+ if (MAX_BUFFER_SIZE - total < nread) {
+ throw new OutOfMemoryError("Required array size too large");
+ }
+ total += nread;
+ if (result == null) {
+ result = buf;
+ } else {
+ if (bufs == null) {
+ bufs = new ArrayList<>();
+ bufs.add(result);
+ }
+ bufs.add(buf);
+ }
+ }
+ // if the last call to read returned -1 or the number of bytes
+ // requested have been read then break
+ } while (n >= 0 && remaining > 0);
+
+ if (bufs == null) {
+ if (result == null) {
+ return new byte[0];
+ }
+ return result.length == total ?
+ result : Arrays.copyOf(result, total);
+ }
+
+ result = new byte[total];
+ int offset = 0;
+ remaining = total;
+ for (byte[] b : bufs) {
+ int count = Math.min(b.length, remaining);
+ System.arraycopy(b, 0, result, offset, count);
+ offset += count;
+ remaining -= count;
+ }
+
+ return result;
+ }
+
+ /**
+ * Reads the requested number of bytes from the input stream into the given
+ * byte array. This method blocks until {@code len} bytes of input data have
+ * been read, end of stream is detected, or an exception is thrown. The
+ * number of bytes actually read, possibly zero, is returned. This method
+ * does not close the input stream.
+ *
+ * <p> In the case where end of stream is reached before {@code len} bytes
+ * have been read, then the actual number of bytes read will be returned.
+ * When this stream reaches end of stream, further invocations of this
+ * method will return zero.
+ *
+ * <p> If {@code len} is zero, then no bytes are read and {@code 0} is
+ * returned; otherwise, there is an attempt to read up to {@code len} bytes.
+ *
+ * <p> The first byte read is stored into element {@code b[off]}, the next
+ * one in to {@code b[off+1]}, and so on. The number of bytes read is, at
+ * most, equal to {@code len}. Let <i>k</i> be the number of bytes actually
+ * read; these bytes will be stored in elements {@code b[off]} through
+ * {@code b[off+}<i>k</i>{@code -1]}, leaving elements {@code b[off+}<i>k</i>
+ * {@code ]} through {@code b[off+len-1]} unaffected.
+ *
+ * <p> The behavior for the case where the input stream is <i>asynchronously
+ * closed</i>, or the thread interrupted during the read, is highly input
+ * stream specific, and therefore not specified.
+ *
+ * <p> If an I/O error occurs reading from the input stream, then it may do
+ * so after some, but not all, bytes of {@code b} have been updated with
+ * data from the input stream. Consequently the input stream and {@code b}
+ * may be in an inconsistent state. It is strongly recommended that the
+ * stream be promptly closed if an I/O error occurs.
+ *
+ * @param b the byte array into which the data is read
+ * @param off the start offset in {@code b} at which the data is written
+ * @param len the maximum number of bytes to read
+ * @return the actual number of bytes read into the buffer
+ * @throws IOException if an I/O error occurs
+ * @throws NullPointerException if {@code b} is {@code null}
+ * @throws IndexOutOfBoundsException If {@code off} is negative, {@code len}
+ * is negative, or {@code len} is greater than {@code b.length - off}
+ *
+ * @since 9
+ */
+ public int readNBytes(byte[] b, int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+
+ int n = 0;
+ while (n < len) {
+ int count = read(b, off + n, len - n);
+ if (count < 0)
+ break;
+ n += count;
+ }
+ return n;
+ }
+
+ /**
* Skips over and discards <code>n</code> bytes of data from this input
* stream. The <code>skip</code> method may, for a variety of reasons, end
* up skipping over some smaller number of bytes, possibly <code>0</code>.
@@ -198,7 +506,7 @@
* returns 0, and no bytes are skipped. Subclasses may handle the negative
* value differently.
*
- * <p> The <code>skip</code> method of this class creates a
+ * <p> The <code>skip</code> method implementation of this class creates a
* byte array and then repeatedly reads into it until <code>n</code> bytes
* have been read or the end of the stream has been reached. Subclasses are
* encouraged to provide a more efficient implementation of this method.
@@ -206,8 +514,7 @@
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
- * @exception IOException if the stream does not support seek,
- * or if some other I/O error occurs.
+ * @throws IOException if an I/O error occurs.
*/
public long skip(long n) throws IOException {
@@ -232,29 +539,29 @@
}
/**
- * Returns an estimate of the number of bytes that can be read (or
- * skipped over) from this input stream without blocking by the next
- * invocation of a method for this input stream. The next invocation
- * might be the same thread or another thread. A single read or skip of this
- * many bytes will not block, but may read or skip fewer bytes.
+ * Returns an estimate of the number of bytes that can be read (or skipped
+ * over) from this input stream without blocking, which may be 0, or 0 when
+ * end of stream is detected. The read might be on the same thread or
+ * another thread. A single read or skip of this many bytes will not block,
+ * but may read or skip fewer bytes.
*
- * <p> Note that while some implementations of {@code InputStream} will return
- * the total number of bytes in the stream, many will not. It is
+ * <p> Note that while some implementations of {@code InputStream} will
+ * return the total number of bytes in the stream, many will not. It is
* never correct to use the return value of this method to allocate
* a buffer intended to hold all data in this stream.
*
- * <p> A subclass' implementation of this method may choose to throw an
- * {@link IOException} if this input stream has been closed by
- * invoking the {@link #close()} method.
+ * <p> A subclass's implementation of this method may choose to throw an
+ * {@link IOException} if this input stream has been closed by invoking the
+ * {@link #close()} method.
*
- * <p> The {@code available} method for class {@code InputStream} always
- * returns {@code 0}.
+ * <p> The {@code available} method of {@code InputStream} always returns
+ * {@code 0}.
*
* <p> This method should be overridden by subclasses.
*
- * @return an estimate of the number of bytes that can be read (or skipped
- * over) from this input stream without blocking or {@code 0} when
- * it reaches the end of the input stream.
+ * @return an estimate of the number of bytes that can be read (or
+ * skipped over) from this input stream without blocking or
+ * {@code 0} when it reaches the end of the input stream.
* @exception IOException if an I/O error occurs.
*/
public int available() throws IOException {
@@ -364,4 +671,40 @@
return false;
}
+ /**
+ * Reads all bytes from this input stream and writes the bytes to the
+ * given output stream in the order that they are read. On return, this
+ * input stream will be at end of stream. This method does not close either
+ * stream.
+ * <p>
+ * This method may block indefinitely reading from the input stream, or
+ * writing to the output stream. The behavior for the case where the input
+ * and/or output stream is <i>asynchronously closed</i>, or the thread
+ * interrupted during the transfer, is highly input and output stream
+ * specific, and therefore not specified.
+ * <p>
+ * If an I/O error occurs reading from the input stream or writing to the
+ * output stream, then it may do so after some bytes have been read or
+ * written. Consequently the input stream may not be at end of stream and
+ * one, or both, streams may be in an inconsistent state. It is strongly
+ * recommended that both streams be promptly closed if an I/O error occurs.
+ *
+ * @param out the output stream, non-null
+ * @return the number of bytes transferred
+ * @throws IOException if an I/O error occurs when reading or writing
+ * @throws NullPointerException if {@code out} is {@code null}
+ *
+ * @since 9
+ */
+ public long transferTo(OutputStream out) throws IOException {
+ Objects.requireNonNull(out, "out");
+ long transferred = 0;
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int read;
+ while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
+ out.write(buffer, 0, read);
+ transferred += read;
+ }
+ return transferred;
+ }
}
diff --git a/ojluni/src/main/java/java/io/InputStreamReader.java b/ojluni/src/main/java/java/io/InputStreamReader.java
index e131dca..564537e 100644
--- a/ojluni/src/main/java/java/io/InputStreamReader.java
+++ b/ojluni/src/main/java/java/io/InputStreamReader.java
@@ -56,7 +56,7 @@
* @see java.nio.charset.Charset
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class InputStreamReader extends Reader {
diff --git a/ojluni/src/main/java/java/io/InvalidClassException.java b/ojluni/src/main/java/java/io/InvalidClassException.java
index 77f0a5a..3d78c6b 100644
--- a/ojluni/src/main/java/java/io/InvalidClassException.java
+++ b/ojluni/src/main/java/java/io/InvalidClassException.java
@@ -36,7 +36,7 @@
* </UL>
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class InvalidClassException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/InvalidObjectException.java b/ojluni/src/main/java/java/io/InvalidObjectException.java
index fbe1108..5853911 100644
--- a/ojluni/src/main/java/java/io/InvalidObjectException.java
+++ b/ojluni/src/main/java/java/io/InvalidObjectException.java
@@ -30,10 +30,10 @@
* tests. The argument should provide the reason for the failure.
*
* @see ObjectInputValidation
- * @since JDK1.1
+ * @since 1.1
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class InvalidObjectException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/LineNumberInputStream.java b/ojluni/src/main/java/java/io/LineNumberInputStream.java
index 1f37a98..2235a2b 100644
--- a/ojluni/src/main/java/java/io/LineNumberInputStream.java
+++ b/ojluni/src/main/java/java/io/LineNumberInputStream.java
@@ -40,7 +40,7 @@
*
* @author Arthur van Hoff
* @see java.io.LineNumberReader
- * @since JDK1.0
+ * @since 1.0
* @deprecated This class incorrectly assumes that bytes adequately represent
* characters. As of JDK 1.1, the preferred way to operate on
* character streams is via the new character-stream classes, which
diff --git a/ojluni/src/main/java/java/io/LineNumberReader.java b/ojluni/src/main/java/java/io/LineNumberReader.java
index 29884fd..c9ecbe0 100644
--- a/ojluni/src/main/java/java/io/LineNumberReader.java
+++ b/ojluni/src/main/java/java/io/LineNumberReader.java
@@ -34,17 +34,17 @@
*
* <p> By default, line numbering begins at 0. This number increments at every
* <a href="#lt">line terminator</a> as the data is read, and can be changed
- * with a call to <tt>setLineNumber(int)</tt>. Note however, that
- * <tt>setLineNumber(int)</tt> does not actually change the current position in
+ * with a call to {@code setLineNumber(int)}. Note however, that
+ * {@code setLineNumber(int)} does not actually change the current position in
* the stream; it only changes the value that will be returned by
- * <tt>getLineNumber()</tt>.
+ * {@code getLineNumber()}.
*
- * <p> A line is considered to be <a name="lt">terminated</a> by any one of a
+ * <p> A line is considered to be <a id="lt">terminated</a> by any one of a
* line feed ('\n'), a carriage return ('\r'), or a carriage return followed
* immediately by a linefeed.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class LineNumberReader extends BufferedReader {
@@ -159,6 +159,8 @@
*
* @throws IOException
* If an I/O error occurs
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
*/
@SuppressWarnings("fallthrough")
public int read(char cbuf[], int off, int len) throws IOException {
@@ -191,7 +193,7 @@
*
* @return A String containing the contents of the line, not including
* any <a href="#lt">line termination characters</a>, or
- * <tt>null</tt> if the end of the stream has been reached
+ * {@code null} if the end of the stream has been reached
*
* @throws IOException
* If an I/O error occurs
@@ -224,7 +226,7 @@
* If an I/O error occurs
*
* @throws IllegalArgumentException
- * If <tt>n</tt> is negative
+ * If {@code n} is negative
*/
public long skip(long n) throws IOException {
if (n < 0)
diff --git a/ojluni/src/main/java/java/io/NotActiveException.java b/ojluni/src/main/java/java/io/NotActiveException.java
index 13291e6..231598c 100644
--- a/ojluni/src/main/java/java/io/NotActiveException.java
+++ b/ojluni/src/main/java/java/io/NotActiveException.java
@@ -29,7 +29,7 @@
* Thrown when serialization or deserialization is not active.
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class NotActiveException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/NotSerializableException.java b/ojluni/src/main/java/java/io/NotSerializableException.java
index 70e4f57..43dbc1a 100644
--- a/ojluni/src/main/java/java/io/NotSerializableException.java
+++ b/ojluni/src/main/java/java/io/NotSerializableException.java
@@ -31,7 +31,7 @@
* this exception. The argument should be the name of the class.
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class NotSerializableException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/ObjectInput.java b/ojluni/src/main/java/java/io/ObjectInput.java
index bc7c26b..92d3b70 100644
--- a/ojluni/src/main/java/java/io/ObjectInput.java
+++ b/ojluni/src/main/java/java/io/ObjectInput.java
@@ -34,7 +34,7 @@
* @see java.io.InputStream
* @see java.io.ObjectOutputStream
* @see java.io.ObjectInputStream
- * @since JDK1.1
+ * @since 1.1
*/
public interface ObjectInput extends DataInput, AutoCloseable {
/**
diff --git a/ojluni/src/main/java/java/io/ObjectInputValidation.java b/ojluni/src/main/java/java/io/ObjectInputValidation.java
index dc6f842..09ee8a6 100644
--- a/ojluni/src/main/java/java/io/ObjectInputValidation.java
+++ b/ojluni/src/main/java/java/io/ObjectInputValidation.java
@@ -33,7 +33,7 @@
* @author unascribed
* @see ObjectInputStream
* @see ObjectInputStream#registerValidation(java.io.ObjectInputValidation, int)
- * @since JDK1.1
+ * @since 1.1
*/
public interface ObjectInputValidation {
/**
diff --git a/ojluni/src/main/java/java/io/ObjectOutput.java b/ojluni/src/main/java/java/io/ObjectOutput.java
index 7c2cbeb..720d198 100644
--- a/ojluni/src/main/java/java/io/ObjectOutput.java
+++ b/ojluni/src/main/java/java/io/ObjectOutput.java
@@ -34,7 +34,7 @@
* @see java.io.InputStream
* @see java.io.ObjectOutputStream
* @see java.io.ObjectInputStream
- * @since JDK1.1
+ * @since 1.1
*/
public interface ObjectOutput extends DataOutput, AutoCloseable {
/**
diff --git a/ojluni/src/main/java/java/io/ObjectStreamException.java b/ojluni/src/main/java/java/io/ObjectStreamException.java
index 889cad3..94fc560 100644
--- a/ojluni/src/main/java/java/io/ObjectStreamException.java
+++ b/ojluni/src/main/java/java/io/ObjectStreamException.java
@@ -29,7 +29,7 @@
* Superclass of all exceptions specific to Object Stream classes.
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public abstract class ObjectStreamException extends IOException {
@@ -38,10 +38,10 @@
/**
* Create an ObjectStreamException with the specified argument.
*
- * @param classname the detailed message for the exception
+ * @param message the detailed message for the exception
*/
- protected ObjectStreamException(String classname) {
- super(classname);
+ protected ObjectStreamException(String message) {
+ super(message);
}
/**
diff --git a/ojluni/src/main/java/java/io/OptionalDataException.java b/ojluni/src/main/java/java/io/OptionalDataException.java
index 91283fd..f51e0ac 100644
--- a/ojluni/src/main/java/java/io/OptionalDataException.java
+++ b/ojluni/src/main/java/java/io/OptionalDataException.java
@@ -43,7 +43,7 @@
* </ul>
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class OptionalDataException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/OutputStream.java b/ojluni/src/main/java/java/io/OutputStream.java
index c6dd7de..556e7e0 100644
--- a/ojluni/src/main/java/java/io/OutputStream.java
+++ b/ojluni/src/main/java/java/io/OutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package java.io;
+import java.util.Objects;
+
/**
* This abstract class is the superclass of all classes representing
* an output stream of bytes. An output stream accepts output bytes
@@ -41,10 +43,55 @@
* @see java.io.FilterOutputStream
* @see java.io.InputStream
* @see java.io.OutputStream#write(int)
- * @since JDK1.0
+ * @since 1.0
*/
public abstract class OutputStream implements Closeable, Flushable {
/**
+ * Returns a new {@code OutputStream} which discards all bytes. The
+ * returned stream is initially open. The stream is closed by calling
+ * the {@code close()} method. Subsequent calls to {@code close()} have
+ * no effect.
+ *
+ * <p> While the stream is open, the {@code write(int)}, {@code
+ * write(byte[])}, and {@code write(byte[], int, int)} methods do nothing.
+ * After the stream has been closed, these methods all throw {@code
+ * IOException}.
+ *
+ * <p> The {@code flush()} method does nothing.
+ *
+ * @return an {@code OutputStream} which discards all bytes
+ *
+ * @since 11
+ */
+ public static OutputStream nullOutputStream() {
+ return new OutputStream() {
+ private volatile boolean closed;
+
+ private void ensureOpen() throws IOException {
+ if (closed) {
+ throw new IOException("Stream closed");
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ ensureOpen();
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ Objects.checkFromIndexSize(off, len, b.length);
+ ensureOpen();
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ };
+ }
+
+ /**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
@@ -94,7 +141,7 @@
* <p>
* If <code>off</code> is negative, or <code>len</code> is negative, or
* <code>off+len</code> is greater than the length of the array
- * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
+ * {@code b}, then an {@code IndexOutOfBoundsException} is thrown.
*
* @param b the data.
* @param off the start offset in the data.
@@ -104,14 +151,8 @@
* stream is closed.
*/
public void write(byte b[], int off, int len) throws IOException {
- if (b == null) {
- throw new NullPointerException();
- } else if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) > b.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
- return;
- }
+ Objects.checkFromIndexSize(off, len, b.length);
+ // len == 0 condition implicitly handled by loop bounds
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
diff --git a/ojluni/src/main/java/java/io/PipedInputStream.java b/ojluni/src/main/java/java/io/PipedInputStream.java
index b79bcbd..b1be6b4 100644
--- a/ojluni/src/main/java/java/io/PipedInputStream.java
+++ b/ojluni/src/main/java/java/io/PipedInputStream.java
@@ -42,18 +42,18 @@
* The piped input stream contains a buffer,
* decoupling read operations from write operations,
* within limits.
- * A pipe is said to be <a name="BROKEN"> <i>broken</i> </a> if a
+ * A pipe is said to be <a id="BROKEN"> <i>broken</i> </a> if a
* thread that was providing data bytes to the connected
* piped output stream is no longer alive.
*
* @author James Gosling
* @see java.io.PipedOutputStream
- * @since JDK1.0
+ * @since 1.0
*/
public class PipedInputStream extends InputStream {
- boolean closedByWriter = false;
- volatile boolean closedByReader = false;
- boolean connected = false;
+ boolean closedByWriter;
+ volatile boolean closedByReader;
+ boolean connected;
/* REMIND: identification of the read and write sides needs to be
more sophisticated. Either using thread groups (but what about
@@ -66,7 +66,7 @@
/**
* The default size of the pipe's circular input buffer.
- * @since JDK1.1
+ * @since 1.1
*/
// This used to be a constant before the pipe size was allowed
// to change. This field will continue to be maintained
@@ -75,7 +75,7 @@
/**
* The circular buffer into which incoming data is placed.
- * @since JDK1.1
+ * @since 1.1
*/
protected byte buffer[];
@@ -84,14 +84,14 @@
* next byte of data will be stored when received from the connected
* piped output stream. <code>in<0</code> implies the buffer is empty,
* <code>in==out</code> implies the buffer is full
- * @since JDK1.1
+ * @since 1.1
*/
protected int in = -1;
/**
* The index of the position in the circular buffer at which the next
* byte of data will be read by this piped input stream.
- * @since JDK1.1
+ * @since 1.1
*/
protected int out = 0;
@@ -198,7 +198,7 @@
* @exception IOException If the pipe is <a href="#BROKEN"> <code>broken</code></a>,
* {@link #connect(java.io.PipedOutputStream) unconnected},
* closed, or if an I/O error occurs.
- * @since JDK1.1
+ * @since 1.1
*/
protected synchronized void receive(int b) throws IOException {
checkStateForReceive();
@@ -428,7 +428,7 @@
* <a href="#BROKEN"> <code>broken</code></a>.
*
* @exception IOException if an I/O error occurs.
- * @since JDK1.0.2
+ * @since 1.0.2
*/
public synchronized int available() throws IOException {
if(in < 0)
diff --git a/ojluni/src/main/java/java/io/PipedOutputStream.java b/ojluni/src/main/java/java/io/PipedOutputStream.java
index feed73a..79ffd38 100644
--- a/ojluni/src/main/java/java/io/PipedOutputStream.java
+++ b/ojluni/src/main/java/java/io/PipedOutputStream.java
@@ -35,13 +35,13 @@
* read from the connected <code>PipedInputStream</code> by some
* other thread. Attempting to use both objects from a single thread
* is not recommended as it may deadlock the thread.
- * The pipe is said to be <a name=BROKEN> <i>broken</i> </a> if a
+ * The pipe is said to be <a id=BROKEN> <i>broken</i> </a> if a
* thread that was reading data bytes from the connected piped input
* stream is no longer alive.
*
* @author James Gosling
* @see java.io.PipedInputStream
- * @since JDK1.0
+ * @since 1.0
*/
public
class PipedOutputStream extends OutputStream {
diff --git a/ojluni/src/main/java/java/io/PipedReader.java b/ojluni/src/main/java/java/io/PipedReader.java
index d72e1e5..fe6b0ea 100644
--- a/ojluni/src/main/java/java/io/PipedReader.java
+++ b/ojluni/src/main/java/java/io/PipedReader.java
@@ -32,7 +32,7 @@
* Piped character-input streams.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class PipedReader extends Reader {
@@ -292,6 +292,7 @@
* <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
* {@link #connect(java.io.PipedWriter) unconnected}, closed,
* or an I/O error occurs.
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public synchronized int read(char cbuf[], int off, int len) throws IOException {
if (!connected) {
diff --git a/ojluni/src/main/java/java/io/PipedWriter.java b/ojluni/src/main/java/java/io/PipedWriter.java
index 46e1455..a4534b8 100644
--- a/ojluni/src/main/java/java/io/PipedWriter.java
+++ b/ojluni/src/main/java/java/io/PipedWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,7 @@
* Piped character-output streams.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class PipedWriter extends Writer {
@@ -125,19 +125,25 @@
}
/**
- * Writes <code>len</code> characters from the specified character array
- * starting at offset <code>off</code> to this piped output stream.
+ * Writes {@code len} characters from the specified character array
+ * starting at offset {@code off} to this piped output stream.
* This method blocks until all the characters are written to the output
* stream.
* If a thread was reading data characters from the connected piped input
* stream, but the thread is no longer alive, then an
- * <code>IOException</code> is thrown.
+ * {@code IOException} is thrown.
*
* @param cbuf the data.
* @param off the start offset in the data.
* @param len the number of characters to write.
- * @exception IOException if the pipe is
- * <a href=PipedOutputStream.html#BROKEN> <code>broken</code></a>,
+ *
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given array
+ *
+ * @throws IOException if the pipe is
+ * <a href=PipedOutputStream.html#BROKEN><code>broken</code></a>,
* {@link #connect(java.io.PipedReader) unconnected}, closed
* or an I/O error occurs.
*/
diff --git a/ojluni/src/main/java/java/io/PushbackInputStream.java b/ojluni/src/main/java/java/io/PushbackInputStream.java
index b44848d..d9905c8 100644
--- a/ojluni/src/main/java/java/io/PushbackInputStream.java
+++ b/ojluni/src/main/java/java/io/PushbackInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,9 +28,10 @@
/**
* A <code>PushbackInputStream</code> adds
* functionality to another input stream, namely
- * the ability to "push back" or "unread"
- * one byte. This is useful in situations where
- * it is convenient for a fragment of code
+ * the ability to "push back" or "unread" bytes,
+ * by storing pushed-back bytes in an internal buffer.
+ * This is useful in situations where
+ * it is convenient for a fragment of code
* to read an indefinite number of data bytes
* that are delimited by a particular byte
* value; after reading the terminating byte,
@@ -46,13 +47,13 @@
*
* @author David Connelly
* @author Jonathan Payne
- * @since JDK1.0
+ * @since 1.0
*/
public
class PushbackInputStream extends FilterInputStream {
/**
* The pushback buffer.
- * @since JDK1.1
+ * @since 1.1
*/
protected byte[] buf;
@@ -62,7 +63,7 @@
* <code>buf.length</code>; when the buffer is full, <code>pos</code> is
* equal to zero.
*
- * @since JDK1.1
+ * @since 1.1
*/
protected int pos;
@@ -77,16 +78,14 @@
/**
* Creates a <code>PushbackInputStream</code>
* with a pushback buffer of the specified <code>size</code>,
- * and saves its argument, the input stream
+ * and saves its argument, the input stream
* <code>in</code>, for later use. Initially,
- * there is no pushed-back byte (the field
- * <code>pushBack</code> is initialized to
- * <code>-1</code>).
+ * the pushback buffer is empty.
*
* @param in the input stream from which bytes will be read.
* @param size the size of the pushback buffer.
* @exception IllegalArgumentException if {@code size <= 0}
- * @since JDK1.1
+ * @since 1.1
*/
public PushbackInputStream(InputStream in, int size) {
super(in);
@@ -99,11 +98,9 @@
/**
* Creates a <code>PushbackInputStream</code>
- * and saves its argument, the input stream
+ * with a 1-byte pushback buffer, and saves its argument, the input stream
* <code>in</code>, for later use. Initially,
- * there is no pushed-back byte (the field
- * <code>pushBack</code> is initialized to
- * <code>-1</code>).
+ * the pushback buffer is empty.
*
* @param in the input stream from which bytes will be read.
*/
@@ -224,7 +221,7 @@
* buffer for the specified number of bytes,
* or this input stream has been closed by
* invoking its {@link #close()} method.
- * @since JDK1.1
+ * @since 1.1
*/
public void unread(byte[] b, int off, int len) throws IOException {
ensureOpen();
@@ -246,7 +243,7 @@
* buffer for the specified number of bytes,
* or this input stream has been closed by
* invoking its {@link #close()} method.
- * @since JDK1.1
+ * @since 1.1
*/
public void unread(byte[] b) throws IOException {
unread(b, 0, b.length);
@@ -294,10 +291,10 @@
*
* @param n {@inheritDoc}
* @return {@inheritDoc}
- * @exception IOException if the stream does not support seek,
- * or the stream has been closed by
- * invoking its {@link #close()} method,
- * or an I/O error occurs.
+ * @throws IOException if the stream has been closed by
+ * invoking its {@link #close()} method,
+ * {@code in.skip(n)} throws an IOException,
+ * or an I/O error occurs.
* @see java.io.FilterInputStream#in
* @see java.io.InputStream#skip(long n)
* @since 1.2
diff --git a/ojluni/src/main/java/java/io/PushbackReader.java b/ojluni/src/main/java/java/io/PushbackReader.java
index f918621..85b1854 100644
--- a/ojluni/src/main/java/java/io/PushbackReader.java
+++ b/ojluni/src/main/java/java/io/PushbackReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
* stream.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class PushbackReader extends FilterReader {
@@ -102,6 +102,7 @@
* stream has been reached
*
* @exception IOException If an I/O error occurs
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
@@ -240,13 +241,16 @@
* Closes the stream and releases any system resources associated with
* it. Once the stream has been closed, further read(),
* unread(), ready(), or skip() invocations will throw an IOException.
- * Closing a previously closed stream has no effect.
+ * Closing a previously closed stream has no effect. This method will block
+ * while there is another thread blocking on the reader.
*
* @exception IOException If an I/O error occurs
*/
public void close() throws IOException {
- super.close();
- buf = null;
+ synchronized (lock) {
+ super.close();
+ buf = null;
+ }
}
/**
diff --git a/ojluni/src/main/java/java/io/Serializable.java b/ojluni/src/main/java/java/io/Serializable.java
index 496aef7..eb245cc 100644
--- a/ojluni/src/main/java/java/io/Serializable.java
+++ b/ojluni/src/main/java/java/io/Serializable.java
@@ -26,9 +26,16 @@
package java.io;
// Android-added: Notes about serialVersionUID, using serialization judiciously, JSON.
+// Android-removed: External links to serialization guidelinenes.
/**
* Serializability of a class is enabled by the class implementing the
- * java.io.Serializable interface. Classes that do not implement this
+ * java.io.Serializable interface.
+ *
+ * <p><strong>Warning: Deserialization of untrusted data is inherently dangerous
+ * and should be avoided. Untrusted data should be carefully validated.
+ * </strong></p>
+ *
+ * Classes that do not implement this
* interface will not have any of their state serialized or
* deserialized. All subtypes of a serializable class are themselves
* serializable. The serialization interface has no methods or fields
@@ -85,9 +92,9 @@
* correspondingly named fields in the current object. This handles the case
* when the class has evolved to add new fields. The method does not need to
* concern itself with the state belonging to its superclasses or subclasses.
- * State is saved by writing the individual fields to the
- * ObjectOutputStream using the writeObject method or by using the
- * methods for primitive data types supported by DataOutput.
+ * State is restored by reading data from the ObjectInputStream for
+ * the individual fields and making assignments to the appropriate fields
+ * of the object. Reading primitive data types is supported by DataInput.
*
* <p>The readObjectNoData method is responsible for initializing the state of
* the object for its particular class in the event that the serialization
@@ -183,7 +190,7 @@
* @see java.io.ObjectOutput
* @see java.io.ObjectInput
* @see java.io.Externalizable
- * @since JDK1.1
+ * @since 1.1
*/
public interface Serializable {
}
diff --git a/ojluni/src/main/java/java/io/StreamCorruptedException.java b/ojluni/src/main/java/java/io/StreamCorruptedException.java
index 57af82c..22e2216 100644
--- a/ojluni/src/main/java/java/io/StreamCorruptedException.java
+++ b/ojluni/src/main/java/java/io/StreamCorruptedException.java
@@ -30,7 +30,7 @@
* violates internal consistency checks.
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class StreamCorruptedException extends ObjectStreamException {
diff --git a/ojluni/src/main/java/java/io/StreamTokenizer.java b/ojluni/src/main/java/java/io/StreamTokenizer.java
index 3c7c7cc..9de1405 100644
--- a/ojluni/src/main/java/java/io/StreamTokenizer.java
+++ b/ojluni/src/main/java/java/io/StreamTokenizer.java
@@ -59,7 +59,7 @@
* @author James Gosling
* @see java.io.StreamTokenizer#nextToken()
* @see java.io.StreamTokenizer#TT_EOF
- * @since JDK1.0
+ * @since 1.0
*/
public class StreamTokenizer {
@@ -240,7 +240,7 @@
* Create a tokenizer that parses the given character stream.
*
* @param r a Reader object providing the input stream.
- * @since JDK1.1
+ * @since 1.1
*/
public StreamTokenizer(Reader r) {
this();
diff --git a/ojluni/src/main/java/java/io/StringBufferInputStream.java b/ojluni/src/main/java/java/io/StringBufferInputStream.java
index 90c5d4d..f54b725 100644
--- a/ojluni/src/main/java/java/io/StringBufferInputStream.java
+++ b/ojluni/src/main/java/java/io/StringBufferInputStream.java
@@ -37,7 +37,7 @@
* @author Arthur van Hoff
* @see java.io.ByteArrayInputStream
* @see java.io.StringReader
- * @since JDK1.0
+ * @since 1.0
* @deprecated This class does not properly convert characters into bytes. As
* of JDK 1.1, the preferred way to create a stream from a
* string is via the <code>StringReader</code> class.
@@ -108,6 +108,7 @@
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
*/
+ @SuppressWarnings("deprecation")
public synchronized int read(byte b[], int off, int len) {
if (b == null) {
throw new NullPointerException();
@@ -127,12 +128,8 @@
if (len <= 0) {
return 0;
}
- String s = buffer;
- int cnt = len;
- while (--cnt >= 0) {
- b[off++] = (byte)s.charAt(pos++);
- }
-
+ buffer.getBytes(pos, pos + len, b, off);
+ pos += len;
return len;
}
diff --git a/ojluni/src/main/java/java/io/StringReader.java b/ojluni/src/main/java/java/io/StringReader.java
index ce9ff60..9cfe541 100644
--- a/ojluni/src/main/java/java/io/StringReader.java
+++ b/ojluni/src/main/java/java/io/StringReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,7 @@
* A character stream whose source is a string.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class StringReader extends Reader {
@@ -84,6 +84,7 @@
* stream has been reached
*
* @exception IOException If an I/O error occurs
+ * @exception IndexOutOfBoundsException {@inheritDoc}
*/
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
@@ -141,8 +142,8 @@
*/
public boolean ready() throws IOException {
synchronized (lock) {
- ensureOpen();
- return true;
+ ensureOpen();
+ return true;
}
}
@@ -193,9 +194,12 @@
* Closes the stream and releases any system resources associated with
* it. Once the stream has been closed, further read(),
* ready(), mark(), or reset() invocations will throw an IOException.
- * Closing a previously closed stream has no effect.
+ * Closing a previously closed stream has no effect. This method will block
+ * while there is another thread blocking on the reader.
*/
public void close() {
- str = null;
+ synchronized (lock) {
+ str = null;
+ }
}
}
diff --git a/ojluni/src/main/java/java/io/StringWriter.java b/ojluni/src/main/java/java/io/StringWriter.java
index c4e5d40..16eed62 100644
--- a/ojluni/src/main/java/java/io/StringWriter.java
+++ b/ojluni/src/main/java/java/io/StringWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,12 +30,12 @@
* A character stream that collects its output in a string buffer, which can
* then be used to construct a string.
* <p>
- * Closing a <tt>StringWriter</tt> has no effect. The methods in this class
+ * Closing a {@code StringWriter} has no effect. The methods in this class
* can be called after the stream has been closed without generating an
- * <tt>IOException</tt>.
+ * {@code IOException}.
*
* @author Mark Reinhold
- * @since JDK1.1
+ * @since 1.1
*/
public class StringWriter extends Writer {
@@ -56,11 +56,11 @@
* size.
*
* @param initialSize
- * The number of <tt>char</tt> values that will fit into this buffer
+ * The number of {@code char} values that will fit into this buffer
* before it is automatically expanded
*
* @throws IllegalArgumentException
- * If <tt>initialSize</tt> is negative
+ * If {@code initialSize} is negative
*/
public StringWriter(int initialSize) {
if (initialSize < 0) {
@@ -83,6 +83,11 @@
* @param cbuf Array of characters
* @param off Offset from which to start writing characters
* @param len Number of characters to write
+ *
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given array
*/
public void write(char cbuf[], int off, int len) {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
@@ -107,29 +112,34 @@
* @param str String to be written
* @param off Offset from which to start writing characters
* @param len Number of characters to write
+ *
+ * @throws IndexOutOfBoundsException
+ * If {@code off} is negative, or {@code len} is negative,
+ * or {@code off + len} is negative or greater than the length
+ * of the given string
*/
public void write(String str, int off, int len) {
- buf.append(str.substring(off, off + len));
+ buf.append(str, off, off + len);
}
/**
* Appends the specified character sequence to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(csq)</tt>
+ * <p> An invocation of this method of the form {@code out.append(csq)}
* behaves in exactly the same way as the invocation
*
* <pre>
* out.write(csq.toString()) </pre>
*
- * <p> Depending on the specification of <tt>toString</tt> for the
- * character sequence <tt>csq</tt>, the entire sequence may not be
- * appended. For instance, invoking the <tt>toString</tt> method of a
+ * <p> Depending on the specification of {@code toString} for the
+ * character sequence {@code csq}, the entire sequence may not be
+ * appended. For instance, invoking the {@code toString} method of a
* character buffer will return a subsequence whose content depends upon
* the buffer's position and limit.
*
* @param csq
- * The character sequence to append. If <tt>csq</tt> is
- * <tt>null</tt>, then the four characters <tt>"null"</tt> are
+ * The character sequence to append. If {@code csq} is
+ * {@code null}, then the four characters {@code "null"} are
* appended to this writer.
*
* @return This writer
@@ -137,28 +147,27 @@
* @since 1.5
*/
public StringWriter append(CharSequence csq) {
- if (csq == null)
- write("null");
- else
- write(csq.toString());
+ write(String.valueOf(csq));
return this;
}
/**
* Appends a subsequence of the specified character sequence to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(csq, start,
- * end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
+ * <p> An invocation of this method of the form
+ * {@code out.append(csq, start, end)} when {@code csq}
+ * is not {@code null}, behaves in
* exactly the same way as the invocation
*
- * <pre>
- * out.write(csq.subSequence(start, end).toString()) </pre>
+ * <pre>{@code
+ * out.write(csq.subSequence(start, end).toString())
+ * }</pre>
*
* @param csq
* The character sequence from which a subsequence will be
- * appended. If <tt>csq</tt> is <tt>null</tt>, then characters
- * will be appended as if <tt>csq</tt> contained the four
- * characters <tt>"null"</tt>.
+ * appended. If {@code csq} is {@code null}, then characters
+ * will be appended as if {@code csq} contained the four
+ * characters {@code "null"}.
*
* @param start
* The index of the first character in the subsequence
@@ -170,22 +179,21 @@
* @return This writer
*
* @throws IndexOutOfBoundsException
- * If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
- * is greater than <tt>end</tt>, or <tt>end</tt> is greater than
- * <tt>csq.length()</tt>
+ * If {@code start} or {@code end} are negative, {@code start}
+ * is greater than {@code end}, or {@code end} is greater than
+ * {@code csq.length()}
*
* @since 1.5
*/
public StringWriter append(CharSequence csq, int start, int end) {
- CharSequence cs = (csq == null ? "null" : csq);
- write(cs.subSequence(start, end).toString());
- return this;
+ if (csq == null) csq = "null";
+ return append(csq.subSequence(start, end));
}
/**
* Appends the specified character to this writer.
*
- * <p> An invocation of this method of the form <tt>out.append(c)</tt>
+ * <p> An invocation of this method of the form {@code out.append(c)}
* behaves in exactly the same way as the invocation
*
* <pre>
@@ -226,9 +234,9 @@
}
/**
- * Closing a <tt>StringWriter</tt> has no effect. The methods in this
+ * Closing a {@code StringWriter} has no effect. The methods in this
* class can be called after the stream has been closed without generating
- * an <tt>IOException</tt>.
+ * an {@code IOException}.
*/
public void close() throws IOException {
}
diff --git a/ojluni/src/main/java/java/io/SyncFailedException.java b/ojluni/src/main/java/java/io/SyncFailedException.java
index 7e553a7..dc1228ad 100644
--- a/ojluni/src/main/java/java/io/SyncFailedException.java
+++ b/ojluni/src/main/java/java/io/SyncFailedException.java
@@ -31,7 +31,7 @@
* @author Ken Arnold
* @see java.io.FileDescriptor#sync
* @see java.io.IOException
- * @since JDK1.1
+ * @since 1.1
*/
public class SyncFailedException extends IOException {
private static final long serialVersionUID = -2353342684412443330L;
diff --git a/ojluni/src/main/java/java/io/UTFDataFormatException.java b/ojluni/src/main/java/java/io/UTFDataFormatException.java
index 422d28b..88a1cf7 100644
--- a/ojluni/src/main/java/java/io/UTFDataFormatException.java
+++ b/ojluni/src/main/java/java/io/UTFDataFormatException.java
@@ -40,7 +40,7 @@
* @see java.io.DataInput
* @see java.io.DataInputStream#readUTF(java.io.DataInput)
* @see java.io.IOException
- * @since JDK1.0
+ * @since 1.0
*/
public
class UTFDataFormatException extends IOException {
diff --git a/ojluni/src/main/java/java/io/UnsupportedEncodingException.java b/ojluni/src/main/java/java/io/UnsupportedEncodingException.java
index b59f9c3..48b2224 100644
--- a/ojluni/src/main/java/java/io/UnsupportedEncodingException.java
+++ b/ojluni/src/main/java/java/io/UnsupportedEncodingException.java
@@ -28,7 +28,7 @@
* The Character Encoding is not supported.
*
* @author Asmus Freytag
- * @since JDK1.1
+ * @since 1.1
*/
public class UnsupportedEncodingException
extends IOException
diff --git a/ojluni/src/main/java/java/io/WriteAbortedException.java b/ojluni/src/main/java/java/io/WriteAbortedException.java
index c39950d..202575c 100644
--- a/ojluni/src/main/java/java/io/WriteAbortedException.java
+++ b/ojluni/src/main/java/java/io/WriteAbortedException.java
@@ -41,7 +41,7 @@
* method, as well as the aforementioned "legacy field."
*
* @author unascribed
- * @since JDK1.1
+ * @since 1.1
*/
public class WriteAbortedException extends ObjectStreamException {
private static final long serialVersionUID = -3326426625597282442L;
diff --git a/ojluni/src/main/java/java/util/BitSet.java b/ojluni/src/main/java/java/util/BitSet.java
index 261a77c..c72970d 100644
--- a/ojluni/src/main/java/java/util/BitSet.java
+++ b/ojluni/src/main/java/java/util/BitSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.LongBuffer;
+import java.util.function.IntConsumer;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
@@ -60,7 +61,7 @@
* @author Arthur van Hoff
* @author Michael McCloskey
* @author Martin Buchholz
- * @since JDK1.0
+ * @since 1.0
*/
public class BitSet implements Cloneable, java.io.Serializable {
/*
@@ -68,9 +69,9 @@
* a long, which consists of 64 bits, requiring 6 address bits.
* The choice of word size is determined purely by performance concerns.
*/
- private final static int ADDRESS_BITS_PER_WORD = 6;
- private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
- private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;
+ private static final int ADDRESS_BITS_PER_WORD = 6;
+ private static final int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
+ private static final int BIT_INDEX_MASK = BITS_PER_WORD - 1;
/* Used to shift left or right for a partial word mask */
private static final long WORD_MASK = 0xffffffffffffffffL;
@@ -437,7 +438,7 @@
*
* @param bitIndex a bit index
* @throws IndexOutOfBoundsException if the specified index is negative
- * @since JDK1.0
+ * @since 1.0
*/
public void set(int bitIndex) {
if (bitIndex < 0)
@@ -533,7 +534,7 @@
*
* @param bitIndex the index of the bit to be cleared
* @throws IndexOutOfBoundsException if the specified index is negative
- * @since JDK1.0
+ * @since 1.0
*/
public void clear(int bitIndex) {
if (bitIndex < 0)
@@ -1209,40 +1210,186 @@
* is the number of bits in the set state, equal to the value
* returned by the {@link #cardinality()} method.
*
- * <p>The bit set must remain constant during the execution of the
- * terminal stream operation. Otherwise, the result of the terminal
- * stream operation is undefined.
+ * <p>The stream binds to this bit set when the terminal stream operation
+ * commences (specifically, the spliterator for the stream is
+ * <a href="Spliterator.html#binding"><em>late-binding</em></a>). If the
+ * bit set is modified during that operation then the result is undefined.
*
* @return a stream of integers representing set indices
* @since 1.8
*/
public IntStream stream() {
- class BitSetIterator implements PrimitiveIterator.OfInt {
- int next = nextSetBit(0);
+ class BitSetSpliterator implements Spliterator.OfInt {
+ private int index; // current bit index for a set bit
+ private int fence; // -1 until used; then one past last bit index
+ private int est; // size estimate
+ private boolean root; // true if root and not split
+ // root == true then size estimate is accurate
+ // index == -1 or index >= fence if fully traversed
+ // Special case when the max bit set is Integer.MAX_VALUE
- @Override
- public boolean hasNext() {
- return next != -1;
+ BitSetSpliterator(int origin, int fence, int est, boolean root) {
+ this.index = origin;
+ this.fence = fence;
+ this.est = est;
+ this.root = root;
+ }
+
+ private int getFence() {
+ int hi;
+ if ((hi = fence) < 0) {
+ // Round up fence to maximum cardinality for allocated words
+ // This is sufficient and cheap for sequential access
+ // When splitting this value is lowered
+ hi = fence = (wordsInUse >= wordIndex(Integer.MAX_VALUE))
+ ? Integer.MAX_VALUE
+ : wordsInUse << ADDRESS_BITS_PER_WORD;
+ est = cardinality();
+ index = nextSetBit(0);
+ }
+ return hi;
}
@Override
- public int nextInt() {
- if (next != -1) {
- int ret = next;
- next = nextSetBit(next+1);
- return ret;
- } else {
- throw new NoSuchElementException();
+ public boolean tryAdvance(IntConsumer action) {
+ Objects.requireNonNull(action);
+
+ int hi = getFence();
+ int i = index;
+ if (i < 0 || i >= hi) {
+ // Check if there is a final bit set for Integer.MAX_VALUE
+ if (i == Integer.MAX_VALUE && hi == Integer.MAX_VALUE) {
+ index = -1;
+ action.accept(Integer.MAX_VALUE);
+ return true;
+ }
+ return false;
+ }
+
+ index = nextSetBit(i + 1, wordIndex(hi - 1));
+ action.accept(i);
+ return true;
+ }
+
+ @Override
+ public void forEachRemaining(IntConsumer action) {
+ Objects.requireNonNull(action);
+
+ int hi = getFence();
+ int i = index;
+ index = -1;
+
+ if (i >= 0 && i < hi) {
+ action.accept(i++);
+
+ int u = wordIndex(i); // next lower word bound
+ int v = wordIndex(hi - 1); // upper word bound
+
+ words_loop:
+ for (; u <= v && i <= hi; u++, i = u << ADDRESS_BITS_PER_WORD) {
+ long word = words[u] & (WORD_MASK << i);
+ while (word != 0) {
+ i = (u << ADDRESS_BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
+ if (i >= hi) {
+ // Break out of outer loop to ensure check of
+ // Integer.MAX_VALUE bit set
+ break words_loop;
+ }
+
+ // Flip the set bit
+ word &= ~(1L << i);
+
+ action.accept(i);
+ }
+ }
+ }
+
+ // Check if there is a final bit set for Integer.MAX_VALUE
+ if (i == Integer.MAX_VALUE && hi == Integer.MAX_VALUE) {
+ action.accept(Integer.MAX_VALUE);
}
}
- }
- return StreamSupport.intStream(
- () -> Spliterators.spliterator(
- new BitSetIterator(), cardinality(),
- Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED),
- Spliterator.SIZED | Spliterator.SUBSIZED |
- Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED,
- false);
+ @Override
+ public OfInt trySplit() {
+ int hi = getFence();
+ int lo = index;
+ if (lo < 0) {
+ return null;
+ }
+
+ // Lower the fence to be the upper bound of last bit set
+ // The index is the first bit set, thus this spliterator
+ // covers one bit and cannot be split, or two or more
+ // bits
+ hi = fence = (hi < Integer.MAX_VALUE || !get(Integer.MAX_VALUE))
+ ? previousSetBit(hi - 1) + 1
+ : Integer.MAX_VALUE;
+
+ // Find the mid point
+ int mid = (lo + hi) >>> 1;
+ if (lo >= mid) {
+ return null;
+ }
+
+ // Raise the index of this spliterator to be the next set bit
+ // from the mid point
+ index = nextSetBit(mid, wordIndex(hi - 1));
+ root = false;
+
+ // Don't lower the fence (mid point) of the returned spliterator,
+ // traversal or further splitting will do that work
+ return new BitSetSpliterator(lo, mid, est >>>= 1, false);
+ }
+
+ @Override
+ public long estimateSize() {
+ getFence(); // force init
+ return est;
+ }
+
+ @Override
+ public int characteristics() {
+ // Only sized when root and not split
+ return (root ? Spliterator.SIZED : 0) |
+ Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED;
+ }
+
+ @Override
+ public Comparator<? super Integer> getComparator() {
+ return null;
+ }
+ }
+ return StreamSupport.intStream(new BitSetSpliterator(0, -1, 0, true), false);
}
+
+ /**
+ * Returns the index of the first bit that is set to {@code true}
+ * that occurs on or after the specified starting index and up to and
+ * including the specified word index
+ * If no such bit exists then {@code -1} is returned.
+ *
+ * @param fromIndex the index to start checking from (inclusive)
+ * @param toWordIndex the last word index to check (inclusive)
+ * @return the index of the next set bit, or {@code -1} if there
+ * is no such bit
+ */
+ private int nextSetBit(int fromIndex, int toWordIndex) {
+ int u = wordIndex(fromIndex);
+ // Check if out of bounds
+ if (u > toWordIndex)
+ return -1;
+
+ long word = words[u] & (WORD_MASK << fromIndex);
+
+ while (true) {
+ if (word != 0)
+ return (u * BITS_PER_WORD) + Long.numberOfTrailingZeros(word);
+ // Check if out of bounds
+ if (++u > toWordIndex)
+ return -1;
+ word = words[u];
+ }
+ }
+
}
diff --git a/ojluni/src/main/java/java/util/StringJoiner.java b/ojluni/src/main/java/java/util/StringJoiner.java
index 07ccbcc..27e2fb2 100644
--- a/ojluni/src/main/java/java/util/StringJoiner.java
+++ b/ojluni/src/main/java/java/util/StringJoiner.java
@@ -67,16 +67,15 @@
private final String delimiter;
private final String suffix;
- /** Contains all the string components added so far. */
- private String[] elts;
+ /*
+ * StringBuilder value -- at any time, the characters constructed from the
+ * prefix, the added element separated by the delimiter, but without the
+ * suffix, so that we can more easily add elements without having to jigger
+ * the suffix each time.
+ */
+ private StringBuilder value;
- /** The number of string components added so far. */
- private int size;
-
- /** Total length in chars so far, excluding prefix and suffix. */
- private int len;
-
- /**
+ /*
* When overridden by the user to be non-null via {@link setEmptyValue}, the
* string returned by toString() when no elements have yet been added.
* When null, prefix + suffix is used as the empty value.
@@ -125,6 +124,7 @@
this.prefix = prefix.toString();
this.delimiter = delimiter.toString();
this.suffix = suffix.toString();
+ this.emptyValue = this.prefix + this.suffix;
}
/**
@@ -147,12 +147,6 @@
return this;
}
- private static int getChars(String s, char[] chars, int start) {
- int len = s.length();
- s.getChars(0, len, chars, start);
- return len;
- }
-
/**
* Returns the current value, consisting of the {@code prefix}, the values
* added so far separated by the {@code delimiter}, and the {@code suffix},
@@ -163,28 +157,19 @@
*/
@Override
public String toString() {
- final String[] elts = this.elts;
- if (elts == null && emptyValue != null) {
+ if (value == null) {
return emptyValue;
- }
- final int size = this.size;
- final int addLen = prefix.length() + suffix.length();
- if (addLen == 0) {
- compactElts();
- return size == 0 ? "" : elts[0];
- }
- final String delimiter = this.delimiter;
- final char[] chars = new char[len + addLen];
- int k = getChars(prefix, chars, 0);
- if (size > 0) {
- k += getChars(elts[0], chars, k);
- for (int i = 1; i < size; i++) {
- k += getChars(delimiter, chars, k);
- k += getChars(elts[i], chars, k);
+ } else {
+ if (suffix.equals("")) {
+ return value.toString();
+ } else {
+ int initialLength = value.length();
+ String result = value.append(suffix).toString();
+ // reset value to pre-append initialLength
+ value.setLength(initialLength);
+ return result;
}
}
- k += getChars(suffix, chars, k);
- return new String(chars);
}
/**
@@ -196,16 +181,7 @@
* @return a reference to this {@code StringJoiner}
*/
public StringJoiner add(CharSequence newElement) {
- final String elt = String.valueOf(newElement);
- if (elts == null) {
- elts = new String[8];
- } else {
- if (size == elts.length)
- elts = Arrays.copyOf(elts, 2 * size);
- len += delimiter.length();
- }
- len += elt.length();
- elts[size++] = elt;
+ prepareBuilder().append(newElement);
return this;
}
@@ -230,25 +206,24 @@
*/
public StringJoiner merge(StringJoiner other) {
Objects.requireNonNull(other);
- if (other.elts == null) {
- return this;
+ if (other.value != null) {
+ final int length = other.value.length();
+ // lock the length so that we can seize the data to be appended
+ // before initiate copying to avoid interference, especially when
+ // merge 'this'
+ StringBuilder builder = prepareBuilder();
+ builder.append(other.value, other.prefix.length(), length);
}
- other.compactElts();
- return add(other.elts[0]);
+ return this;
}
- private void compactElts() {
- if (size > 1) {
- final char[] chars = new char[len];
- int i = 1, k = getChars(elts[0], chars, 0);
- do {
- k += getChars(delimiter, chars, k);
- k += getChars(elts[i], chars, k);
- elts[i] = null;
- } while (++i < size);
- size = 1;
- elts[0] = new String(chars);
+ private StringBuilder prepareBuilder() {
+ if (value != null) {
+ value.append(delimiter);
+ } else {
+ value = new StringBuilder().append(prefix);
}
+ return value;
}
/**
@@ -262,7 +237,10 @@
* @return the length of the current value of {@code StringJoiner}
*/
public int length() {
- return (size == 0 && emptyValue != null) ? emptyValue.length() :
- len + prefix.length() + suffix.length();
+ // Remember that we never actually append the suffix unless we return
+ // the full (present) value or some sub-string or length of it, so that
+ // we can add on more if we need to.
+ return (value != null ? value.length() + suffix.length() :
+ emptyValue.length());
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/AbstractExecutorService.java b/ojluni/src/main/java/java/util/concurrent/AbstractExecutorService.java
index a206867..e4d2235 100644
--- a/ojluni/src/main/java/java/util/concurrent/AbstractExecutorService.java
+++ b/ojluni/src/main/java/java/util/concurrent/AbstractExecutorService.java
@@ -245,7 +245,8 @@
Future<T> f = futures.get(i);
if (!f.isDone()) {
try { f.get(); }
- catch (CancellationException | ExecutionException ignore) {}
+ catch (CancellationException ignore) {}
+ catch (ExecutionException ignore) {}
}
}
return futures;
@@ -282,7 +283,8 @@
Future<T> f = futures.get(j);
if (!f.isDone()) {
try { f.get(deadline - System.nanoTime(), NANOSECONDS); }
- catch (CancellationException | ExecutionException ignore) {}
+ catch (CancellationException ignore) {}
+ catch (ExecutionException ignore) {}
catch (TimeoutException timedOut) {
break timedOut;
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/ojluni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
index dced5a4..96a60b3 100644
--- a/ojluni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
@@ -46,8 +46,10 @@
import java.util.Spliterators;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A bounded {@linkplain BlockingQueue blocking queue} backed by an
@@ -72,24 +74,17 @@
* generally decreases throughput but reduces variability and avoids
* starvation.
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
+ * @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this queue
*/
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
- /*
- * Much of the implementation mechanics, especially the unusual
- * nested loops, are shared and co-maintained with ArrayDeque.
- */
-
/**
* Serialization ID. This class relies on default serialization
* even for the items array, which is default-serialized, even if
@@ -134,21 +129,10 @@
// Internal helper methods
/**
- * Increments i, mod modulus.
- * Precondition and postcondition: 0 <= i < modulus.
+ * Circularly decrements array index i.
*/
- static final int inc(int i, int modulus) {
- if (++i >= modulus) i = 0;
- return i;
- }
-
- /**
- * Decrements i, mod modulus.
- * Precondition and postcondition: 0 <= i < modulus.
- */
- static final int dec(int i, int modulus) {
- if (--i < 0) i = modulus - 1;
- return i;
+ final int dec(int i) {
+ return ((i == 0) ? items.length : i) - 1;
}
/**
@@ -160,24 +144,14 @@
}
/**
- * Returns element at array index i.
- * This is a slight abuse of generics, accepted by javac.
- */
- @SuppressWarnings("unchecked")
- static <E> E itemAt(Object[] items, int i) {
- return (E) items[i];
- }
-
- /**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
- private void enqueue(E e) {
- // assert lock.isHeldByCurrentThread();
+ private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
- items[putIndex] = e;
+ items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0;
count++;
notEmpty.signal();
@@ -188,19 +162,18 @@
* Call only when holding lock.
*/
private E dequeue() {
- // assert lock.isHeldByCurrentThread();
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
- E e = (E) items[takeIndex];
+ E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
- return e;
+ return x;
}
/**
@@ -209,7 +182,6 @@
* Call only when holding lock.
*/
void removeAt(final int removeIndex) {
- // assert lock.isHeldByCurrentThread();
// assert lock.getHoldCount() == 1;
// assert items[removeIndex] != null;
// assert removeIndex >= 0 && removeIndex < items.length;
@@ -295,7 +267,6 @@
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
- final Object[] items = this.items;
int i = 0;
try {
for (E e : c)
@@ -510,16 +481,15 @@
try {
if (count > 0) {
final Object[] items = this.items;
- for (int i = takeIndex, end = putIndex,
- to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++)
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
- if (to == end) break;
- }
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i])) {
+ removeAt(i);
+ return true;
+ }
+ if (++i == items.length) i = 0;
+ } while (i != putIndex);
}
return false;
} finally {
@@ -542,14 +512,13 @@
try {
if (count > 0) {
final Object[] items = this.items;
- for (int i = takeIndex, end = putIndex,
- to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++)
- if (o.equals(items[i]))
- return true;
- if (to == end) break;
- }
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i]))
+ return true;
+ if (++i == items.length) i = 0;
+ } while (i != putIndex);
}
return false;
} finally {
@@ -656,9 +625,15 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int k;
- if ((k = count) > 0) {
- circularClear(items, takeIndex, putIndex);
+ int k = count;
+ if (k > 0) {
+ final Object[] items = this.items;
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ items[i] = null;
+ if (++i == items.length) i = 0;
+ } while (i != putIndex);
takeIndex = putIndex;
count = 0;
if (itrs != null)
@@ -672,20 +647,6 @@
}
/**
- * Nulls out slots starting at array index i, upto index end.
- * Condition i == end means "full" - the entire array is cleared.
- */
- private static void circularClear(Object[] items, int i, int end) {
- // assert 0 <= i && i < items.length;
- // assert 0 <= end && end < items.length;
- for (int to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++) items[i] = null;
- if (to == end) break;
- }
- }
-
- /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@@ -717,8 +678,8 @@
try {
while (i < n) {
@SuppressWarnings("unchecked")
- E e = (E) items[take];
- c.add(e);
+ E x = (E) items[take];
+ c.add(x);
items[take] = null;
if (++take == items.length) take = 0;
i++;
@@ -847,7 +808,7 @@
* there is known to be at least one iterator to collect
*/
void doSomeSweeping(boolean tryHarder) {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
// assert head != null;
int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
Node o, p;
@@ -903,7 +864,7 @@
* Adds a new iterator to the linked list of tracked iterators.
*/
void register(Itr itr) {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
head = new Node(itr, head);
}
@@ -913,7 +874,7 @@
* Notifies all iterators, and expunges any that are now stale.
*/
void takeIndexWrapped() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
cycles++;
for (Node o = null, p = head; p != null;) {
final Itr it = p.get();
@@ -970,7 +931,7 @@
* clears all weak refs, and unlinks the itrs datastructure.
*/
void queueIsEmpty() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
for (Node p = head; p != null; p = p.next) {
Itr it = p.get();
if (it != null) {
@@ -986,7 +947,7 @@
* Called whenever an element has been dequeued (at takeIndex).
*/
void elementDequeued() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
if (count == 0)
queueIsEmpty();
else if (takeIndex == 0)
@@ -1011,11 +972,6 @@
* expected element to remove, in lastItem. Yes, we may fail to
* remove lastItem from the queue if it moved due to an interleaved
* interior remove while in detached mode.
- *
- * Method forEachRemaining, added in Java 8, is treated similarly
- * to hasNext returning false, in that we switch to detached mode,
- * but we regard it as an even stronger request to "close" this
- * iteration, and don't bother supporting subsequent remove().
*/
private class Itr implements Iterator<E> {
/** Index to look for new nextItem; NONE at end */
@@ -1052,6 +1008,7 @@
private static final int DETACHED = -3;
Itr() {
+ // assert lock.getHoldCount() == 0;
lastRet = NONE;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
@@ -1084,12 +1041,12 @@
}
boolean isDetached() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
return prevTakeIndex < 0;
}
private int incCursor(int index) {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
if (++index == items.length) index = 0;
if (index == putIndex) index = NONE;
return index;
@@ -1114,7 +1071,7 @@
* operation on this iterator. Call only from iterating thread.
*/
private void incorporateDequeues() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
// assert itrs != null;
// assert !isDetached();
// assert count > 0;
@@ -1128,7 +1085,7 @@
final int len = items.length;
// how far takeIndex has advanced since the previous
// operation of this iterator
- long dequeues = (long) (cycles - prevCycles) * len
+ long dequeues = (cycles - prevCycles) * len
+ (takeIndex - prevTakeIndex);
// Check indices for invalidation
@@ -1157,7 +1114,7 @@
*/
private void detach() {
// Switch to detached mode
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
// assert cursor == NONE;
// assert nextIndex < 0;
// assert lastRet < 0 || nextItem == null;
@@ -1177,6 +1134,7 @@
* triggered by queue modifications.
*/
public boolean hasNext() {
+ // assert lock.getHoldCount() == 0;
if (nextItem != null)
return true;
noNext();
@@ -1206,8 +1164,9 @@
}
public E next() {
- final E e = nextItem;
- if (e == null)
+ // assert lock.getHoldCount() == 0;
+ final E x = nextItem;
+ if (x == null)
throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
@@ -1225,48 +1184,17 @@
} else {
nextIndex = NONE;
nextItem = null;
- if (lastRet == REMOVED) detach();
}
} finally {
lock.unlock();
}
- return e;
- }
-
- public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final ReentrantLock lock = ArrayBlockingQueue.this.lock;
- lock.lock();
- try {
- final E e = nextItem;
- if (e == null) return;
- if (!isDetached())
- incorporateDequeues();
- action.accept(e);
- if (isDetached() || cursor < 0) return;
- final Object[] items = ArrayBlockingQueue.this.items;
- for (int i = cursor, end = putIndex,
- to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++)
- action.accept(itemAt(items, i));
- if (to == end) break;
- }
- } finally {
- // Calling forEachRemaining is a strong hint that this
- // iteration is surely over; supporting remove() after
- // forEachRemaining() is more trouble than it's worth
- cursor = nextIndex = lastRet = NONE;
- nextItem = lastItem = null;
- detach();
- lock.unlock();
- }
+ return x;
}
public void remove() {
+ // assert lock.getHoldCount() == 0;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
- // assert lock.getHoldCount() == 1;
try {
if (!isDetached())
incorporateDequeues(); // might update lastRet or detach
@@ -1304,7 +1232,7 @@
* from next(), as promised by returning true from hasNext().
*/
void shutdown() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
cursor = NONE;
if (nextIndex >= 0)
nextIndex = REMOVED;
@@ -1332,7 +1260,7 @@
* @return true if this iterator should be unlinked from itrs
*/
boolean removedAt(int removedIndex) {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
if (isDetached())
return true;
@@ -1357,7 +1285,7 @@
}
else if (x > removedDistance) {
// assert cursor != prevTakeIndex;
- this.cursor = cursor = dec(cursor, len);
+ this.cursor = cursor = dec(cursor);
}
}
int lastRet = this.lastRet;
@@ -1366,7 +1294,7 @@
if (x == removedDistance)
this.lastRet = lastRet = REMOVED;
else if (x > removedDistance)
- this.lastRet = lastRet = dec(lastRet, len);
+ this.lastRet = lastRet = dec(lastRet);
}
int nextIndex = this.nextIndex;
if (nextIndex >= 0) {
@@ -1374,7 +1302,7 @@
if (x == removedDistance)
this.nextIndex = nextIndex = REMOVED;
else if (x > removedDistance)
- this.nextIndex = nextIndex = dec(nextIndex, len);
+ this.nextIndex = nextIndex = dec(nextIndex);
}
if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
this.prevTakeIndex = DETACHED;
@@ -1389,7 +1317,7 @@
* @return true if this iterator should be unlinked from itrs
*/
boolean takeIndexWrapped() {
- // assert lock.isHeldByCurrentThread();
+ // assert lock.getHoldCount() == 1;
if (isDetached())
return true;
if (itrs.cycles - prevCycles > 1) {
@@ -1438,197 +1366,4 @@
Spliterator.CONCURRENT));
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count > 0) {
- final Object[] items = this.items;
- for (int i = takeIndex, end = putIndex,
- to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++)
- action.accept(itemAt(items, i));
- if (to == end) break;
- }
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
-
- /** Implementation of bulk remove methods. */
- private boolean bulkRemove(Predicate<? super E> filter) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (itrs == null) { // check for active iterators
- if (count > 0) {
- final Object[] items = this.items;
- // Optimize for initial run of survivors
- for (int i = takeIndex, end = putIndex,
- to = (i < end) ? end : items.length;
- ; i = 0, to = end) {
- for (; i < to; i++)
- if (filter.test(itemAt(items, i)))
- return bulkRemoveModified(filter, i);
- if (to == end) break;
- }
- }
- return false;
- }
- } finally {
- lock.unlock();
- }
- // Active iterators are too hairy!
- // Punting (for now) to the slow n^2 algorithm ...
- return super.removeIf(filter);
- }
-
- // A tiny bit set implementation
-
- private static long[] nBits(int n) {
- return new long[((n - 1) >> 6) + 1];
- }
- private static void setBit(long[] bits, int i) {
- bits[i >> 6] |= 1L << i;
- }
- private static boolean isClear(long[] bits, int i) {
- return (bits[i >> 6] & (1L << i)) == 0;
- }
-
- /**
- * Returns circular distance from i to j, disambiguating i == j to
- * items.length; never returns 0.
- */
- private int distanceNonEmpty(int i, int j) {
- if ((j -= i) <= 0) j += items.length;
- return j;
- }
-
- /**
- * Helper for bulkRemove, in case of at least one deletion.
- * Tolerate predicates that reentrantly access the collection for
- * read (but not write), so traverse once to find elements to
- * delete, a second pass to physically expunge.
- *
- * @param beg valid index of first element to be deleted
- */
- private boolean bulkRemoveModified(
- Predicate<? super E> filter, final int beg) {
- final Object[] es = items;
- final int capacity = items.length;
- final int end = putIndex;
- final long[] deathRow = nBits(distanceNonEmpty(beg, putIndex));
- deathRow[0] = 1L; // set bit 0
- for (int i = beg + 1, to = (i <= end) ? end : es.length, k = beg;
- ; i = 0, to = end, k -= capacity) {
- for (; i < to; i++)
- if (filter.test(itemAt(es, i)))
- setBit(deathRow, i - k);
- if (to == end) break;
- }
- // a two-finger traversal, with hare i reading, tortoise w writing
- int w = beg;
- for (int i = beg + 1, to = (i <= end) ? end : es.length, k = beg;
- ; w = 0) { // w rejoins i on second leg
- // In this loop, i and w are on the same leg, with i > w
- for (; i < to; i++)
- if (isClear(deathRow, i - k))
- es[w++] = es[i];
- if (to == end) break;
- // In this loop, w is on the first leg, i on the second
- for (i = 0, to = end, k -= capacity; i < to && w < capacity; i++)
- if (isClear(deathRow, i - k))
- es[w++] = es[i];
- if (i >= to) {
- if (w == capacity) w = 0; // "corner" case
- break;
- }
- }
- count -= distanceNonEmpty(w, end);
- circularClear(es, putIndex = w, end);
- return true;
- }
-
- /** debugging */
- void checkInvariants() {
- // meta-assertions
- // assert lock.isHeldByCurrentThread();
- if (!invariantsSatisfied()) {
- String detail = String.format(
- "takeIndex=%d putIndex=%d count=%d capacity=%d items=%s",
- takeIndex, putIndex, count, items.length,
- Arrays.toString(items));
- System.err.println(detail);
- throw new AssertionError(detail);
- }
- }
-
- private boolean invariantsSatisfied() {
- // Unlike ArrayDeque, we have a count field but no spare slot.
- // We prefer ArrayDeque's strategy (and the names of its fields!),
- // but our field layout is baked into the serial form, and so is
- // too annoying to change.
- //
- // putIndex == takeIndex must be disambiguated by checking count.
- int capacity = items.length;
- return capacity > 0
- && items.getClass() == Object[].class
- && (takeIndex | putIndex | count) >= 0
- && takeIndex < capacity
- && putIndex < capacity
- && count <= capacity
- && (putIndex - takeIndex - count) % capacity == 0
- && (count == 0 || items[takeIndex] != null)
- && (count == capacity || items[putIndex] == null)
- && (count == 0 || items[dec(putIndex, capacity)] != null);
- }
-
- /**
- * Reconstitutes this queue from a stream (that is, deserializes it).
- *
- * @param s the stream
- * @throws ClassNotFoundException if the class of a serialized object
- * could not be found
- * @throws java.io.InvalidObjectException if invariants are violated
- * @throws java.io.IOException if an I/O error occurs
- */
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
-
- // Read in items array and various fields
- s.defaultReadObject();
-
- if (!invariantsSatisfied())
- throw new java.io.InvalidObjectException("invariants violated");
- }
}
diff --git a/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java b/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
index 8043510..290fee1 100644
--- a/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
+++ b/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
@@ -39,6 +39,10 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
+// BEGIN android-note
+// fixed framework docs link to "Collection#optional"
+// END android-note
+
/**
* A {@link Deque} that additionally supports blocking operations that wait
* for the deque to become non-empty when retrieving an element, and wait for
@@ -53,69 +57,69 @@
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
- * <table class="plain">
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of BlockingDeque methods</caption>
* <tr>
- * <th id="First" colspan="5"> First Element (Head)</th>
+ * <td ALIGN=CENTER COLSPAN = 5> <b>First Element (Head)</b></td>
* </tr>
* <tr>
* <td></td>
- * <th id="FThrow" style="font-weight:normal; font-style: italic">Throws exception</th>
- * <th id="FValue" style="font-weight:normal; font-style: italic">Special value</th>
- * <th id="FBlock" style="font-weight:normal; font-style: italic">Blocks</th>
- * <th id="FTimes" style="font-weight:normal; font-style: italic">Times out</th>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Blocks</em></td>
+ * <td ALIGN=CENTER><em>Times out</em></td>
* </tr>
* <tr>
- * <th id="FInsert" style="text-align:left">Insert</th>
- * <td headers="First FInsert FThrow">{@link #addFirst(Object) addFirst(e)}</td>
- * <td headers="First FInsert FValue">{@link #offerFirst(Object) offerFirst(e)}</td>
- * <td headers="First FInsert FBlock">{@link #putFirst(Object) putFirst(e)}</td>
- * <td headers="First FInsert FTimes">{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td>
+ * <td><b>Insert</b></td>
+ * <td>{@link #addFirst addFirst(e)}</td>
+ * <td>{@link #offerFirst(Object) offerFirst(e)}</td>
+ * <td>{@link #putFirst putFirst(e)}</td>
+ * <td>{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td>
* </tr>
* <tr>
- * <th id="FRemove" style="text-align:left">Remove</th>
- * <td headers="First FRemove FThrow">{@link #removeFirst() removeFirst()}</td>
- * <td headers="First FRemove FValue">{@link #pollFirst() pollFirst()}</td>
- * <td headers="First FRemove FBlock">{@link #takeFirst() takeFirst()}</td>
- * <td headers="First FRemove FTimes">{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
+ * <td><b>Remove</b></td>
+ * <td>{@link #removeFirst removeFirst()}</td>
+ * <td>{@link #pollFirst pollFirst()}</td>
+ * <td>{@link #takeFirst takeFirst()}</td>
+ * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
* </tr>
* <tr>
- * <th id="FExamine" style="text-align:left">Examine</th>
- * <td headers="First FExamine FThrow">{@link #getFirst() getFirst()}</td>
- * <td headers="First FExamine FValue">{@link #peekFirst() peekFirst()}</td>
- * <td headers="First FExamine FBlock" style="font-style:italic">not applicable</td>
- * <td headers="First FExamine FTimes" style="font-style:italic">not applicable</td>
+ * <td><b>Examine</b></td>
+ * <td>{@link #getFirst getFirst()}</td>
+ * <td>{@link #peekFirst peekFirst()}</td>
+ * <td><em>not applicable</em></td>
+ * <td><em>not applicable</em></td>
* </tr>
* <tr>
- * <th id="Last" colspan="5"> Last Element (Tail)</th>
+ * <td ALIGN=CENTER COLSPAN = 5> <b>Last Element (Tail)</b></td>
* </tr>
* <tr>
* <td></td>
- * <th id="LThrow" style="font-weight:normal; font-style: italic">Throws exception</th>
- * <th id="LValue" style="font-weight:normal; font-style: italic">Special value</th>
- * <th id="LBlock" style="font-weight:normal; font-style: italic">Blocks</th>
- * <th id="LTimes" style="font-weight:normal; font-style: italic">Times out</th>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Blocks</em></td>
+ * <td ALIGN=CENTER><em>Times out</em></td>
* </tr>
* <tr>
- * <th id="LInsert" style="text-align:left">Insert</th>
- * <td headers="Last LInsert LThrow">{@link #addLast(Object) addLast(e)}</td>
- * <td headers="Last LInsert LValue">{@link #offerLast(Object) offerLast(e)}</td>
- * <td headers="Last LInsert LBlock">{@link #putLast(Object) putLast(e)}</td>
- * <td headers="Last LInsert LTimes">{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
+ * <td><b>Insert</b></td>
+ * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
+ * <td>{@link #putLast putLast(e)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
- * <th id="LRemove" style="text-align:left">Remove</th>
- * <td headers="Last LRemove LThrow">{@link #removeLast() removeLast()}</td>
- * <td headers="Last LRemove LValue">{@link #pollLast() pollLast()}</td>
- * <td headers="Last LRemove LBlock">{@link #takeLast() takeLast()}</td>
- * <td headers="Last LRemove LTimes">{@link #pollLast(long, TimeUnit) pollLast(time, unit)}</td>
+ * <td><b>Remove</b></td>
+ * <td>{@link #removeLast() removeLast()}</td>
+ * <td>{@link #pollLast() pollLast()}</td>
+ * <td>{@link #takeLast takeLast()}</td>
+ * <td>{@link #pollLast(long, TimeUnit) pollLast(time, unit)}</td>
* </tr>
* <tr>
- * <th id="LExamine" style="text-align:left">Examine</th>
- * <td headers="Last LExamine LThrow">{@link #getLast() getLast()}</td>
- * <td headers="Last LExamine LValue">{@link #peekLast() peekLast()}</td>
- * <td headers="Last LExamine LBlock" style="font-style:italic">not applicable</td>
- * <td headers="Last LExamine LTimes" style="font-style:italic">not applicable</td>
+ * <td><b>Examine</b></td>
+ * <td>{@link #getLast getLast()}</td>
+ * <td>{@link #peekLast peekLast()}</td>
+ * <td><em>not applicable</em></td>
+ * <td><em>not applicable</em></td>
* </tr>
* </table>
*
@@ -128,55 +132,60 @@
* {@code BlockingQueue} interface are precisely equivalent to
* {@code BlockingDeque} methods as indicated in the following table:
*
- * <table class="plain">
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Comparison of BlockingQueue and BlockingDeque methods</caption>
* <tr>
- * <td></td>
- * <th id="BQueue"> {@code BlockingQueue} Method</th>
- * <th id="BDeque"> Equivalent {@code BlockingDeque} Method</th>
+ * <td ALIGN=CENTER> <b>{@code BlockingQueue} Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent {@code BlockingDeque} Method</b></td>
* </tr>
* <tr>
- * <th id="Insert" rowspan="4" style="text-align:left; vertical-align:top">Insert</th>
- * <th id="add" style="font-weight:normal; text-align:left">{@link #add(Object) add(e)}</th>
- * <td headers="Insert BDeque add">{@link #addLast(Object) addLast(e)}</td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td>
* </tr>
* <tr>
- * <th id="offer1" style="font-weight:normal; text-align:left">{@link #offer(Object) offer(e)}</th>
- * <td headers="Insert BDeque offer1">{@link #offerLast(Object) offerLast(e)}</td>
+ * <td>{@link #add(Object) add(e)}</td>
+ * <td>{@link #addLast(Object) addLast(e)}</td>
* </tr>
* <tr>
- * <th id="put" style="font-weight:normal; text-align:left">{@link #put(Object) put(e)}</th>
- * <td headers="Insert BDeque put">{@link #putLast(Object) putLast(e)}</td>
+ * <td>{@link #offer(Object) offer(e)}</td>
+ * <td>{@link #offerLast(Object) offerLast(e)}</td>
* </tr>
* <tr>
- * <th id="offer2" style="font-weight:normal; text-align:left">{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</th>
- * <td headers="Insert BDeque offer2">{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
+ * <td>{@link #put(Object) put(e)}</td>
+ * <td>{@link #putLast(Object) putLast(e)}</td>
* </tr>
* <tr>
- * <th id="Remove" rowspan="4" style="text-align:left; vertical-align:top">Remove</th>
- * <th id="remove" style="font-weight:normal; text-align:left">{@link #remove() remove()}</th>
- * <td headers="Remove BDeque remove">{@link #removeFirst() removeFirst()}</td>
+ * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
+ * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td>
* </tr>
* <tr>
- * <th id="poll1" style="font-weight:normal; text-align:left">{@link #poll() poll()}</th>
- * <td headers="Remove BDeque poll1">{@link #pollFirst() pollFirst()}</td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td>
* </tr>
* <tr>
- * <th id="take" style="font-weight:normal; text-align:left">{@link #take() take()}</th>
- * <td headers="Remove BDeque take">{@link #takeFirst() takeFirst()}</td>
+ * <td>{@link #remove() remove()}</td>
+ * <td>{@link #removeFirst() removeFirst()}</td>
* </tr>
* <tr>
- * <th id="poll2" style="font-weight:normal; text-align:left">{@link #poll(long, TimeUnit) poll(time, unit)}</th>
- * <td headers="Remove BDeque poll2">{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
+ * <td>{@link #poll() poll()}</td>
+ * <td>{@link #pollFirst() pollFirst()}</td>
* </tr>
* <tr>
- * <th id="Examine" rowspan="2" style="text-align:left; vertical-align:top">Examine</th>
- * <th id="element" style="font-weight:normal; text-align:left">{@link #element() element()}</th>
- * <td headers="Examine BDeque element">{@link #getFirst() getFirst()}</td>
+ * <td>{@link #take() take()}</td>
+ * <td>{@link #takeFirst() takeFirst()}</td>
* </tr>
* <tr>
- * <th id="peek" style="font-weight:normal; text-align:left">{@link #peek() peek()}</th>
- * <td headers="Examine BDeque peek">{@link #peekFirst() peekFirst()}</td>
+ * <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td>
+ * <td>{@link #pollFirst(long, TimeUnit) pollFirst(time, unit)}</td>
+ * </tr>
+ * <tr>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>Examine</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #element() element()}</td>
+ * <td>{@link #getFirst() getFirst()}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #peek() peek()}</td>
+ * <td>{@link #peekFirst() peekFirst()}</td>
* </tr>
* </table>
*
@@ -188,7 +197,7 @@
* the {@code BlockingDeque} in another thread.
*
* <p>This interface is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @since 1.6
@@ -399,9 +408,9 @@
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
@@ -417,9 +426,9 @@
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
@@ -507,7 +516,7 @@
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque).
- * This method differs from {@link #poll() poll()} only in that it
+ * This method differs from {@link #poll poll} only in that it
* throws an exception if this deque is empty.
*
* <p>This method is equivalent to {@link #removeFirst() removeFirst}.
@@ -558,7 +567,7 @@
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque).
- * This method differs from {@link #peek() peek} only in that it throws an
+ * This method differs from {@link #peek peek} only in that it throws an
* exception if this deque is empty.
*
* <p>This method is equivalent to {@link #getFirst() getFirst}.
@@ -594,9 +603,9 @@
* @return {@code true} if this deque changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -609,9 +618,9 @@
* @return {@code true} if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
diff --git a/ojluni/src/main/java/java/util/concurrent/BlockingQueue.java b/ojluni/src/main/java/java/util/concurrent/BlockingQueue.java
index 6fecc27..6f01b77 100644
--- a/ojluni/src/main/java/java/util/concurrent/BlockingQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/BlockingQueue.java
@@ -38,10 +38,16 @@
import java.util.Collection;
import java.util.Queue;
+// BEGIN android-note
+// removed link to collections framework docs from header
+// fixed framework docs link to "Collection#optional"
+// END android-note
+
/**
- * A {@link Queue} that additionally supports operations that wait for
- * the queue to become non-empty when retrieving an element, and wait
- * for space to become available in the queue when storing an element.
+ * A {@link java.util.Queue} that additionally supports operations
+ * that wait for the queue to become non-empty when retrieving an
+ * element, and wait for space to become available in the queue when
+ * storing an element.
*
* <p>{@code BlockingQueue} methods come in four forms, with different ways
* of handling operations that cannot be satisfied immediately, but may be
@@ -52,35 +58,35 @@
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
- * <table class="plain">
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of BlockingQueue methods</caption>
* <tr>
* <td></td>
- * <th scope="col" style="font-weight:normal; font-style:italic">Throws exception</th>
- * <th scope="col" style="font-weight:normal; font-style:italic">Special value</th>
- * <th scope="col" style="font-weight:normal; font-style:italic">Blocks</th>
- * <th scope="col" style="font-weight:normal; font-style:italic">Times out</th>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Blocks</em></td>
+ * <td ALIGN=CENTER><em>Times out</em></td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left">Insert</th>
- * <td>{@link #add(Object) add(e)}</td>
- * <td>{@link #offer(Object) offer(e)}</td>
- * <td>{@link #put(Object) put(e)}</td>
+ * <td><b>Insert</b></td>
+ * <td>{@link #add add(e)}</td>
+ * <td>{@link #offer offer(e)}</td>
+ * <td>{@link #put put(e)}</td>
* <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left">Remove</th>
- * <td>{@link #remove() remove()}</td>
- * <td>{@link #poll() poll()}</td>
- * <td>{@link #take() take()}</td>
+ * <td><b>Remove</b></td>
+ * <td>{@link #remove remove()}</td>
+ * <td>{@link #poll poll()}</td>
+ * <td>{@link #take take()}</td>
* <td>{@link #poll(long, TimeUnit) poll(time, unit)}</td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left">Examine</th>
- * <td>{@link #element() element()}</td>
- * <td>{@link #peek() peek()}</td>
- * <td style="font-style: italic">not applicable</td>
- * <td style="font-style: italic">not applicable</td>
+ * <td><b>Examine</b></td>
+ * <td>{@link #element element()}</td>
+ * <td>{@link #peek peek()}</td>
+ * <td><em>not applicable</em></td>
+ * <td><em>not applicable</em></td>
* </tr>
* </table>
*
@@ -98,7 +104,7 @@
*
* <p>{@code BlockingQueue} implementations are designed to be used
* primarily for producer-consumer queues, but additionally support
- * the {@link Collection} interface. So, for example, it is
+ * the {@link java.util.Collection} interface. So, for example, it is
* possible to remove an arbitrary element from a queue using
* {@code remove(x)}. However, such operations are in general
* <em>not</em> performed very efficiently, and are intended for only
@@ -168,10 +174,7 @@
* actions subsequent to the access or removal of that element from
* the {@code BlockingQueue} in another thread.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
+ * @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this queue
*/
@@ -301,9 +304,9 @@
* @return {@code true} if this queue changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -316,9 +319,9 @@
* @return {@code true} if this queue contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
diff --git a/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java b/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
index 936ffc3..e98e1be 100644
--- a/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
+++ b/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -112,9 +110,9 @@
* in a {@link NullPointerException} being thrown.
*
* @author Doug Lea
+ * @since 1.8
* @param <T> The result type returned by this future's {@code join}
* and {@code get} methods
- * @since 1.8
*/
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
@@ -127,29 +125,26 @@
* applies across normal vs exceptional outcomes, sync vs async
* actions, binary triggers, and various forms of completions.
*
- * Non-nullness of volatile field "result" indicates done. It may
- * be set directly if known to be thread-confined, else via CAS.
- * An AltResult is used to box null as a result, as well as to
- * hold exceptions. Using a single field makes completion simple
- * to detect and trigger. Result encoding and decoding is
- * straightforward but tedious and adds to the sprawl of trapping
- * and associating exceptions with targets. Minor simplifications
- * rely on (static) NIL (to box null results) being the only
- * AltResult with a null exception field, so we don't usually need
- * explicit comparisons. Even though some of the generics casts
- * are unchecked (see SuppressWarnings annotations), they are
- * placed to be appropriate even if checked.
+ * Non-nullness of field result (set via CAS) indicates done. An
+ * AltResult is used to box null as a result, as well as to hold
+ * exceptions. Using a single field makes completion simple to
+ * detect and trigger. Encoding and decoding is straightforward
+ * but adds to the sprawl of trapping and associating exceptions
+ * with targets. Minor simplifications rely on (static) NIL (to
+ * box null results) being the only AltResult with a null
+ * exception field, so we don't usually need explicit comparisons.
+ * Even though some of the generics casts are unchecked (see
+ * SuppressWarnings annotations), they are placed to be
+ * appropriate even if checked.
*
* Dependent actions are represented by Completion objects linked
* as Treiber stacks headed by field "stack". There are Completion
- * classes for each kind of action, grouped into:
- * - single-input (UniCompletion),
- * - two-input (BiCompletion),
- * - projected (BiCompletions using exactly one of two inputs),
- * - shared (CoCompletion, used by the second of two sources),
- * - zero-input source actions,
- * - Signallers that unblock waiters.
- * Class Completion extends ForkJoinTask to enable async execution
+ * classes for each kind of action, grouped into single-input
+ * (UniCompletion), two-input (BiCompletion), projected
+ * (BiCompletions using either (not both) of two inputs), shared
+ * (CoCompletion, used by the second of two sources), zero-input
+ * source actions, and Signallers that unblock waiters. Class
+ * Completion extends ForkJoinTask to enable async execution
* (adding no space overhead because we exploit its "tag" methods
* to maintain claims). It is also declared as Runnable to allow
* usage with arbitrary executors.
@@ -165,7 +160,7 @@
* encounter layers of adapters in common usages.
*
* * Boolean CompletableFuture method x(...) (for example
- * biApply) takes all of the arguments needed to check that an
+ * uniApply) takes all of the arguments needed to check that an
* action is triggerable, and then either runs the action or
* arranges its async execution by executing its Completion
* argument, if present. The method returns true if known to be
@@ -175,32 +170,24 @@
* method with its held arguments, and on success cleans up.
* The mode argument allows tryFire to be called twice (SYNC,
* then ASYNC); the first to screen and trap exceptions while
- * arranging to execute, and the second when called from a task.
- * (A few classes are not used async so take slightly different
- * forms.) The claim() callback suppresses function invocation
- * if already claimed by another thread.
- *
- * * Some classes (for example UniApply) have separate handling
- * code for when known to be thread-confined ("now" methods) and
- * for when shared (in tryFire), for efficiency.
+ * arranging to execute, and the second when called from a
+ * task. (A few classes are not used async so take slightly
+ * different forms.) The claim() callback suppresses function
+ * invocation if already claimed by another thread.
*
* * CompletableFuture method xStage(...) is called from a public
- * stage method of CompletableFuture f. It screens user
+ * stage method of CompletableFuture x. It screens user
* arguments and invokes and/or creates the stage object. If
- * not async and already triggerable, the action is run
- * immediately. Otherwise a Completion c is created, and
- * submitted to the executor if triggerable, or pushed onto f's
- * stack if not. Completion actions are started via c.tryFire.
- * We recheck after pushing to a source future's stack to cover
- * possible races if the source completes while pushing.
- * Classes with two inputs (for example BiApply) deal with races
- * across both while pushing actions. The second completion is
- * a CoCompletion pointing to the first, shared so that at most
- * one performs the action. The multiple-arity methods allOf
- * does this pairwise to form trees of completions. Method
- * anyOf is handled differently from allOf because completion of
- * any source should trigger a cleanStack of other sources.
- * Each AnyOf completion can reach others via a shared array.
+ * not async and x is already complete, the action is run
+ * immediately. Otherwise a Completion c is created, pushed to
+ * x's stack (unless done), and started or triggered via
+ * c.tryFire. This also covers races possible if x completes
+ * while pushing. Classes with two inputs (for example BiApply)
+ * deal with races across both while pushing actions. The
+ * second completion is a CoCompletion pointing to the first,
+ * shared so that at most one performs the action. The
+ * multiple-arity methods allOf and anyOf do this pairwise to
+ * form trees of completions.
*
* Note that the generic type parameters of methods vary according
* to whether "this" is a source, dependent, or completion.
@@ -225,30 +212,29 @@
* pointing back to its sources. So we null out fields as soon as
* possible. The screening checks needed anyway harmlessly ignore
* null arguments that may have been obtained during races with
- * threads nulling out fields. We also try to unlink non-isLive
- * (fired or cancelled) Completions from stacks that might
- * otherwise never be popped: Method cleanStack always unlinks non
- * isLive completions from the head of stack; others may
- * occasionally remain if racing with other cancellations or
- * removals.
- *
- * Completion fields need not be declared as final or volatile
- * because they are only visible to other threads upon safe
- * publication.
+ * threads nulling out fields. We also try to unlink fired
+ * Completions from stacks that might never be popped (see method
+ * postFire). Completion fields need not be declared as final or
+ * volatile because they are only visible to other threads upon
+ * safe publication.
*/
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
final boolean internalComplete(Object r) { // CAS from null to r
- return RESULT.compareAndSet(this, null, r);
+ return U.compareAndSwapObject(this, RESULT, null, r);
+ }
+
+ final boolean casStack(Completion cmp, Completion val) {
+ return U.compareAndSwapObject(this, STACK, cmp, val);
}
/** Returns true if successfully pushed c onto stack. */
final boolean tryPushStack(Completion c) {
Completion h = stack;
- NEXT.set(c, h); // CAS piggyback
- return STACK.compareAndSet(this, h, c);
+ lazySetNext(c, h);
+ return U.compareAndSwapObject(this, STACK, h, c);
}
/** Unconditionally pushes c onto stack, retrying if necessary. */
@@ -268,7 +254,8 @@
/** Completes with the null value, unless already completed. */
final boolean completeNull() {
- return RESULT.compareAndSet(this, null, NIL);
+ return U.compareAndSwapObject(this, RESULT, null,
+ NIL);
}
/** Returns the encoding of the given non-exceptional value. */
@@ -278,7 +265,8 @@
/** Completes with a non-exceptional result, unless already completed. */
final boolean completeValue(T t) {
- return RESULT.compareAndSet(this, null, (t == null) ? NIL : t);
+ return U.compareAndSwapObject(this, RESULT, null,
+ (t == null) ? NIL : t);
}
/**
@@ -292,7 +280,8 @@
/** Completes with an exceptional result, unless already completed. */
final boolean completeThrowable(Throwable x) {
- return RESULT.compareAndSet(this, null, encodeThrowable(x));
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeThrowable(x));
}
/**
@@ -319,7 +308,8 @@
* existing CompletionException.
*/
final boolean completeThrowable(Throwable x, Object r) {
- return RESULT.compareAndSet(this, null, encodeThrowable(x, r));
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeThrowable(x, r));
}
/**
@@ -337,11 +327,10 @@
*/
static Object encodeRelay(Object r) {
Throwable x;
- if (r instanceof AltResult
- && (x = ((AltResult)r).ex) != null
- && !(x instanceof CompletionException))
- r = new AltResult(new CompletionException(x));
- return r;
+ return (((r instanceof AltResult) &&
+ (x = ((AltResult)r).ex) != null &&
+ !(x instanceof CompletionException)) ?
+ new AltResult(new CompletionException(x)) : r);
}
/**
@@ -349,13 +338,14 @@
* If exceptional, r is first coerced to a CompletionException.
*/
final boolean completeRelay(Object r) {
- return RESULT.compareAndSet(this, null, encodeRelay(r));
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeRelay(r));
}
/**
* Reports result using Future.get conventions.
*/
- private static Object reportGet(Object r)
+ private static <T> T reportGet(Object r)
throws InterruptedException, ExecutionException {
if (r == null) // by convention below, null means interrupted
throw new InterruptedException();
@@ -370,13 +360,14 @@
x = cause;
throw new ExecutionException(x);
}
- return r;
+ @SuppressWarnings("unchecked") T t = (T) r;
+ return t;
}
/**
* Decodes outcome to return result or throw unchecked exception.
*/
- private static Object reportJoin(Object r) {
+ private static <T> T reportJoin(Object r) {
if (r instanceof AltResult) {
Throwable x;
if ((x = ((AltResult)r).ex) == null)
@@ -387,7 +378,8 @@
throw (CompletionException)x;
throw new CompletionException(x);
}
- return r;
+ @SuppressWarnings("unchecked") T t = (T) r;
+ return t;
}
/* ------------- Async task preliminaries -------------- */
@@ -433,6 +425,12 @@
static final int ASYNC = 1;
static final int NESTED = -1;
+ /**
+ * Spins before blocking in waitingGet
+ */
+ static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
+ 1 << 8 : 0);
+
/* ------------- Base Completion classes and operations -------------- */
@SuppressWarnings("serial")
@@ -457,6 +455,10 @@
public final void setRawResult(Void v) {}
}
+ static void lazySetNext(Completion c, Completion next) {
+ U.putOrderedObject(c, NEXT, next);
+ }
+
/**
* Pops and tries to trigger all reachable dependents. Call only
* when known to be done.
@@ -471,47 +473,40 @@
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture<?> d; Completion t;
- if (STACK.compareAndSet(f, h, t = h.next)) {
+ if (f.casStack(h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
- NEXT.compareAndSet(h, t, null); // try to detach
+ h.next = null; // detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
- /** Traverses stack and unlinks one or more dead Completions, if found. */
+ /** Traverses stack and unlinks dead Completions. */
final void cleanStack() {
- Completion p = stack;
- // ensure head of stack live
- for (boolean unlinked = false;;) {
- if (p == null)
- return;
- else if (p.isLive()) {
- if (unlinked)
- return;
- else
- break;
- }
- else if (STACK.weakCompareAndSet(this, p, (p = p.next)))
- unlinked = true;
- else
- p = stack;
- }
- // try to unlink first non-live
- for (Completion q = p.next; q != null;) {
+ for (Completion p = null, q = stack; q != null;) {
Completion s = q.next;
if (q.isLive()) {
p = q;
q = s;
- } else if (NEXT.weakCompareAndSet(p, q, s))
- break;
- else
- q = p.next;
+ }
+ else if (p == null) {
+ casStack(q, s);
+ q = stack;
+ }
+ else {
+ p.next = s;
+ if (p.isLive())
+ q = s;
+ else {
+ p = null; // restart
+ q = stack;
+ }
+ }
}
}
@@ -549,34 +544,24 @@
final boolean isLive() { return dep != null; }
}
- /**
- * Pushes the given completion unless it completes while trying.
- * Caller should first check that result is null.
- */
- final void unipush(Completion c) {
+ /** Pushes the given completion (if it exists) unless done. */
+ final void push(UniCompletion<?,?> c) {
if (c != null) {
- while (!tryPushStack(c)) {
- if (result != null) {
- NEXT.set(c, null);
- break;
- }
- }
- if (result != null)
- c.tryFire(SYNC);
+ while (result == null && !tryPushStack(c))
+ lazySetNext(c, null); // clear on failure
}
}
/**
- * Post-processing by dependent after successful UniCompletion tryFire.
- * Tries to clean stack of source a, and then either runs postComplete
- * or returns this to caller, depending on mode.
+ * Post-processing by dependent after successful UniCompletion
+ * tryFire. Tries to clean stack of source a, and then either runs
+ * postComplete or returns this to caller, depending on mode.
*/
final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
if (a != null && a.stack != null) {
- Object r;
- if ((r = a.result) == null)
+ if (mode < 0 || a.result == null)
a.cleanStack();
- if (mode >= 0 && (r != null || a.result != null))
+ else
a.postComplete();
}
if (result != null && stack != null) {
@@ -598,65 +583,48 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- Object r; Throwable x; Function<? super T,? extends V> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null)
+ if ((d = dep) == null ||
+ !d.uniApply(a = src, fn, mode > 0 ? null : this))
return null;
- tryComplete: if (d.result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (mode <= 0 && !claim())
- return null;
- else {
- @SuppressWarnings("unchecked") T t = (T) r;
- d.completeValue(f.apply(t));
- }
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
+ final <S> boolean uniApply(CompletableFuture<S> a,
+ Function<? super S,? extends T> f,
+ UniApply<S,T> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ completeValue(f.apply(s));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private <V> CompletableFuture<V> uniApplyStage(
Executor e, Function<? super T,? extends V> f) {
if (f == null) throw new NullPointerException();
- Object r;
- if ((r = result) != null)
- return uniApplyNow(r, e, f);
CompletableFuture<V> d = newIncompleteFuture();
- unipush(new UniApply<T,V>(e, d, this, f));
- return d;
- }
-
- private <V> CompletableFuture<V> uniApplyNow(
- Object r, Executor e, Function<? super T,? extends V> f) {
- Throwable x;
- CompletableFuture<V> d = newIncompleteFuture();
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.result = encodeThrowable(x, r);
- return d;
- }
- r = null;
- }
- try {
- if (e != null) {
- e.execute(new UniApply<T,V>(null, d, this, f));
- } else {
- @SuppressWarnings("unchecked") T t = (T) r;
- d.result = d.encodeValue(f.apply(t));
- }
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
+ if (e != null || !d.uniApply(this, f, null)) {
+ UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
}
return d;
}
@@ -670,67 +638,48 @@
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d; CompletableFuture<T> a;
- Object r; Throwable x; Consumer<? super T> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null)
+ if ((d = dep) == null ||
+ !d.uniAccept(a = src, fn, mode > 0 ? null : this))
return null;
- tryComplete: if (d.result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (mode <= 0 && !claim())
- return null;
- else {
- @SuppressWarnings("unchecked") T t = (T) r;
- f.accept(t);
- d.completeNull();
- }
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
+ final <S> boolean uniAccept(CompletableFuture<S> a,
+ Consumer<? super S> f, UniAccept<S> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ f.accept(s);
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private CompletableFuture<Void> uniAcceptStage(Executor e,
Consumer<? super T> f) {
if (f == null) throw new NullPointerException();
- Object r;
- if ((r = result) != null)
- return uniAcceptNow(r, e, f);
CompletableFuture<Void> d = newIncompleteFuture();
- unipush(new UniAccept<T>(e, d, this, f));
- return d;
- }
-
- private CompletableFuture<Void> uniAcceptNow(
- Object r, Executor e, Consumer<? super T> f) {
- Throwable x;
- CompletableFuture<Void> d = newIncompleteFuture();
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.result = encodeThrowable(x, r);
- return d;
- }
- r = null;
- }
- try {
- if (e != null) {
- e.execute(new UniAccept<T>(null, d, this, f));
- } else {
- @SuppressWarnings("unchecked") T t = (T) r;
- f.accept(t);
- d.result = NIL;
- }
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
+ if (e != null || !d.uniAccept(this, f, null)) {
+ UniAccept<T> c = new UniAccept<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
}
return d;
}
@@ -744,56 +693,42 @@
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d; CompletableFuture<T> a;
- Object r; Throwable x; Runnable f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null)
+ if ((d = dep) == null ||
+ !d.uniRun(a = src, fn, mode > 0 ? null : this))
return null;
- if (d.result == null) {
- if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
- d.completeThrowable(x, r);
- else
- try {
- if (mode <= 0 && !claim())
- return null;
- else {
- f.run();
- d.completeNull();
- }
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
- if (f == null) throw new NullPointerException();
- Object r;
- if ((r = result) != null)
- return uniRunNow(r, e, f);
- CompletableFuture<Void> d = newIncompleteFuture();
- unipush(new UniRun<T>(e, d, this, f));
- return d;
+ final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ if (result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else
+ try {
+ if (c != null && !c.claim())
+ return false;
+ f.run();
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
}
- private CompletableFuture<Void> uniRunNow(Object r, Executor e, Runnable f) {
- Throwable x;
+ private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
+ if (f == null) throw new NullPointerException();
CompletableFuture<Void> d = newIncompleteFuture();
- if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
- d.result = encodeThrowable(x, r);
- else
- try {
- if (e != null) {
- e.execute(new UniRun<T>(null, d, this, f));
- } else {
- f.run();
- d.result = NIL;
- }
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.uniRun(this, f, null)) {
+ UniRun<T> c = new UniRun<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -807,20 +742,20 @@
}
final CompletableFuture<T> tryFire(int mode) {
CompletableFuture<T> d; CompletableFuture<T> a;
- Object r; BiConsumer<? super T, ? super Throwable> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || !d.uniWhenComplete(r, f, mode > 0 ? null : this))
+ if ((d = dep) == null ||
+ !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniWhenComplete(Object r,
+ final boolean uniWhenComplete(CompletableFuture<T> a,
BiConsumer<? super T,? super Throwable> f,
UniWhenComplete<T> c) {
- T t; Throwable x = null;
+ Object r; T t; Throwable x = null;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -852,17 +787,10 @@
Executor e, BiConsumer<? super T, ? super Throwable> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<T> d = newIncompleteFuture();
- Object r;
- if ((r = result) == null)
- unipush(new UniWhenComplete<T>(e, d, this, f));
- else if (e == null)
- d.uniWhenComplete(r, f, null);
- else {
- try {
- e.execute(new UniWhenComplete<T>(null, d, this, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.uniWhenComplete(this, f, null)) {
+ UniWhenComplete<T> c = new UniWhenComplete<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
}
return d;
}
@@ -877,20 +805,20 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- Object r; BiFunction<? super T, Throwable, ? extends V> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || !d.uniHandle(r, f, mode > 0 ? null : this))
+ if ((d = dep) == null ||
+ !d.uniHandle(a = src, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final <S> boolean uniHandle(Object r,
+ final <S> boolean uniHandle(CompletableFuture<S> a,
BiFunction<? super S, Throwable, ? extends T> f,
UniHandle<S,T> c) {
- S s; Throwable x;
+ Object r; S s; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -915,17 +843,10 @@
Executor e, BiFunction<? super T, Throwable, ? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<V> d = newIncompleteFuture();
- Object r;
- if ((r = result) == null)
- unipush(new UniHandle<T,V>(e, d, this, f));
- else if (e == null)
- d.uniHandle(r, f, null);
- else {
- try {
- e.execute(new UniHandle<T,V>(null, d, this, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.uniHandle(this, f, null)) {
+ UniHandle<T,V> c = new UniHandle<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
}
return d;
}
@@ -940,20 +861,19 @@
final CompletableFuture<T> tryFire(int mode) { // never ASYNC
// assert mode != ASYNC;
CompletableFuture<T> d; CompletableFuture<T> a;
- Object r; Function<? super Throwable, ? extends T> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || !d.uniExceptionally(r, f, this))
+ if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniExceptionally(Object r,
+ final boolean uniExceptionally(CompletableFuture<T> a,
Function<? super Throwable, ? extends T> f,
UniExceptionally<T> c) {
- Throwable x;
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
if (result == null) {
try {
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) {
@@ -973,39 +893,47 @@
Function<Throwable, ? extends T> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<T> d = newIncompleteFuture();
- Object r;
- if ((r = result) == null)
- unipush(new UniExceptionally<T>(d, this, f));
- else
- d.uniExceptionally(r, f, null);
+ if (!d.uniExceptionally(this, f, null)) {
+ UniExceptionally<T> c = new UniExceptionally<T>(d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
return d;
}
@SuppressWarnings("serial")
- static final class UniRelay<U, T extends U> extends UniCompletion<T,U> {
- UniRelay(CompletableFuture<U> dep, CompletableFuture<T> src) {
+ static final class UniRelay<T> extends UniCompletion<T,T> { // for Compose
+ UniRelay(CompletableFuture<T> dep, CompletableFuture<T> src) {
super(null, dep, src);
}
- final CompletableFuture<U> tryFire(int mode) {
- CompletableFuture<U> d; CompletableFuture<T> a; Object r;
- if ((d = dep) == null
- || (a = src) == null || (r = a.result) == null)
+ final CompletableFuture<T> tryFire(int mode) {
+ CompletableFuture<T> d; CompletableFuture<T> a;
+ if ((d = dep) == null || !d.uniRelay(a = src))
return null;
- if (d.result == null)
- d.completeRelay(r);
src = null; dep = null;
return d.postFire(a, mode);
}
}
- private static <U, T extends U> CompletableFuture<U> uniCopyStage(
- CompletableFuture<T> src) {
+ final boolean uniRelay(CompletableFuture<T> a) {
Object r;
- CompletableFuture<U> d = src.newIncompleteFuture();
- if ((r = src.result) != null)
- d.result = encodeRelay(r);
- else
- src.unipush(new UniRelay<U,T>(d, src));
+ if (a == null || (r = a.result) == null)
+ return false;
+ if (result == null) // no need to claim
+ completeRelay(r);
+ return true;
+ }
+
+ private CompletableFuture<T> uniCopyStage() {
+ Object r;
+ CompletableFuture<T> d = newIncompleteFuture();
+ if ((r = result) != null)
+ d.completeRelay(r);
+ else {
+ UniRelay<T> c = new UniRelay<T>(d, this);
+ push(c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -1014,7 +942,9 @@
if ((r = result) != null)
return new MinimalStage<T>(encodeRelay(r));
MinimalStage<T> d = new MinimalStage<T>();
- unipush(new UniRelay<T,T>(d, this));
+ UniRelay<T> c = new UniRelay<T>(d, this);
+ push(c);
+ c.tryFire(SYNC);
return d;
}
@@ -1028,48 +958,54 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- Function<? super T, ? extends CompletionStage<V>> f;
- Object r; Throwable x;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null)
+ if ((d = dep) == null ||
+ !d.uniCompose(a = src, fn, mode > 0 ? null : this))
return null;
- tryComplete: if (d.result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (mode <= 0 && !claim())
- return null;
- @SuppressWarnings("unchecked") T t = (T) r;
- CompletableFuture<V> g = f.apply(t).toCompletableFuture();
- if ((r = g.result) != null)
- d.completeRelay(r);
- else {
- g.unipush(new UniRelay<V,V>(d, g));
- if (d.result == null)
- return null;
- }
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
+ final <S> boolean uniCompose(
+ CompletableFuture<S> a,
+ Function<? super S, ? extends CompletionStage<T>> f,
+ UniCompose<S,T> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ CompletableFuture<T> g = f.apply(s).toCompletableFuture();
+ if (g.result == null || !uniRelay(g)) {
+ UniRelay<T> copy = new UniRelay<T>(this, g);
+ g.push(copy);
+ copy.tryFire(SYNC);
+ if (result == null)
+ return false;
+ }
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private <V> CompletableFuture<V> uniComposeStage(
Executor e, Function<? super T, ? extends CompletionStage<V>> f) {
if (f == null) throw new NullPointerException();
- CompletableFuture<V> d = newIncompleteFuture();
Object r, s; Throwable x;
- if ((r = result) == null)
- unipush(new UniCompose<T,V>(e, d, this, f));
- else if (e == null) {
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e == null && (r = result) != null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
d.result = encodeThrowable(x, r);
@@ -1081,20 +1017,21 @@
@SuppressWarnings("unchecked") T t = (T) r;
CompletableFuture<V> g = f.apply(t).toCompletableFuture();
if ((s = g.result) != null)
- d.result = encodeRelay(s);
+ d.completeRelay(s);
else {
- g.unipush(new UniRelay<V,V>(d, g));
+ UniRelay<V> c = new UniRelay<V>(d, g);
+ g.push(c);
+ c.tryFire(SYNC);
}
+ return d;
} catch (Throwable ex) {
d.result = encodeThrowable(ex);
+ return d;
}
}
- else
- try {
- e.execute(new UniCompose<T,V>(null, d, this, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ UniCompose<T,V> c = new UniCompose<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
return d;
}
@@ -1124,28 +1061,21 @@
}
final boolean isLive() {
BiCompletion<?,?,?> c;
- return (c = base) != null
- // && c.isLive()
- && c.dep != null;
+ return (c = base) != null && c.dep != null;
}
}
- /**
- * Pushes completion to this and b unless both done.
- * Caller should first check that either result or b.result is null.
- */
+ /** Pushes completion to this and b unless both done. */
final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
- while (result == null) {
- if (tryPushStack(c)) {
- if (b.result == null)
- b.unipush(new CoCompletion(c));
- else if (result != null)
- c.tryFire(SYNC);
- return;
- }
+ Object r;
+ while ((r = result) == null && !tryPushStack(c))
+ lazySetNext(c, null); // clear on failure
+ if (b != null && b != this && b.result == null) {
+ Completion q = (r != null) ? c : new CoCompletion(c);
+ while (b.result == null && !b.tryPushStack(q))
+ lazySetNext(q, null); // clear on failure
}
- b.unipush(c);
}
}
@@ -1153,10 +1083,9 @@
final CompletableFuture<T> postFire(CompletableFuture<?> a,
CompletableFuture<?> b, int mode) {
if (b != null && b.stack != null) { // clean second source
- Object r;
- if ((r = b.result) == null)
+ if (mode < 0 || b.result == null)
b.cleanStack();
- if (mode >= 0 && (r != null || b.result != null))
+ else
b.postComplete();
}
return postFire(a, mode);
@@ -1174,21 +1103,22 @@
CompletableFuture<V> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r, s; BiFunction<? super T,? super U,? extends V> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || (b = snd) == null || (s = b.result) == null
- || !d.biApply(r, s, f, mode > 0 ? null : this))
+ if ((d = dep) == null ||
+ !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final <R,S> boolean biApply(Object r, Object s,
+ final <R,S> boolean biApply(CompletableFuture<R> a,
+ CompletableFuture<S> b,
BiFunction<? super R,? super S,? extends T> f,
BiApply<R,S,T> c) {
- Throwable x;
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
tryComplete: if (result == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
@@ -1220,20 +1150,15 @@
private <U,V> CompletableFuture<V> biApplyStage(
Executor e, CompletionStage<U> o,
BiFunction<? super T,? super U,? extends V> f) {
- CompletableFuture<U> b; Object r, s;
+ CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture<V> d = newIncompleteFuture();
- if ((r = result) == null || (s = b.result) == null)
- bipush(b, new BiApply<T,U,V>(e, d, this, b, f));
- else if (e == null)
- d.biApply(r, s, f, null);
- else
- try {
- e.execute(new BiApply<T,U,V>(null, d, this, b, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.biApply(this, b, f, null)) {
+ BiApply<T,U,V> c = new BiApply<T,U,V>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -1249,21 +1174,22 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r, s; BiConsumer<? super T,? super U> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || (b = snd) == null || (s = b.result) == null
- || !d.biAccept(r, s, f, mode > 0 ? null : this))
+ if ((d = dep) == null ||
+ !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final <R,S> boolean biAccept(Object r, Object s,
+ final <R,S> boolean biAccept(CompletableFuture<R> a,
+ CompletableFuture<S> b,
BiConsumer<? super R,? super S> f,
BiAccept<R,S> c) {
- Throwable x;
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
tryComplete: if (result == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
@@ -1296,20 +1222,15 @@
private <U> CompletableFuture<Void> biAcceptStage(
Executor e, CompletionStage<U> o,
BiConsumer<? super T,? super U> f) {
- CompletableFuture<U> b; Object r, s;
+ CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture<Void> d = newIncompleteFuture();
- if ((r = result) == null || (s = b.result) == null)
- bipush(b, new BiAccept<T,U>(e, d, this, b, f));
- else if (e == null)
- d.biAccept(r, s, f, null);
- else
- try {
- e.execute(new BiAccept<T,U>(null, d, this, b, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.biAccept(this, b, f, null)) {
+ BiAccept<T,U> c = new BiAccept<T,U>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -1317,7 +1238,8 @@
static final class BiRun<T,U> extends BiCompletion<T,U,Void> {
Runnable fn;
BiRun(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src, CompletableFuture<U> snd,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
Runnable fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1325,25 +1247,25 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r, s; Runnable f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (r = a.result) == null
- || (b = snd) == null || (s = b.result) == null
- || !d.biRun(r, s, f, mode > 0 ? null : this))
+ if ((d = dep) == null ||
+ !d.biRun(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final boolean biRun(Object r, Object s, Runnable f, BiRun<?,?> c) {
- Throwable x; Object z;
+ final boolean biRun(CompletableFuture<?> a, CompletableFuture<?> b,
+ Runnable f, BiRun<?,?> c) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
if (result == null) {
- if ((r instanceof AltResult
- && (x = ((AltResult)(z = r)).ex) != null) ||
- (s instanceof AltResult
- && (x = ((AltResult)(z = s)).ex) != null))
- completeThrowable(x, z);
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
+ completeThrowable(x, s);
else
try {
if (c != null && !c.claim())
@@ -1359,52 +1281,52 @@
private CompletableFuture<Void> biRunStage(Executor e, CompletionStage<?> o,
Runnable f) {
- CompletableFuture<?> b; Object r, s;
+ CompletableFuture<?> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture<Void> d = newIncompleteFuture();
- if ((r = result) == null || (s = b.result) == null)
- bipush(b, new BiRun<>(e, d, this, b, f));
- else if (e == null)
- d.biRun(r, s, f, null);
- else
- try {
- e.execute(new BiRun<>(null, d, this, b, f));
- } catch (Throwable ex) {
- d.result = encodeThrowable(ex);
- }
+ if (e != null || !d.biRun(this, b, f, null)) {
+ BiRun<T,?> c = new BiRun<>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
@SuppressWarnings("serial")
static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And
BiRelay(CompletableFuture<Void> dep,
- CompletableFuture<T> src, CompletableFuture<U> snd) {
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd) {
super(null, dep, src, snd);
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r, s, z; Throwable x;
- if ((d = dep) == null
- || (a = src) == null || (r = a.result) == null
- || (b = snd) == null || (s = b.result) == null)
+ if ((d = dep) == null || !d.biRelay(a = src, b = snd))
return null;
- if (d.result == null) {
- if ((r instanceof AltResult
- && (x = ((AltResult)(z = r)).ex) != null) ||
- (s instanceof AltResult
- && (x = ((AltResult)(z = s)).ex) != null))
- d.completeThrowable(x, z);
- else
- d.completeNull();
- }
src = null; snd = null; dep = null;
return d.postFire(a, b, mode);
}
}
+ boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null)
+ return false;
+ if (result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
+ completeThrowable(x, s);
+ else
+ completeNull();
+ }
+ return true;
+ }
+
/** Recursively constructs a tree of completions. */
static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs,
int lo, int hi) {
@@ -1412,44 +1334,39 @@
if (lo > hi) // empty
d.result = NIL;
else {
- CompletableFuture<?> a, b; Object r, s, z; Throwable x;
+ CompletableFuture<?> a, b;
int mid = (lo + hi) >>> 1;
if ((a = (lo == mid ? cfs[lo] :
andTree(cfs, lo, mid))) == null ||
(b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
andTree(cfs, mid+1, hi))) == null)
throw new NullPointerException();
- if ((r = a.result) == null || (s = b.result) == null)
- a.bipush(b, new BiRelay<>(d, a, b));
- else if ((r instanceof AltResult
- && (x = ((AltResult)(z = r)).ex) != null) ||
- (s instanceof AltResult
- && (x = ((AltResult)(z = s)).ex) != null))
- d.result = encodeThrowable(x, z);
- else
- d.result = NIL;
+ if (!d.biRelay(a, b)) {
+ BiRelay<?,?> c = new BiRelay<>(d, a, b);
+ a.bipush(b, c);
+ c.tryFire(SYNC);
+ }
}
return d;
}
/* ------------- Projected (Ored) BiCompletions -------------- */
- /**
- * Pushes completion to this and b unless either done.
- * Caller should first check that result and b.result are both null.
- */
+ /** Pushes completion to this and b unless either done. */
final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
- while (!tryPushStack(c)) {
- if (result != null) {
- NEXT.set(c, null);
+ while ((b == null || b.result == null) && result == null) {
+ if (tryPushStack(c)) {
+ if (b != null && b != this && b.result == null) {
+ Completion q = new CoCompletion(c);
+ while (result == null && b.result == null &&
+ !b.tryPushStack(q))
+ lazySetNext(q, null); // clear on failure
+ }
break;
}
+ lazySetNext(c, null); // clear on failure
}
- if (result != null)
- c.tryFire(SYNC);
- else
- b.unipush(new CoCompletion(c));
}
}
@@ -1457,7 +1374,8 @@
static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> {
Function<? super T,? extends V> fn;
OrApply(Executor executor, CompletableFuture<V> dep,
- CompletableFuture<T> src, CompletableFuture<U> snd,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
Function<? super T,? extends V> fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1465,46 +1383,54 @@
CompletableFuture<V> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r; Throwable x; Function<? super T,? extends V> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (b = snd) == null
- || ((r = a.result) == null && (r = b.result) == null))
+ if ((d = dep) == null ||
+ !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
- tryComplete: if (d.result == null) {
- try {
- if (mode <= 0 && !claim())
- return null;
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- @SuppressWarnings("unchecked") T t = (T) r;
- d.completeValue(f.apply(t));
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
+ final <R,S extends R> boolean orApply(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ Function<? super R, ? extends T> f,
+ OrApply<R,S,T> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ completeValue(f.apply(rr));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private <U extends T,V> CompletableFuture<V> orApplyStage(
- Executor e, CompletionStage<U> o, Function<? super T, ? extends V> f) {
+ Executor e, CompletionStage<U> o,
+ Function<? super T, ? extends V> f) {
CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
-
- Object r; CompletableFuture<? extends T> z;
- if ((r = (z = this).result) != null ||
- (r = (z = b).result) != null)
- return z.uniApplyNow(r, e, f);
-
CompletableFuture<V> d = newIncompleteFuture();
- orpush(b, new OrApply<T,U,V>(e, d, this, b, f));
+ if (e != null || !d.orApply(this, b, f, null)) {
+ OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -1512,7 +1438,8 @@
static final class OrAccept<T,U extends T> extends BiCompletion<T,U,Void> {
Consumer<? super T> fn;
OrAccept(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src, CompletableFuture<U> snd,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
Consumer<? super T> fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1520,47 +1447,54 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r; Throwable x; Consumer<? super T> f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (b = snd) == null
- || ((r = a.result) == null && (r = b.result) == null))
+ if ((d = dep) == null ||
+ !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
- tryComplete: if (d.result == null) {
- try {
- if (mode <= 0 && !claim())
- return null;
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- d.completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- @SuppressWarnings("unchecked") T t = (T) r;
- f.accept(t);
- d.completeNull();
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
+ final <R,S extends R> boolean orAccept(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ Consumer<? super R> f,
+ OrAccept<R,S> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ f.accept(rr);
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private <U extends T> CompletableFuture<Void> orAcceptStage(
Executor e, CompletionStage<U> o, Consumer<? super T> f) {
CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
-
- Object r; CompletableFuture<? extends T> z;
- if ((r = (z = this).result) != null ||
- (r = (z = b).result) != null)
- return z.uniAcceptNow(r, e, f);
-
CompletableFuture<Void> d = newIncompleteFuture();
- orpush(b, new OrAccept<T,U>(e, d, this, b, f));
+ if (e != null || !d.orAccept(this, b, f, null)) {
+ OrAccept<T,U> c = new OrAccept<T,U>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
@@ -1568,7 +1502,8 @@
static final class OrRun<T,U> extends BiCompletion<T,U,Void> {
Runnable fn;
OrRun(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src, CompletableFuture<U> snd,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
Runnable fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1576,83 +1511,99 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- Object r; Throwable x; Runnable f;
- if ((d = dep) == null || (f = fn) == null
- || (a = src) == null || (b = snd) == null
- || ((r = a.result) == null && (r = b.result) == null))
+ if ((d = dep) == null ||
+ !d.orRun(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
- if (d.result == null) {
- try {
- if (mode <= 0 && !claim())
- return null;
- else if (r instanceof AltResult
- && (x = ((AltResult)r).ex) != null)
- d.completeThrowable(x, r);
- else {
- f.run();
- d.completeNull();
- }
- } catch (Throwable ex) {
- d.completeThrowable(ex);
- }
- }
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
+ final boolean orRun(CompletableFuture<?> a, CompletableFuture<?> b,
+ Runnable f, OrRun<?,?> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else {
+ f.run();
+ completeNull();
+ }
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
private CompletableFuture<Void> orRunStage(Executor e, CompletionStage<?> o,
Runnable f) {
CompletableFuture<?> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
-
- Object r; CompletableFuture<?> z;
- if ((r = (z = this).result) != null ||
- (r = (z = b).result) != null)
- return z.uniRunNow(r, e, f);
-
CompletableFuture<Void> d = newIncompleteFuture();
- orpush(b, new OrRun<>(e, d, this, b, f));
+ if (e != null || !d.orRun(this, b, f, null)) {
+ OrRun<T,?> c = new OrRun<>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
return d;
}
- /** Completion for an anyOf input future. */
@SuppressWarnings("serial")
- static class AnyOf extends Completion {
- CompletableFuture<Object> dep; CompletableFuture<?> src;
- CompletableFuture<?>[] srcs;
- AnyOf(CompletableFuture<Object> dep, CompletableFuture<?> src,
- CompletableFuture<?>[] srcs) {
- this.dep = dep; this.src = src; this.srcs = srcs;
+ static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or
+ OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src,
+ CompletableFuture<U> snd) {
+ super(null, dep, src, snd);
}
final CompletableFuture<Object> tryFire(int mode) {
- // assert mode != ASYNC;
- CompletableFuture<Object> d; CompletableFuture<?> a;
- CompletableFuture<?>[] as;
- Object r;
- if ((d = dep) == null
- || (a = src) == null || (r = a.result) == null
- || (as = srcs) == null)
- return null;
- dep = null; src = null; srcs = null;
- if (d.completeRelay(r)) {
- for (CompletableFuture<?> b : as)
- if (b != a)
- b.cleanStack();
- if (mode < 0)
- return d;
- else
- d.postComplete();
- }
- return null;
- }
- final boolean isLive() {
CompletableFuture<Object> d;
- return (d = dep) != null && d.result == null;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null || !d.orRelay(a = src, b = snd))
+ return null;
+ src = null; snd = null; dep = null;
+ return d.postFire(a, b, mode);
}
}
+ final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
+ Object r;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null))
+ return false;
+ if (result == null)
+ completeRelay(r);
+ return true;
+ }
+
+ /** Recursively constructs a tree of completions. */
+ static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs,
+ int lo, int hi) {
+ CompletableFuture<Object> d = new CompletableFuture<Object>();
+ if (lo <= hi) {
+ CompletableFuture<?> a, b;
+ int mid = (lo + hi) >>> 1;
+ if ((a = (lo == mid ? cfs[lo] :
+ orTree(cfs, lo, mid))) == null ||
+ (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
+ orTree(cfs, mid+1, hi))) == null)
+ throw new NullPointerException();
+ if (!d.orRelay(a, b)) {
+ OrRelay<?,?> c = new OrRelay<>(d, a, b);
+ a.orpush(b, c);
+ c.tryFire(SYNC);
+ }
+ }
+ return d;
+ }
+
/* ------------- Zero-input Async forms -------------- */
@SuppressWarnings("serial")
@@ -1665,7 +1616,7 @@
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
- public final boolean exec() { run(); return false; }
+ public final boolean exec() { run(); return true; }
public void run() {
CompletableFuture<T> d; Supplier<? extends T> f;
@@ -1701,7 +1652,7 @@
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
- public final boolean exec() { run(); return false; }
+ public final boolean exec() { run(); return true; }
public void run() {
CompletableFuture<Void> d; Runnable f;
@@ -1785,13 +1736,15 @@
private Object waitingGet(boolean interruptible) {
Signaller q = null;
boolean queued = false;
+ int spins = SPINS;
Object r;
while ((r = result) == null) {
- if (q == null) {
- q = new Signaller(interruptible, 0L, 0L);
- if (Thread.currentThread() instanceof ForkJoinWorkerThread)
- ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
+ if (spins > 0) {
+ if (ThreadLocalRandom.nextSecondarySeed() >= 0)
+ --spins;
}
+ else if (q == null)
+ q = new Signaller(interruptible, 0L, 0L);
else if (!queued)
queued = tryPushStack(q);
else {
@@ -1804,14 +1757,16 @@
break;
}
}
- if (q != null && queued) {
+ if (q != null) {
q.thread = null;
- if (!interruptible && q.interrupted)
- Thread.currentThread().interrupt();
- if (r == null)
- cleanStack();
+ if (q.interrupted) {
+ if (interruptible)
+ cleanStack();
+ else
+ Thread.currentThread().interrupt();
+ }
}
- if (r != null || (r = result) != null)
+ if (r != null)
postComplete();
return r;
}
@@ -1829,12 +1784,9 @@
Signaller q = null;
boolean queued = false;
Object r;
- while ((r = result) == null) { // similar to untimed
- if (q == null) {
+ while ((r = result) == null) { // similar to untimed, without spins
+ if (q == null)
q = new Signaller(true, nanos, deadline);
- if (Thread.currentThread() instanceof ForkJoinWorkerThread)
- ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
- }
else if (!queued)
queued = tryPushStack(q);
else if (q.nanos <= 0L)
@@ -1849,13 +1801,12 @@
break;
}
}
- if (q != null && queued) {
+ if (q != null)
q.thread = null;
- if (r == null)
- cleanStack();
- }
- if (r != null || (r = result) != null)
+ if (r != null)
postComplete();
+ else
+ cleanStack();
if (r != null || (q != null && q.interrupted))
return r;
}
@@ -1967,12 +1918,9 @@
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
- @SuppressWarnings("unchecked")
public T get() throws InterruptedException, ExecutionException {
Object r;
- if ((r = result) == null)
- r = waitingGet(true);
- return (T) reportGet(r);
+ return reportGet((r = result) == null ? waitingGet(true) : r);
}
/**
@@ -1988,14 +1936,11 @@
* while waiting
* @throws TimeoutException if the wait timed out
*/
- @SuppressWarnings("unchecked")
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- long nanos = unit.toNanos(timeout);
Object r;
- if ((r = result) == null)
- r = timedGet(nanos);
- return (T) reportGet(r);
+ long nanos = unit.toNanos(timeout);
+ return reportGet((r = result) == null ? timedGet(nanos) : r);
}
/**
@@ -2012,12 +1957,9 @@
* @throws CompletionException if this future completed
* exceptionally or a completion computation threw an exception
*/
- @SuppressWarnings("unchecked")
public T join() {
Object r;
- if ((r = result) == null)
- r = waitingGet(false);
- return (T) reportJoin(r);
+ return reportJoin((r = result) == null ? waitingGet(false) : r);
}
/**
@@ -2030,10 +1972,9 @@
* @throws CompletionException if this future completed
* exceptionally or a completion computation threw an exception
*/
- @SuppressWarnings("unchecked")
public T getNow(T valueIfAbsent) {
Object r;
- return ((r = result) == null) ? valueIfAbsent : (T) reportJoin(r);
+ return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
}
/**
@@ -2329,28 +2270,7 @@
* {@code null}
*/
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
- int n; Object r;
- if ((n = cfs.length) <= 1)
- return (n == 0)
- ? new CompletableFuture<Object>()
- : uniCopyStage(cfs[0]);
- for (CompletableFuture<?> cf : cfs)
- if ((r = cf.result) != null)
- return new CompletableFuture<Object>(encodeRelay(r));
- cfs = cfs.clone();
- CompletableFuture<Object> d = new CompletableFuture<>();
- for (CompletableFuture<?> cf : cfs)
- cf.unipush(new AnyOf(d, cf, cfs));
- // If d was completed while we were adding completions, we should
- // clean the stack of any sources that may have had completions
- // pushed on their stack after d was completed.
- if (d.result != null)
- for (int i = 0, len = cfs.length; i < len; i++)
- if (cfs[i].result != null)
- for (i++; i < len; i++)
- if (cfs[i].result == null)
- cfs[i].cleanStack();
- return d;
+ return orTree(cfs, 0, cfs.length - 1);
}
/* ------------- Control and status methods -------------- */
@@ -2466,13 +2386,13 @@
for (Completion p = stack; p != null; p = p.next)
++count;
return super.toString() +
- ((r == null)
- ? ((count == 0)
- ? "[Not completed]"
- : "[Not completed, " + count + " dependents]")
- : (((r instanceof AltResult) && ((AltResult)r).ex != null)
- ? "[Completed exceptionally: " + ((AltResult)r).ex + "]"
- : "[Completed normally]"));
+ ((r == null) ?
+ ((count == 0) ?
+ "[Not completed]" :
+ "[Not completed, " + count + " dependents]") :
+ (((r instanceof AltResult) && ((AltResult)r).ex != null) ?
+ "[Completed exceptionally]" :
+ "[Completed normally]"));
}
// jdk9 additions
@@ -2522,7 +2442,7 @@
* @since 9
*/
public CompletableFuture<T> copy() {
- return uniCopyStage(this);
+ return uniCopyStage();
}
/**
@@ -2535,13 +2455,6 @@
* exceptionally with a CompletionException with this exception as
* cause.
*
- * <p>Unless overridden by a subclass, a new non-minimal
- * CompletableFuture with all methods available can be obtained from
- * a minimal CompletionStage via {@link #toCompletableFuture()}.
- * For example, completion of a minimal stage can be awaited by
- *
- * <pre> {@code minimalStage.toCompletableFuture().join(); }</pre>
- *
* @return the new CompletionStage
* @since 9
*/
@@ -2836,30 +2749,23 @@
@Override public CompletableFuture<T> completeOnTimeout
(T value, long timeout, TimeUnit unit) {
throw new UnsupportedOperationException(); }
- @Override public CompletableFuture<T> toCompletableFuture() {
- Object r;
- if ((r = result) != null)
- return new CompletableFuture<T>(encodeRelay(r));
- else {
- CompletableFuture<T> d = new CompletableFuture<>();
- unipush(new UniRelay<T,T>(d, this));
- return d;
- }
- }
}
- // VarHandle mechanics
- private static final VarHandle RESULT;
- private static final VarHandle STACK;
- private static final VarHandle NEXT;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long RESULT;
+ private static final long STACK;
+ private static final long NEXT;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- RESULT = l.findVarHandle(CompletableFuture.class, "result", Object.class);
- STACK = l.findVarHandle(CompletableFuture.class, "stack", Completion.class);
- NEXT = l.findVarHandle(Completion.class, "next", Completion.class);
+ RESULT = U.objectFieldOffset
+ (CompletableFuture.class.getDeclaredField("result"));
+ STACK = U.objectFieldOffset
+ (CompletableFuture.class.getDeclaredField("stack"));
+ NEXT = U.objectFieldOffset
+ (Completion.class.getDeclaredField("next"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
// Reduce the risk of rare disastrous classloading in first call to
diff --git a/ojluni/src/main/java/java/util/concurrent/CompletionService.java b/ojluni/src/main/java/java/util/concurrent/CompletionService.java
index 5e5232e..f647e21 100644
--- a/ojluni/src/main/java/java/util/concurrent/CompletionService.java
+++ b/ojluni/src/main/java/java/util/concurrent/CompletionService.java
@@ -57,8 +57,6 @@
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* actions taken by that task, which in turn <i>happen-before</i>
* actions following a successful return from the corresponding {@code take()}.
- *
- * @since 1.5
*/
public interface CompletionService<V> {
/**
diff --git a/ojluni/src/main/java/java/util/concurrent/CompletionStage.java b/ojluni/src/main/java/java/util/concurrent/CompletionStage.java
index 70b601a..d855945 100644
--- a/ojluni/src/main/java/java/util/concurrent/CompletionStage.java
+++ b/ojluni/src/main/java/java/util/concurrent/CompletionStage.java
@@ -856,9 +856,13 @@
* CompletableFuture, this method may return this stage itself.
* Otherwise, invocation of this method may be equivalent in
* effect to {@code thenApply(x -> x)}, but returning an instance
- * of type {@code CompletableFuture}.
+ * of type {@code CompletableFuture}. A CompletionStage
+ * implementation that does not choose to interoperate with others
+ * may throw {@code UnsupportedOperationException}.
*
* @return the CompletableFuture
+ * @throws UnsupportedOperationException if this implementation
+ * does not interoperate with CompletableFuture
*/
public CompletableFuture<T> toCompletableFuture();
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index 2cf8c86..5407963 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -68,7 +68,10 @@
import java.util.function.ToLongBiFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;
-import jdk.internal.misc.Unsafe;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A hash table supporting full concurrency of retrievals and
@@ -159,7 +162,8 @@
* ordering, or on any other objects or values that may transiently
* change while computation is in progress; and except for forEach
* actions, should ideally be side-effect-free. Bulk operations on
- * {@link Map.Entry} objects do not support method {@code setValue}.
+ * {@link java.util.Map.Entry} objects do not support method {@code
+ * setValue}.
*
* <ul>
* <li>forEach: Performs a given action on each element.
@@ -252,10 +256,6 @@
*
* <p>All arguments to all task methods must be non-null.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.5
* @author Doug Lea
* @param <K> the type of keys maintained by this map
@@ -297,7 +297,7 @@
* Table accesses require volatile/atomic reads, writes, and
* CASes. Because there is no other way to arrange this without
* adding further indirections, we use intrinsics
- * (jdk.internal.misc.Unsafe) operations.
+ * (sun.misc.Unsafe) operations.
*
* We use the top (sign) bit of Node hash fields for control
* purposes -- it is available anyway because of addressing
@@ -628,14 +628,10 @@
volatile V val;
volatile Node<K,V> next;
- Node(int hash, K key, V val) {
+ Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
- }
-
- Node(int hash, K key, V val, Node<K,V> next) {
- this(hash, key, val);
this.next = next;
}
@@ -702,7 +698,12 @@
* See Hackers Delight, sec 3.2
*/
private static final int tableSizeFor(int c) {
- int n = -1 >>> Integer.numberOfLeadingZeros(c - 1);
+ int n = c - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
@@ -712,12 +713,12 @@
*/
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
- Class<?> c; Type[] ts, as; ParameterizedType p;
+ Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
if ((c = x.getClass()) == String.class) // bypass checks
return c;
if ((ts = c.getGenericInterfaces()) != null) {
- for (Type t : ts) {
- if ((t instanceof ParameterizedType) &&
+ for (int i = 0; i < ts.length; ++i) {
+ if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
@@ -742,7 +743,7 @@
/* ---------------- Table element access -------------- */
/*
- * Atomic access methods are used for table elements as well as
+ * Volatile access methods are used for table elements as well as
* elements of in-progress next table while resizing. All uses of
* the tab arguments must be null checked by callers. All callers
* also paranoically precheck that tab's length is not zero (or an
@@ -752,21 +753,23 @@
* errors by users, these checks must operate on local variables,
* which accounts for some odd-looking inline assignments below.
* Note that calls to setTabAt always occur within locked regions,
- * and so require only release ordering.
+ * and so in principle require only release ordering, not
+ * full volatile semantics, but are currently coded as volatile
+ * writes to be conservative.
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
- return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
+ return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
- return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
+ return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
- U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
+ U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
/* ---------------- Fields -------------- */
@@ -839,7 +842,12 @@
* elements is negative
*/
public ConcurrentHashMap(int initialCapacity) {
- this(initialCapacity, LOAD_FACTOR, 1);
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException();
+ int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+ this.sizeCtl = cap;
}
/**
@@ -873,8 +881,8 @@
/**
* Creates a new, empty map with an initial table size based on
- * the given number of elements ({@code initialCapacity}), initial
- * table density ({@code loadFactor}), and number of concurrently
+ * the given number of elements ({@code initialCapacity}), table
+ * density ({@code loadFactor}), and number of concurrently
* updating threads ({@code concurrencyLevel}).
*
* @param initialCapacity the initial capacity. The implementation
@@ -1012,20 +1020,16 @@
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh; K fk; V fv;
+ Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
- if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
+ if (casTabAt(tab, i, null,
+ new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
- else if (onlyIfAbsent // check first node without acquiring lock
- && fh == hash
- && ((fk = f.key) == key || (fk != null && key.equals(fk)))
- && (fv = f.val) != null)
- return fv;
else {
V oldVal = null;
synchronized (f) {
@@ -1044,7 +1048,8 @@
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
- pred.next = new Node<K,V>(hash, key, value);
+ pred.next = new Node<K,V>(hash, key,
+ value, null);
break;
}
}
@@ -1240,8 +1245,7 @@
@dalvik.annotation.codegen.CovariantReturnType(returnType = KeySetView.class, presentAfter = 28)
public Set<K> keySet() {
KeySetView<K,V> ks;
- if ((ks = keySet) != null) return ks;
- return keySet = new KeySetView<K,V>(this, null);
+ return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
}
/**
@@ -1264,8 +1268,7 @@
*/
public Collection<V> values() {
ValuesView<K,V> vs;
- if ((vs = values) != null) return vs;
- return values = new ValuesView<K,V>(this);
+ return (vs = values) != null ? vs : (values = new ValuesView<K,V>(this));
}
/**
@@ -1287,8 +1290,7 @@
*/
public Set<Map.Entry<K,V>> entrySet() {
EntrySetView<K,V> es;
- if ((es = entrySet) != null) return es;
- return entrySet = new EntrySetView<K,V>(this);
+ return (es = entrySet) != null ? es : (entrySet = new EntrySetView<K,V>(this));
}
/**
@@ -1389,8 +1391,8 @@
}
/**
- * Saves this map to a stream (that is, serializes it).
- *
+ * Saves the state of the {@code ConcurrentHashMap} instance to a
+ * stream (i.e., serializes it).
* @param s the stream
* @throws java.io.IOException if an I/O error occurs
* @serialData
@@ -1434,7 +1436,7 @@
}
/**
- * Reconstitutes this map from a stream (that is, deserializes it).
+ * Reconstitutes the instance from a stream (that is, deserializes it).
* @param s the stream
* @throws ClassNotFoundException if the class of a serialized object
* could not be found
@@ -1468,9 +1470,13 @@
if (size == 0L)
sizeCtl = 0;
else {
- long ts = (long)(1.0 + size / LOAD_FACTOR);
- int n = (ts >= (long)MAXIMUM_CAPACITY) ?
- MAXIMUM_CAPACITY : tableSizeFor((int)ts);
+ int n;
+ if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
+ n = MAXIMUM_CAPACITY;
+ else {
+ int sz = (int)size;
+ n = tableSizeFor(sz + (sz >>> 1) + 1);
+ }
@SuppressWarnings("unchecked")
Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
int mask = n - 1;
@@ -1697,7 +1703,7 @@
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh; K fk; V fv;
+ Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
@@ -1708,7 +1714,7 @@
Node<K,V> node = null;
try {
if ((val = mappingFunction.apply(key)) != null)
- node = new Node<K,V>(h, key, val);
+ node = new Node<K,V>(h, key, val, null);
} finally {
setTabAt(tab, i, node);
}
@@ -1719,10 +1725,6 @@
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
- else if (fh == h // check first node without acquiring lock
- && ((fk = f.key) == key || (fk != null && key.equals(fk)))
- && (fv = f.val) != null)
- return fv;
else {
boolean added = false;
synchronized (f) {
@@ -1743,7 +1745,7 @@
if (pred.next != null)
throw new IllegalStateException("Recursive update");
added = true;
- pred.next = new Node<K,V>(h, key, val);
+ pred.next = new Node<K,V>(h, key, val, null);
}
break;
}
@@ -1912,7 +1914,7 @@
try {
if ((val = remappingFunction.apply(key, null)) != null) {
delta = 1;
- node = new Node<K,V>(h, key, val);
+ node = new Node<K,V>(h, key, val, null);
}
} finally {
setTabAt(tab, i, node);
@@ -1954,7 +1956,8 @@
if (pred.next != null)
throw new IllegalStateException("Recursive update");
delta = 1;
- pred.next = new Node<K,V>(h, key, val);
+ pred.next =
+ new Node<K,V>(h, key, val, null);
}
break;
}
@@ -2032,7 +2035,7 @@
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
- if (casTabAt(tab, i, null, new Node<K,V>(h, key, value))) {
+ if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
delta = 1;
val = value;
break;
@@ -2067,7 +2070,8 @@
if ((e = e.next) == null) {
delta = 1;
val = value;
- pred.next = new Node<K,V>(h, key, val);
+ pred.next =
+ new Node<K,V>(h, key, val, null);
break;
}
}
@@ -2228,7 +2232,7 @@
static final class ForwardingNode<K,V> extends Node<K,V> {
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab) {
- super(MOVED, null, null);
+ super(MOVED, null, null, null);
this.nextTable = tab;
}
@@ -2264,7 +2268,7 @@
*/
static final class ReservationNode<K,V> extends Node<K,V> {
ReservationNode() {
- super(RESERVED, null, null);
+ super(RESERVED, null, null, null);
}
Node<K,V> find(int h, Object k) {
@@ -2290,7 +2294,7 @@
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
- else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
+ else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@@ -2319,15 +2323,15 @@
* @param check if <0, don't check resize, if <= 1 only check if uncontended
*/
private final void addCount(long x, int check) {
- CounterCell[] cs; long b, s;
- if ((cs = counterCells) != null ||
- !U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
- CounterCell c; long v; int m;
+ CounterCell[] as; long b, s;
+ if ((as = counterCells) != null ||
+ !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
+ CounterCell a; long v; int m;
boolean uncontended = true;
- if (cs == null || (m = cs.length - 1) < 0 ||
- (c = cs[ThreadLocalRandom.getProbe() & m]) == null ||
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
- U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))) {
+ U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
fullAddCount(x, uncontended);
return;
}
@@ -2345,10 +2349,10 @@
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
- if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1))
+ if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
- else if (U.compareAndSetInt(this, SIZECTL, sc,
+ else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
@@ -2369,7 +2373,7 @@
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
- if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1)) {
+ if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
@@ -2392,7 +2396,7 @@
Node<K,V>[] tab = table; int n;
if (tab == null || (n = tab.length) == 0) {
n = (sc > c) ? sc : c;
- if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
+ if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if (table == tab) {
@SuppressWarnings("unchecked")
@@ -2409,7 +2413,7 @@
break;
else if (tab == table) {
int rs = resizeStamp(n);
- if (U.compareAndSetInt(this, SIZECTL, sc,
+ if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
}
@@ -2450,7 +2454,7 @@
i = -1;
advance = false;
}
- else if (U.compareAndSetInt
+ else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
@@ -2467,7 +2471,7 @@
sizeCtl = (n << 1) - (n >>> 1);
return;
}
- if (U.compareAndSetInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
+ if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
@@ -2567,12 +2571,13 @@
}
final long sumCount() {
- CounterCell[] cs = counterCells;
+ CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
- if (cs != null) {
- for (CounterCell c : cs)
- if (c != null)
- sum += c.value;
+ if (as != null) {
+ for (int i = 0; i < as.length; ++i) {
+ if ((a = as[i]) != null)
+ sum += a.value;
+ }
}
return sum;
}
@@ -2587,13 +2592,13 @@
}
boolean collide = false; // True if last slot nonempty
for (;;) {
- CounterCell[] cs; CounterCell c; int n; long v;
- if ((cs = counterCells) != null && (n = cs.length) > 0) {
- if ((c = cs[(n - 1) & h]) == null) {
+ CounterCell[] as; CounterCell a; int n; long v;
+ if ((as = counterCells) != null && (n = as.length) > 0) {
+ if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
CounterCell r = new CounterCell(x); // Optimistic create
if (cellsBusy == 0 &&
- U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
@@ -2615,17 +2620,21 @@
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
- else if (U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))
+ else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
- else if (counterCells != cs || n >= NCPU)
+ else if (counterCells != as || n >= NCPU)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 &&
- U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
- if (counterCells == cs) // Expand table unless stale
- counterCells = Arrays.copyOf(cs, n << 1);
+ if (counterCells == as) {// Expand table unless stale
+ CounterCell[] rs = new CounterCell[n << 1];
+ for (int i = 0; i < n; ++i)
+ rs[i] = as[i];
+ counterCells = rs;
+ }
} finally {
cellsBusy = 0;
}
@@ -2634,11 +2643,11 @@
}
h = ThreadLocalRandom.advanceProbe(h);
}
- else if (cellsBusy == 0 && counterCells == cs &&
- U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
+ else if (cellsBusy == 0 && counterCells == as &&
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // Initialize table
- if (counterCells == cs) {
+ if (counterCells == as) {
CounterCell[] rs = new CounterCell[2];
rs[h & 1] = new CounterCell(x);
counterCells = rs;
@@ -2650,7 +2659,7 @@
if (init)
break;
}
- else if (U.compareAndSetLong(this, BASECOUNT, v = baseCount, v + x))
+ else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // Fall back on using base
}
}
@@ -2688,12 +2697,12 @@
}
/**
- * Returns a list of non-TreeNodes replacing those in given list.
+ * Returns a list on non-TreeNodes replacing those in given list.
*/
static <K,V> Node<K,V> untreeify(Node<K,V> b) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = b; q != null; q = q.next) {
- Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val);
+ Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
if (tl == null)
hd = p;
else
@@ -2799,7 +2808,7 @@
* Creates bin with initial set of nodes headed by b.
*/
TreeBin(TreeNode<K,V> b) {
- super(TREEBIN, null, null);
+ super(TREEBIN, null, null, null);
this.first = b;
TreeNode<K,V> r = null;
for (TreeNode<K,V> x = b, next; x != null; x = next) {
@@ -2846,7 +2855,7 @@
* Acquires write lock for tree restructuring.
*/
private final void lockRoot() {
- if (!U.compareAndSetInt(this, LOCKSTATE, 0, WRITER))
+ if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
contendedLock(); // offload to separate method
}
@@ -2864,14 +2873,14 @@
boolean waiting = false;
for (int s;;) {
if (((s = lockState) & ~WAITER) == 0) {
- if (U.compareAndSetInt(this, LOCKSTATE, s, WRITER)) {
+ if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
if (waiting)
waiter = null;
return;
}
}
else if ((s & WAITER) == 0) {
- if (U.compareAndSetInt(this, LOCKSTATE, s, s | WAITER)) {
+ if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
waiting = true;
waiter = Thread.currentThread();
}
@@ -2896,7 +2905,7 @@
return e;
e = e.next;
}
- else if (U.compareAndSetInt(this, LOCKSTATE, s,
+ else if (U.compareAndSwapInt(this, LOCKSTATE, s,
s + READER)) {
TreeNode<K,V> r, p;
try {
@@ -3293,9 +3302,16 @@
return true;
}
- private static final Unsafe U = Unsafe.getUnsafe();
- private static final long LOCKSTATE
- = U.objectFieldOffset(TreeBin.class, "lockState");
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long LOCKSTATE;
+ static {
+ try {
+ LOCKSTATE = U.objectFieldOffset
+ (TreeBin.class.getDeclaredField("lockState"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
}
/* ----------------Table Traversal -------------- */
@@ -3449,9 +3465,9 @@
static final class KeyIterator<K,V> extends BaseIterator<K,V>
implements Iterator<K>, Enumeration<K> {
- KeyIterator(Node<K,V>[] tab, int size, int index, int limit,
+ KeyIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
- super(tab, size, index, limit, map);
+ super(tab, index, size, limit, map);
}
public final K next() {
@@ -3469,9 +3485,9 @@
static final class ValueIterator<K,V> extends BaseIterator<K,V>
implements Iterator<V>, Enumeration<V> {
- ValueIterator(Node<K,V>[] tab, int size, int index, int limit,
+ ValueIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
- super(tab, size, index, limit, map);
+ super(tab, index, size, limit, map);
}
public final V next() {
@@ -3489,9 +3505,9 @@
static final class EntryIterator<K,V> extends BaseIterator<K,V>
implements Iterator<Map.Entry<K,V>> {
- EntryIterator(Node<K,V>[] tab, int size, int index, int limit,
+ EntryIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
- super(tab, size, index, limit, map);
+ super(tab, index, size, limit, map);
}
public final Map.Entry<K,V> next() {
@@ -4542,24 +4558,14 @@
return true;
}
- public boolean removeAll(Collection<?> c) {
+ public final boolean removeAll(Collection<?> c) {
if (c == null) throw new NullPointerException();
boolean modified = false;
- // Use (c instanceof Set) as a hint that lookup in c is as
- // efficient as this view
- Node<K,V>[] t;
- if ((t = map.table) == null) {
- return false;
- } else if (c instanceof Set<?> && c.size() > t.length) {
- for (Iterator<?> it = iterator(); it.hasNext(); ) {
- if (c.contains(it.next())) {
- it.remove();
- modified = true;
- }
+ for (Iterator<E> it = iterator(); it.hasNext();) {
+ if (c.contains(it.next())) {
+ it.remove();
+ modified = true;
}
- } else {
- for (Object e : c)
- modified |= remove(e);
}
return modified;
}
@@ -4745,18 +4751,6 @@
throw new UnsupportedOperationException();
}
- @Override public boolean removeAll(Collection<?> c) {
- if (c == null) throw new NullPointerException();
- boolean modified = false;
- for (Iterator<V> it = iterator(); it.hasNext();) {
- if (c.contains(it.next())) {
- it.remove();
- modified = true;
- }
- }
- return modified;
- }
-
public boolean removeIf(Predicate<? super V> filter) {
return map.removeValueIf(filter);
}
@@ -6350,7 +6344,7 @@
}
// Unsafe mechanics
- private static final Unsafe U = Unsafe.getUnsafe();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
@@ -6360,29 +6354,30 @@
private static final int ASHIFT;
static {
- SIZECTL = U.objectFieldOffset
- (ConcurrentHashMap.class, "sizeCtl");
- TRANSFERINDEX = U.objectFieldOffset
- (ConcurrentHashMap.class, "transferIndex");
- BASECOUNT = U.objectFieldOffset
- (ConcurrentHashMap.class, "baseCount");
- CELLSBUSY = U.objectFieldOffset
- (ConcurrentHashMap.class, "cellsBusy");
+ try {
+ SIZECTL = U.objectFieldOffset
+ (ConcurrentHashMap.class.getDeclaredField("sizeCtl"));
+ TRANSFERINDEX = U.objectFieldOffset
+ (ConcurrentHashMap.class.getDeclaredField("transferIndex"));
+ BASECOUNT = U.objectFieldOffset
+ (ConcurrentHashMap.class.getDeclaredField("baseCount"));
+ CELLSBUSY = U.objectFieldOffset
+ (ConcurrentHashMap.class.getDeclaredField("cellsBusy"));
- CELLVALUE = U.objectFieldOffset
- (CounterCell.class, "value");
+ CELLVALUE = U.objectFieldOffset
+ (CounterCell.class.getDeclaredField("value"));
- ABASE = U.arrayBaseOffset(Node[].class);
- int scale = U.arrayIndexScale(Node[].class);
- if ((scale & (scale - 1)) != 0)
- throw new ExceptionInInitializerError("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ ABASE = U.arrayBaseOffset(Node[].class);
+ int scale = U.arrayIndexScale(Node[].class);
+ if ((scale & (scale - 1)) != 0)
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
// Reduce the risk of rare disastrous classloading in first call to
// LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
Class<?> ensureLoaded = LockSupport.class;
-
- // Eager class load observed to help JIT during startup
- ensureLoaded = ReservationNode.class;
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
index 91ddabd..3edde54 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
@@ -48,7 +46,10 @@
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An unbounded concurrent {@linkplain Deque deque} based on linked nodes.
@@ -67,12 +68,12 @@
* asynchronous nature of these deques, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
- *
- * <p>Bulk operations that add, remove, or examine multiple elements,
- * such as {@link #addAll}, {@link #removeIf} or {@link #forEach},
- * are <em>not</em> guaranteed to be performed atomically.
- * For example, a {@code forEach} traversal concurrent with an {@code
- * addAll} operation might observe only some of the added elements.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Deque} and {@link Iterator} interfaces.
@@ -84,10 +85,6 @@
* actions subsequent to the access or removal of that element from
* the {@code ConcurrentLinkedDeque} in another thread.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.7
* @author Doug Lea
* @author Martin Buchholz
@@ -231,16 +228,15 @@
*
* The implementation is completely directionally symmetrical,
* except that most public methods that iterate through the list
- * follow next pointers, in the "forward" direction.
+ * follow next pointers ("forward" direction).
*
- * We believe (without full proof) that all single-element Deque
- * operations that operate directly at the two ends of the Deque
- * (e.g., addFirst, peekLast, pollLast) are linearizable (see
- * Herlihy and Shavit's book). However, some combinations of
+ * We believe (without full proof) that all single-element deque
+ * operations (e.g., addFirst, peekLast, pollLast) are linearizable
+ * (see Herlihy and Shavit's book). However, some combinations of
* operations are known not to be linearizable. In particular,
- * when an addFirst(A) is racing with pollFirst() removing B, it
- * is possible for an observer iterating over the elements to
- * observe first [A B C] and then [A C], even though no interior
+ * when an addFirst(A) is racing with pollFirst() removing B, it is
+ * possible for an observer iterating over the elements to observe
+ * A B C and subsequently observe A C, even though no interior
* removes are ever performed. Nevertheless, iterators behave
* reasonably, providing the "weakly consistent" guarantees.
*
@@ -296,23 +292,64 @@
volatile Node<E> prev;
volatile E item;
volatile Node<E> next;
- }
- /**
- * Returns a new node holding item. Uses relaxed write because item
- * can only be seen after piggy-backing publication via CAS.
- */
- static <E> Node<E> newNode(E item) {
- Node<E> node = new Node<E>();
- ITEM.set(node, item);
- return node;
+ Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
+ }
+
+ /**
+ * Constructs a new node. Uses relaxed write because item can
+ * only be seen after publication via casNext or casPrev.
+ */
+ Node(E item) {
+ U.putObject(this, ITEM, item);
+ }
+
+ boolean casItem(E cmp, E val) {
+ return U.compareAndSwapObject(this, ITEM, cmp, val);
+ }
+
+ void lazySetNext(Node<E> val) {
+ U.putOrderedObject(this, NEXT, val);
+ }
+
+ boolean casNext(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
+ }
+
+ void lazySetPrev(Node<E> val) {
+ U.putOrderedObject(this, PREV, val);
+ }
+
+ boolean casPrev(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, PREV, cmp, val);
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PREV;
+ private static final long ITEM;
+ private static final long NEXT;
+
+ static {
+ try {
+ PREV = U.objectFieldOffset
+ (Node.class.getDeclaredField("prev"));
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
- final Node<E> newNode = newNode(Objects.requireNonNull(e));
+ final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
restartFromHead:
for (;;)
@@ -326,13 +363,13 @@
continue restartFromHead;
else {
// p is first node
- NEXT.set(newNode, p); // CAS piggyback
- if (PREV.compareAndSet(p, null, newNode)) {
+ newNode.lazySetNext(p); // CAS piggyback
+ if (p.casPrev(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
- if (p != h) // hop two nodes at a time; failure is OK
- HEAD.weakCompareAndSet(this, h, newNode);
+ if (p != h) // hop two nodes at a time
+ casHead(h, newNode); // Failure is OK.
return;
}
// Lost CAS race to another thread; re-read prev
@@ -344,7 +381,7 @@
* Links e as last element.
*/
private void linkLast(E e) {
- final Node<E> newNode = newNode(Objects.requireNonNull(e));
+ final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
restartFromTail:
for (;;)
@@ -358,13 +395,13 @@
continue restartFromTail;
else {
// p is last node
- PREV.set(newNode, p); // CAS piggyback
- if (NEXT.compareAndSet(p, null, newNode)) {
+ newNode.lazySetPrev(p); // CAS piggyback
+ if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
- if (p != t) // hop two nodes at a time; failure is OK
- TAIL.weakCompareAndSet(this, t, newNode);
+ if (p != t) // hop two nodes at a time
+ casTail(t, newNode); // Failure is OK.
return;
}
// Lost CAS race to another thread; re-read next
@@ -479,8 +516,8 @@
updateTail(); // Ensure x is not reachable from tail
// Finally, actually gc-unlink
- PREV.setRelease(x, isFirst ? prevTerminator() : x);
- NEXT.setRelease(x, isLast ? nextTerminator() : x);
+ x.lazySetPrev(isFirst ? prevTerminator() : x);
+ x.lazySetNext(isLast ? nextTerminator() : x);
}
}
}
@@ -494,8 +531,7 @@
// assert first.item == null;
for (Node<E> o = null, p = next, q;;) {
if (p.item != null || (q = p.next) == null) {
- if (o != null && p.prev != p &&
- NEXT.compareAndSet(first, next, p)) {
+ if (o != null && p.prev != p && first.casNext(next, p)) {
skipDeletedPredecessors(p);
if (first.prev == null &&
(p.next == null || p.item != null) &&
@@ -505,8 +541,8 @@
updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink
- NEXT.setRelease(o, o);
- PREV.setRelease(o, prevTerminator());
+ o.lazySetNext(o);
+ o.lazySetPrev(prevTerminator());
}
}
return;
@@ -529,8 +565,7 @@
// assert last.item == null;
for (Node<E> o = null, p = prev, q;;) {
if (p.item != null || (q = p.prev) == null) {
- if (o != null && p.next != p &&
- PREV.compareAndSet(last, prev, p)) {
+ if (o != null && p.next != p && last.casPrev(prev, p)) {
skipDeletedSuccessors(p);
if (last.next == null &&
(p.prev == null || p.item != null) &&
@@ -540,8 +575,8 @@
updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink
- PREV.setRelease(o, o);
- NEXT.setRelease(o, nextTerminator());
+ o.lazySetPrev(o);
+ o.lazySetNext(nextTerminator());
}
}
return;
@@ -572,7 +607,7 @@
(q = (p = q).prev) == null) {
// It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- if (HEAD.compareAndSet(this, h, p))
+ if (casHead(h, p))
return;
else
continue restartFromHead;
@@ -602,7 +637,7 @@
(q = (p = q).next) == null) {
// It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- if (TAIL.compareAndSet(this, t, p))
+ if (casTail(t, p))
return;
else
continue restartFromTail;
@@ -640,7 +675,7 @@
}
// found active CAS target
- if (prev == p || PREV.compareAndSet(x, prev, p))
+ if (prev == p || x.casPrev(prev, p))
return;
} while (x.item != null || x.next == null);
@@ -671,7 +706,7 @@
}
// found active CAS target
- if (next == p || NEXT.compareAndSet(x, next, p))
+ if (next == p || x.casNext(next, p))
return;
} while (x.item != null || x.prev == null);
@@ -684,9 +719,8 @@
*/
final Node<E> succ(Node<E> p) {
// TODO: should we skip deleted nodes here?
- if (p == (p = p.next))
- p = first();
- return p;
+ Node<E> q = p.next;
+ return (p == q) ? first() : q;
}
/**
@@ -695,9 +729,8 @@
* stale pointer that is now off the list.
*/
final Node<E> pred(Node<E> p) {
- if (p == (p = p.prev))
- p = last();
- return p;
+ Node<E> q = p.prev;
+ return (p == q) ? last() : q;
}
/**
@@ -718,7 +751,7 @@
else if (p == h
// It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- || HEAD.compareAndSet(this, h, p))
+ || casHead(h, p))
return p;
else
continue restartFromHead;
@@ -743,7 +776,7 @@
else if (p == t
// It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- || TAIL.compareAndSet(this, t, p))
+ || casTail(t, p))
return p;
else
continue restartFromTail;
@@ -769,7 +802,7 @@
* Constructs an empty deque.
*/
public ConcurrentLinkedDeque() {
- head = tail = new Node<E>();
+ head = tail = new Node<E>(null);
}
/**
@@ -785,12 +818,12 @@
// Copy c into a private chain of Nodes
Node<E> h = null, t = null;
for (E e : c) {
- Node<E> newNode = newNode(Objects.requireNonNull(e));
+ Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
if (h == null)
h = t = newNode;
else {
- NEXT.set(t, newNode);
- PREV.set(newNode, t);
+ t.lazySetNext(newNode);
+ newNode.lazySetPrev(t);
t = newNode;
}
}
@@ -803,12 +836,12 @@
private void initHeadTail(Node<E> h, Node<E> t) {
if (h == t) {
if (h == null)
- h = t = new Node<E>();
+ h = t = new Node<E>(null);
else {
// Avoid edge case of a single Node with non-null item.
- Node<E> newNode = new Node<E>();
- NEXT.set(t, newNode);
- PREV.set(newNode, t);
+ Node<E> newNode = new Node<E>(null);
+ t.lazySetNext(newNode);
+ newNode.lazySetPrev(t);
t = newNode;
}
}
@@ -867,33 +900,21 @@
}
public E peekFirst() {
- restart: for (;;) {
- E item;
- Node<E> first = first(), p = first;
- while ((item = p.item) == null) {
- if (p == (p = p.next)) continue restart;
- if (p == null)
- break;
- }
- // recheck for linearizability
- if (first.prev != null) continue restart;
- return item;
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null)
+ return item;
}
+ return null;
}
public E peekLast() {
- restart: for (;;) {
- E item;
- Node<E> last = last(), p = last;
- while ((item = p.item) == null) {
- if (p == (p = p.prev)) continue restart;
- if (p == null)
- break;
- }
- // recheck for linearizability
- if (last.next != null) continue restart;
- return item;
+ for (Node<E> p = last(); p != null; p = pred(p)) {
+ E item = p.item;
+ if (item != null)
+ return item;
}
+ return null;
}
/**
@@ -911,45 +932,25 @@
}
public E pollFirst() {
- restart: for (;;) {
- for (Node<E> first = first(), p = first;;) {
- final E item;
- if ((item = p.item) != null) {
- // recheck for linearizability
- if (first.prev != null) continue restart;
- if (ITEM.compareAndSet(p, item, null)) {
- unlink(p);
- return item;
- }
- }
- if (p == (p = p.next)) continue restart;
- if (p == null) {
- if (first.prev != null) continue restart;
- return null;
- }
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null && p.casItem(item, null)) {
+ unlink(p);
+ return item;
}
}
+ return null;
}
public E pollLast() {
- restart: for (;;) {
- for (Node<E> last = last(), p = last;;) {
- final E item;
- if ((item = p.item) != null) {
- // recheck for linearizability
- if (last.next != null) continue restart;
- if (ITEM.compareAndSet(p, item, null)) {
- unlink(p);
- return item;
- }
- }
- if (p == (p = p.prev)) continue restart;
- if (p == null) {
- if (last.next != null) continue restart;
- return null;
- }
+ for (Node<E> p = last(); p != null; p = pred(p)) {
+ E item = p.item;
+ if (item != null && p.casItem(item, null)) {
+ unlink(p);
+ return item;
}
}
+ return null;
}
/**
@@ -1029,10 +1030,8 @@
public boolean removeFirstOccurrence(Object o) {
Objects.requireNonNull(o);
for (Node<E> p = first(); p != null; p = succ(p)) {
- final E item;
- if ((item = p.item) != null
- && o.equals(item)
- && ITEM.compareAndSet(p, item, null)) {
+ E item = p.item;
+ if (item != null && o.equals(item) && p.casItem(item, null)) {
unlink(p);
return true;
}
@@ -1055,10 +1054,8 @@
public boolean removeLastOccurrence(Object o) {
Objects.requireNonNull(o);
for (Node<E> p = last(); p != null; p = pred(p)) {
- final E item;
- if ((item = p.item) != null
- && o.equals(item)
- && ITEM.compareAndSet(p, item, null)) {
+ E item = p.item;
+ if (item != null && o.equals(item) && p.casItem(item, null)) {
unlink(p);
return true;
}
@@ -1077,8 +1074,8 @@
public boolean contains(Object o) {
if (o != null) {
for (Node<E> p = first(); p != null; p = succ(p)) {
- final E item;
- if ((item = p.item) != null && o.equals(item))
+ E item = p.item;
+ if (item != null && o.equals(item))
return true;
}
}
@@ -1111,14 +1108,14 @@
* @return the number of elements in this deque
*/
public int size() {
- restart: for (;;) {
+ restartFromHead: for (;;) {
int count = 0;
for (Node<E> p = first(); p != null;) {
if (p.item != null)
if (++count == Integer.MAX_VALUE)
break; // @see Collection.size()
if (p == (p = p.next))
- continue restart;
+ continue restartFromHead;
}
return count;
}
@@ -1162,12 +1159,12 @@
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
- Node<E> newNode = newNode(Objects.requireNonNull(e));
+ Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
- NEXT.set(last, newNode);
- PREV.set(newNode, last);
+ last.lazySetNext(newNode);
+ newNode.lazySetPrev(last);
last = newNode;
}
}
@@ -1187,16 +1184,16 @@
continue restartFromTail;
else {
// p is last node
- PREV.set(beginningOfTheEnd, p); // CAS piggyback
- if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
+ beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
+ if (p.casNext(null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this deque.
- if (!TAIL.weakCompareAndSet(this, t, last)) {
+ if (!casTail(t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
- TAIL.weakCompareAndSet(this, t, last);
+ casTail(t, last);
}
return true;
}
@@ -1215,12 +1212,12 @@
public String toString() {
String[] a = null;
- restart: for (;;) {
+ restartFromHead: for (;;) {
int charLength = 0;
int size = 0;
for (Node<E> p = first(); p != null;) {
- final E item;
- if ((item = p.item) != null) {
+ E item = p.item;
+ if (item != null) {
if (a == null)
a = new String[4];
else if (size == a.length)
@@ -1230,7 +1227,7 @@
charLength += s.length();
}
if (p == (p = p.next))
- continue restart;
+ continue restartFromHead;
}
if (size == 0)
@@ -1242,11 +1239,11 @@
private Object[] toArrayInternal(Object[] a) {
Object[] x = a;
- restart: for (;;) {
+ restartFromHead: for (;;) {
int size = 0;
for (Node<E> p = first(); p != null;) {
- final E item;
- if ((item = p.item) != null) {
+ E item = p.item;
+ if (item != null) {
if (x == null)
x = new Object[4];
else if (size == x.length)
@@ -1254,7 +1251,7 @@
x[size++] = item;
}
if (p == (p = p.next))
- continue restart;
+ continue restartFromHead;
}
if (x == null)
return new Object[0];
@@ -1398,8 +1395,8 @@
nextItem = null;
break;
}
- final E item;
- if ((item = p.item) != null) {
+ E item = p.item;
+ if (item != null) {
nextNode = p;
nextItem = item;
break;
@@ -1429,75 +1426,90 @@
/** Forward iterator */
private class Itr extends AbstractItr {
- Itr() {} // prevent access constructor creation
Node<E> startNode() { return first(); }
Node<E> nextNode(Node<E> p) { return succ(p); }
}
/** Descending iterator */
private class DescendingItr extends AbstractItr {
- DescendingItr() {} // prevent access constructor creation
Node<E> startNode() { return last(); }
Node<E> nextNode(Node<E> p) { return pred(p); }
}
/** A customized variant of Spliterators.IteratorSpliterator */
- final class CLDSpliterator implements Spliterator<E> {
+ static final class CLDSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedDeque<E> queue;
Node<E> current; // current node; null until initialized
int batch; // batch size for splits
boolean exhausted; // true when no more nodes
+ CLDSpliterator(ConcurrentLinkedDeque<E> queue) {
+ this.queue = queue;
+ }
public Spliterator<E> trySplit() {
- Node<E> p, q;
- if ((p = current()) == null || (q = p.next) == null)
- return null;
- int i = 0, n = batch = Math.min(batch + 1, MAX_BATCH);
- Object[] a = null;
- do {
- final E e;
- if ((e = p.item) != null) {
- if (a == null)
- a = new Object[n];
- a[i++] = e;
+ Node<E> p;
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ if (p.item == null && p == (p = p.next))
+ current = p = q.first();
+ if (p != null && p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
}
- if (p == (p = q))
- p = first();
- } while (p != null && (q = p.next) != null && i < n);
- setCurrent(p);
- return (i == 0) ? null :
- Spliterators.spliterator(a, 0, i, (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT));
+ }
+ return null;
}
public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
Node<E> p;
- if ((p = current()) != null) {
- current = null;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
exhausted = true;
do {
- final E e;
- if ((e = p.item) != null)
- action.accept(e);
+ E e = p.item;
if (p == (p = p.next))
- p = first();
+ p = q.first();
+ if (e != null)
+ action.accept(e);
} while (p != null);
}
}
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
Node<E> p;
- if ((p = current()) != null) {
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
E e;
do {
e = p.item;
if (p == (p = p.next))
- p = first();
+ p = q.first();
} while (e == null && p != null);
- setCurrent(p);
+ if ((current = p) == null)
+ exhausted = true;
if (e != null) {
action.accept(e);
return true;
@@ -1506,24 +1518,11 @@
return false;
}
- private void setCurrent(Node<E> p) {
- if ((current = p) == null)
- exhausted = true;
- }
-
- private Node<E> current() {
- Node<E> p;
- if ((p = current) == null && !exhausted)
- setCurrent(p = first());
- return p;
- }
-
public long estimateSize() { return Long.MAX_VALUE; }
public int characteristics() {
- return (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT);
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
}
}
@@ -1544,7 +1543,7 @@
* @since 1.8
*/
public Spliterator<E> spliterator() {
- return new CLDSpliterator();
+ return new CLDSpliterator<E>(this);
}
/**
@@ -1563,8 +1562,8 @@
// Write out all elements in the proper order.
for (Node<E> p = first(); p != null; p = succ(p)) {
- final E item;
- if ((item = p.item) != null)
+ E item = p.item;
+ if (item != null)
s.writeObject(item);
}
@@ -1587,91 +1586,43 @@
Node<E> h = null, t = null;
for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
- Node<E> newNode = newNode((E) item);
+ Node<E> newNode = new Node<E>((E) item);
if (h == null)
h = t = newNode;
else {
- NEXT.set(t, newNode);
- PREV.set(newNode, t);
+ t.lazySetNext(newNode);
+ newNode.lazySetPrev(t);
t = newNode;
}
}
initHeadTail(h, t);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
+ private boolean casHead(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
+ private boolean casTail(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
+ // Unsafe mechanics
- /** Implementation of bulk remove methods. */
- private boolean bulkRemove(Predicate<? super E> filter) {
- boolean removed = false;
- for (Node<E> p = first(), succ; p != null; p = succ) {
- succ = succ(p);
- final E item;
- if ((item = p.item) != null
- && filter.test(item)
- && ITEM.compareAndSet(p, item, null)) {
- unlink(p);
- removed = true;
- }
- }
- return removed;
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- E item;
- for (Node<E> p = first(); p != null; p = succ(p))
- if ((item = p.item) != null)
- action.accept(item);
- }
-
- // VarHandle mechanics
- private static final VarHandle HEAD;
- private static final VarHandle TAIL;
- private static final VarHandle PREV;
- private static final VarHandle NEXT;
- private static final VarHandle ITEM;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
static {
PREV_TERMINATOR = new Node<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>();
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head",
- Node.class);
- TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail",
- Node.class);
- PREV = l.findVarHandle(Node.class, "prev", Node.class);
- NEXT = l.findVarHandle(Node.class, "next", Node.class);
- ITEM = l.findVarHandle(Node.class, "item", Object.class);
+ HEAD = U.objectFieldOffset
+ (ConcurrentLinkedDeque.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (ConcurrentLinkedDeque.class.getDeclaredField("tail"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index eac8ee7..7997c60 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@@ -47,7 +45,10 @@
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
@@ -81,12 +82,12 @@
* asynchronous nature of these queues, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
- *
- * <p>Bulk operations that add, remove, or examine multiple elements,
- * such as {@link #addAll}, {@link #removeIf} or {@link #forEach},
- * are <em>not</em> guaranteed to be performed atomically.
- * For example, a {@code forEach} traversal concurrent with an {@code
- * addAll} operation might observe only some of the added elements.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterator implement all of the <em>optional</em>
* methods of the {@link Queue} and {@link Iterator} interfaces.
@@ -98,10 +99,6 @@
* actions subsequent to the access or removal of that element from
* the {@code ConcurrentLinkedQueue} in another thread.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this queue
@@ -113,7 +110,7 @@
/*
* This is a modification of the Michael & Scott algorithm,
* adapted for a garbage-collected environment, with support for
- * interior node deletion (to support e.g. remove(Object)). For
+ * interior node deletion (to support remove(Object)). For
* explanation, read the paper.
*
* Note that like most non-blocking algorithms in this package,
@@ -161,17 +158,17 @@
* it is possible for tail to lag behind head (why not)?
*
* CASing a Node's item reference to null atomically removes the
- * element from the queue, leaving a "dead" node that should later
- * be unlinked (but unlinking is merely an optimization).
- * Interior element removal methods (other than Iterator.remove())
- * keep track of the predecessor node during traversal so that the
- * node can be CAS-unlinked. Some traversal methods try to unlink
- * any deleted nodes encountered during traversal. See comments
- * in bulkRemove.
+ * element from the queue. Iterators skip over Nodes with null
+ * items. Prior implementations of this class had a race between
+ * poll() and remove(Object) where the same element would appear
+ * to be successfully removed by two concurrent operations. The
+ * method remove(Object) also lazily unlinks deleted Nodes, but
+ * this is merely an optimization.
*
* When constructing a Node (before enqueuing it) we avoid paying
- * for a volatile write to item. This allows the cost of enqueue
- * to be "one-and-a-half" CASes.
+ * for a volatile write to item by using Unsafe.putObject instead
+ * of a normal write. This allows the cost of enqueue to be
+ * "one-and-a-half" CASes.
*
* Both head and tail may or may not point to a Node with a
* non-null item. If the queue is empty, all items must of course
@@ -181,33 +178,31 @@
* optimization.
*/
- static final class Node<E> {
+ private static class Node<E> {
volatile E item;
volatile Node<E> next;
+ }
- /**
- * Constructs a node holding item. Uses relaxed write because
- * item can only be seen after piggy-backing publication via CAS.
- */
- Node(E item) {
- ITEM.set(this, item);
- }
+ /**
+ * Returns a new node holding item. Uses relaxed write because item
+ * can only be seen after piggy-backing publication via casNext.
+ */
+ static <E> Node<E> newNode(E item) {
+ Node<E> node = new Node<E>();
+ U.putObject(node, ITEM, item);
+ return node;
+ }
- /** Constructs a dead dummy node. */
- Node() {}
+ static <E> boolean casItem(Node<E> node, E cmp, E val) {
+ return U.compareAndSwapObject(node, ITEM, cmp, val);
+ }
- void appendRelaxed(Node<E> next) {
- // assert next != null;
- // assert this.next == null;
- NEXT.set(this, next);
- }
+ static <E> void lazySetNext(Node<E> node, Node<E> val) {
+ U.putOrderedObject(node, NEXT, val);
+ }
- boolean casItem(E cmp, E val) {
- // assert item == cmp || item == null;
- // assert cmp != null;
- // assert val == null;
- return ITEM.compareAndSet(this, cmp, val);
- }
+ static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(node, NEXT, cmp, val);
}
/**
@@ -234,7 +229,7 @@
* - tail.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
- * - tail.next may or may not be self-linked.
+ * - tail.next may or may not be self-pointing to tail.
*/
private transient volatile Node<E> tail;
@@ -242,7 +237,7 @@
* Creates a {@code ConcurrentLinkedQueue} that is initially empty.
*/
public ConcurrentLinkedQueue() {
- head = tail = new Node<E>();
+ head = tail = newNode(null);
}
/**
@@ -257,14 +252,16 @@
public ConcurrentLinkedQueue(Collection<? extends E> c) {
Node<E> h = null, t = null;
for (E e : c) {
- Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (h == null)
h = t = newNode;
- else
- t.appendRelaxed(t = newNode);
+ else {
+ lazySetNext(t, newNode);
+ t = newNode;
+ }
}
if (h == null)
- h = t = new Node<E>();
+ h = t = newNode(null);
head = h;
tail = t;
}
@@ -289,8 +286,8 @@
*/
final void updateHead(Node<E> h, Node<E> p) {
// assert h != null && p != null && (h == p || h.item == null);
- if (h != p && HEAD.compareAndSet(this, h, p))
- NEXT.setRelease(h, h);
+ if (h != p && casHead(h, p))
+ lazySetNext(h, h);
}
/**
@@ -299,49 +296,8 @@
* stale pointer that is now off the list.
*/
final Node<E> succ(Node<E> p) {
- if (p == (p = p.next))
- p = head;
- return p;
- }
-
- /**
- * Tries to CAS pred.next (or head, if pred is null) from c to p.
- * Caller must ensure that we're not unlinking the trailing node.
- */
- private boolean tryCasSuccessor(Node<E> pred, Node<E> c, Node<E> p) {
- // assert p != null;
- // assert c.item == null;
- // assert c != p;
- if (pred != null)
- return NEXT.compareAndSet(pred, c, p);
- if (HEAD.compareAndSet(this, c, p)) {
- NEXT.setRelease(c, c);
- return true;
- }
- return false;
- }
-
- /**
- * Collapse dead nodes between pred and q.
- * @param pred the last known live node, or null if none
- * @param c the first dead node
- * @param p the last dead node
- * @param q p.next: the next live node, or null if at end
- * @return either old pred or p if pred dead or CAS failed
- */
- private Node<E> skipDeadNodes(Node<E> pred, Node<E> c, Node<E> p, Node<E> q) {
- // assert pred != c;
- // assert p != q;
- // assert c.item == null;
- // assert p.item == null;
- if (q == null) {
- // Never unlink trailing node.
- if (c == p) return pred;
- q = p;
- }
- return (tryCasSuccessor(pred, c, q)
- && (pred == null || ITEM.get(pred) != null))
- ? pred : p;
+ Node<E> next = p.next;
+ return (p == next) ? head : next;
}
/**
@@ -352,18 +308,18 @@
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
- final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ final Node<E> newNode = newNode(Objects.requireNonNull(e));
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (NEXT.compareAndSet(p, null, newNode)) {
+ if (casNext(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
- if (p != t) // hop two nodes at a time; failure is OK
- TAIL.weakCompareAndSet(this, t, newNode);
+ if (p != t) // hop two nodes at a time
+ casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
@@ -381,10 +337,12 @@
}
public E poll() {
- restartFromHead: for (;;) {
- for (Node<E> h = head, p = h, q;; p = q) {
- final E item;
- if ((item = p.item) != null && p.casItem(item, null)) {
+ restartFromHead:
+ for (;;) {
+ for (Node<E> h = head, p = h, q;;) {
+ E item = p.item;
+
+ if (item != null && casItem(p, item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
@@ -397,21 +355,25 @@
}
else if (p == q)
continue restartFromHead;
+ else
+ p = q;
}
}
}
public E peek() {
- restartFromHead: for (;;) {
- for (Node<E> h = head, p = h, q;; p = q) {
- final E item;
- if ((item = p.item) != null
- || (q = p.next) == null) {
+ restartFromHead:
+ for (;;) {
+ for (Node<E> h = head, p = h, q;;) {
+ E item = p.item;
+ if (item != null || (q = p.next) == null) {
updateHead(h, p);
return item;
}
else if (p == q)
continue restartFromHead;
+ else
+ p = q;
}
}
}
@@ -425,8 +387,9 @@
* of losing a race to a concurrent poll().
*/
Node<E> first() {
- restartFromHead: for (;;) {
- for (Node<E> h = head, p = h, q;; p = q) {
+ restartFromHead:
+ for (;;) {
+ for (Node<E> h = head, p = h, q;;) {
boolean hasItem = (p.item != null);
if (hasItem || (q = p.next) == null) {
updateHead(h, p);
@@ -434,6 +397,8 @@
}
else if (p == q)
continue restartFromHead;
+ else
+ p = q;
}
}
}
@@ -486,25 +451,14 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- if (o == null) return false;
- restartFromHead: for (;;) {
- for (Node<E> p = head, pred = null; p != null; ) {
- Node<E> q = p.next;
- final E item;
- if ((item = p.item) != null) {
- if (o.equals(item))
- return true;
- pred = p; p = q; continue;
- }
- for (Node<E> c = p;; q = p.next) {
- if (q == null || q.item != null) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) continue restartFromHead;
- }
+ if (o != null) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null && o.equals(item))
+ return true;
}
- return false;
}
+ return false;
}
/**
@@ -519,27 +473,27 @@
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
- if (o == null) return false;
- restartFromHead: for (;;) {
- for (Node<E> p = head, pred = null; p != null; ) {
- Node<E> q = p.next;
- final E item;
- if ((item = p.item) != null) {
- if (o.equals(item) && p.casItem(item, null)) {
- skipDeadNodes(pred, p, p, q);
- return true;
+ if (o != null) {
+ Node<E> next, pred = null;
+ for (Node<E> p = first(); p != null; pred = p, p = next) {
+ boolean removed = false;
+ E item = p.item;
+ if (item != null) {
+ if (!o.equals(item)) {
+ next = succ(p);
+ continue;
}
- pred = p; p = q; continue;
+ removed = casItem(p, item, null);
}
- for (Node<E> c = p;; q = p.next) {
- if (q == null || q.item != null) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) continue restartFromHead;
- }
+
+ next = succ(p);
+ if (pred != null && next != null) // unlink
+ casNext(pred, p, next);
+ if (removed)
+ return true;
}
- return false;
}
+ return false;
}
/**
@@ -562,11 +516,13 @@
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
- Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
- else
- last.appendRelaxed(last = newNode);
+ else {
+ lazySetNext(last, newNode);
+ last = newNode;
+ }
}
if (beginningOfTheEnd == null)
return false;
@@ -576,15 +532,15 @@
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
+ if (casNext(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this queue.
- if (!TAIL.weakCompareAndSet(this, t, last)) {
+ if (!casTail(t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
- TAIL.weakCompareAndSet(this, t, last);
+ casTail(t, last);
}
return true;
}
@@ -608,8 +564,8 @@
int charLength = 0;
int size = 0;
for (Node<E> p = first(); p != null;) {
- final E item;
- if ((item = p.item) != null) {
+ E item = p.item;
+ if (item != null) {
if (a == null)
a = new String[4];
else if (size == a.length)
@@ -634,8 +590,8 @@
restartFromHead: for (;;) {
int size = 0;
for (Node<E> p = first(); p != null;) {
- final E item;
- if ((item = p.item) != null) {
+ E item = p.item;
+ if (item != null) {
if (x == null)
x = new Object[4];
else if (size == x.length)
@@ -712,7 +668,7 @@
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
- Objects.requireNonNull(a);
+ if (a == null) throw new NullPointerException();
return (T[]) toArrayInternal(a);
}
@@ -752,7 +708,7 @@
restartFromHead: for (;;) {
Node<E> h, p, q;
for (p = h = head;; p = q) {
- final E item;
+ E item;
if ((item = p.item) != null) {
nextNode = p;
nextItem = item;
@@ -788,12 +744,10 @@
}
// unlink deleted nodes
if ((q = succ(p)) != null)
- NEXT.compareAndSet(pred, p, q);
+ casNext(pred, p, q);
}
}
- // Default implementation of forEachRemaining is "good enough".
-
public void remove() {
Node<E> l = lastRet;
if (l == null) throw new IllegalStateException();
@@ -819,8 +773,8 @@
// Write out all elements in the proper order.
for (Node<E> p = first(); p != null; p = succ(p)) {
- final E item;
- if ((item = p.item) != null)
+ Object item = p.item;
+ if (item != null)
s.writeObject(item);
}
@@ -843,69 +797,91 @@
Node<E> h = null, t = null;
for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
- Node<E> newNode = new Node<E>((E) item);
+ Node<E> newNode = newNode((E) item);
if (h == null)
h = t = newNode;
- else
- t.appendRelaxed(t = newNode);
+ else {
+ lazySetNext(t, newNode);
+ t = newNode;
+ }
}
if (h == null)
- h = t = new Node<E>();
+ h = t = newNode(null);
head = h;
tail = t;
}
/** A customized variant of Spliterators.IteratorSpliterator */
- final class CLQSpliterator implements Spliterator<E> {
+ static final class CLQSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedQueue<E> queue;
Node<E> current; // current node; null until initialized
int batch; // batch size for splits
boolean exhausted; // true when no more nodes
+ CLQSpliterator(ConcurrentLinkedQueue<E> queue) {
+ this.queue = queue;
+ }
public Spliterator<E> trySplit() {
- Node<E> p, q;
- if ((p = current()) == null || (q = p.next) == null)
- return null;
- int i = 0, n = batch = Math.min(batch + 1, MAX_BATCH);
- Object[] a = null;
- do {
- final E e;
- if ((e = p.item) != null) {
- if (a == null)
- a = new Object[n];
- a[i++] = e;
+ Node<E> p;
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null) &&
+ p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
}
- if (p == (p = q))
- p = first();
- } while (p != null && (q = p.next) != null && i < n);
- setCurrent(p);
- return (i == 0) ? null :
- Spliterators.spliterator(a, 0, i, (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT));
+ }
+ return null;
}
public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final Node<E> p;
- if ((p = current()) != null) {
- current = null;
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
exhausted = true;
- forEachFrom(action, p);
+ do {
+ E e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
}
}
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
Node<E> p;
- if ((p = current()) != null) {
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
E e;
do {
e = p.item;
if (p == (p = p.next))
- p = first();
+ p = q.first();
} while (e == null && p != null);
- setCurrent(p);
+ if ((current = p) == null)
+ exhausted = true;
if (e != null) {
action.accept(e);
return true;
@@ -914,24 +890,11 @@
return false;
}
- private void setCurrent(Node<E> p) {
- if ((current = p) == null)
- exhausted = true;
- }
-
- private Node<E> current() {
- Node<E> p;
- if ((p = current) == null && !exhausted)
- setCurrent(p = first());
- return p;
- }
-
public long estimateSize() { return Long.MAX_VALUE; }
public int characteristics() {
- return (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT);
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
}
}
@@ -953,123 +916,36 @@
*/
@Override
public Spliterator<E> spliterator() {
- return new CLQSpliterator();
+ return new CLQSpliterator<E>(this);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
+ private boolean casTail(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
+ private boolean casHead(Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
+ // Unsafe mechanics
- public void clear() {
- bulkRemove(e -> true);
- }
-
- /**
- * Tolerate this many consecutive dead nodes before CAS-collapsing.
- * Amortized cost of clear() is (1 + 1/MAX_HOPS) CASes per element.
- */
- private static final int MAX_HOPS = 8;
-
- /** Implementation of bulk remove methods. */
- private boolean bulkRemove(Predicate<? super E> filter) {
- boolean removed = false;
- restartFromHead: for (;;) {
- int hops = MAX_HOPS;
- // c will be CASed to collapse intervening dead nodes between
- // pred (or head if null) and p.
- for (Node<E> p = head, c = p, pred = null, q; p != null; p = q) {
- q = p.next;
- final E item; boolean pAlive;
- if (pAlive = ((item = p.item) != null)) {
- if (filter.test(item)) {
- if (p.casItem(item, null))
- removed = true;
- pAlive = false;
- }
- }
- if (pAlive || q == null || --hops == 0) {
- // p might already be self-linked here, but if so:
- // - CASing head will surely fail
- // - CASing pred's next will be useless but harmless.
- if ((c != p && !tryCasSuccessor(pred, c, c = p))
- || pAlive) {
- // if CAS failed or alive, abandon old pred
- hops = MAX_HOPS;
- pred = p;
- c = q;
- }
- } else if (p == q)
- continue restartFromHead;
- }
- return removed;
- }
- }
-
- /**
- * Runs action on each element found during a traversal starting at p.
- * If p is null, the action is not run.
- */
- void forEachFrom(Consumer<? super E> action, Node<E> p) {
- for (Node<E> pred = null; p != null; ) {
- Node<E> q = p.next;
- final E item;
- if ((item = p.item) != null) {
- action.accept(item);
- pred = p; p = q; continue;
- }
- for (Node<E> c = p;; q = p.next) {
- if (q == null || q.item != null) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) { pred = null; p = head; break; }
- }
- }
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- forEachFrom(action, head);
- }
-
- // VarHandle mechanics
- private static final VarHandle HEAD;
- private static final VarHandle TAIL;
- static final VarHandle ITEM;
- static final VarHandle NEXT;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long ITEM;
+ private static final long NEXT;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",
- Node.class);
- TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",
- Node.class);
- ITEM = l.findVarHandle(Node.class, "item", Object.class);
- NEXT = l.findVarHandle(Node.class, "next", Node.class);
+ HEAD = U.objectFieldOffset
+ (ConcurrentLinkedQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (ConcurrentLinkedQueue.class.getDeclaredField("tail"));
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentMap.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentMap.java
index 2037e31..69dae6f 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentMap.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentMap.java
@@ -41,8 +41,14 @@
import java.util.function.BiFunction;
import java.util.function.Function;
+// BEGIN android-note
+// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
+// END android-note
+
/**
- * A {@link Map} providing thread safety and atomicity guarantees.
+ * A {@link java.util.Map} providing thread safety and atomicity
+ * guarantees.
*
* <p>To maintain the specified guarantees, default implementations of
* methods including {@link #putIfAbsent} inherited from {@link Map}
@@ -59,10 +65,6 @@
* actions subsequent to the access or removal of that object from
* the {@code ConcurrentMap} in another thread.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.5
* @author Doug Lea
* @param <K> the type of keys maintained by this map
@@ -180,10 +182,10 @@
* is not supported by this map
* @throws ClassCastException if the key or value is of an inappropriate
* type for this map
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key or value is null,
* and this map does not permit null keys or values
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object key, Object value);
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
index 1666c8b..94a90cd 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
@@ -38,14 +38,14 @@
import java.util.NavigableMap;
import java.util.NavigableSet;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* A {@link ConcurrentMap} supporting {@link NavigableMap} operations,
* and recursively so for its navigable sub-maps.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index db14d4b..583244b 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
@@ -48,6 +46,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -58,7 +57,10 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-import java.util.concurrent.atomic.LongAdder;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* A scalable concurrent {@link ConcurrentNavigableMap} implementation.
@@ -87,7 +89,12 @@
* associated map using {@code put}, {@code putIfAbsent}, or
* {@code replace}, depending on exactly which effect you need.)
*
- * <p>Beware that bulk operations {@code putAll}, {@code equals},
+ * <p>Beware that, unlike in most collections, the {@code size}
+ * method is <em>not</em> a constant-time operation. Because of the
+ * asynchronous nature of these maps, determining the current number
+ * of elements requires a traversal of the elements, and so may report
+ * inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code putAll}, {@code equals},
* {@code toArray}, {@code containsValue}, and {@code clear} are
* <em>not</em> guaranteed to be performed atomically. For example, an
* iterator operating concurrently with a {@code putAll} operation
@@ -100,10 +107,6 @@
* null return values cannot be reliably distinguished from the absence of
* elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
@@ -154,35 +157,42 @@
* be slow and space-intensive using AtomicMarkedReference), nodes
* use direct CAS'able next pointers. On deletion, instead of
* marking a pointer, they splice in another node that can be
- * thought of as standing for a marked pointer (see method
- * unlinkNode). Using plain nodes acts roughly like "boxed"
- * implementations of marked pointers, but uses new nodes only
- * when nodes are deleted, not for every link. This requires less
- * space and supports faster traversal. Even if marked references
- * were better supported by JVMs, traversal using this technique
- * might still be faster because any search need only read ahead
- * one more node than otherwise required (to check for trailing
- * marker) rather than unmasking mark bits or whatever on each
- * read.
+ * thought of as standing for a marked pointer (indicating this by
+ * using otherwise impossible field values). Using plain nodes
+ * acts roughly like "boxed" implementations of marked pointers,
+ * but uses new nodes only when nodes are deleted, not for every
+ * link. This requires less space and supports faster
+ * traversal. Even if marked references were better supported by
+ * JVMs, traversal using this technique might still be faster
+ * because any search need only read ahead one more node than
+ * otherwise required (to check for trailing marker) rather than
+ * unmasking mark bits or whatever on each read.
*
* This approach maintains the essential property needed in the HM
* algorithm of changing the next-pointer of a deleted node so
* that any other CAS of it will fail, but implements the idea by
- * changing the pointer to point to a different node (with
- * otherwise illegal null fields), not by marking it. While it
- * would be possible to further squeeze space by defining marker
- * nodes not to have key/value fields, it isn't worth the extra
- * type-testing overhead. The deletion markers are rarely
- * encountered during traversal, are easily detected via null
- * checks that are needed anyway, and are normally quickly garbage
- * collected. (Note that this technique would not work well in
- * systems without garbage collection.)
+ * changing the pointer to point to a different node, not by
+ * marking it. While it would be possible to further squeeze
+ * space by defining marker nodes not to have key/value fields, it
+ * isn't worth the extra type-testing overhead. The deletion
+ * markers are rarely encountered during traversal and are
+ * normally quickly garbage collected. (Note that this technique
+ * would not work well in systems without garbage collection.)
*
* In addition to using deletion markers, the lists also use
* nullness of value fields to indicate deletion, in a style
* similar to typical lazy-deletion schemes. If a node's value is
* null, then it is considered logically deleted and ignored even
- * though it is still reachable.
+ * though it is still reachable. This maintains proper control of
+ * concurrent replace vs delete operations -- an attempted replace
+ * must fail if a delete beat it by nulling field, and a delete
+ * must return the last non-null value held in the field. (Note:
+ * Null, rather than some special marker, is used for value fields
+ * here because it just so happens to mesh with the Map API
+ * requirement that method get returns null if there is no
+ * mapping, which allows nodes to remain concurrently readable
+ * even when deleted. Using any other marker value here would be
+ * messy at best.)
*
* Here's the sequence of events for a deletion of node n with
* predecessor b and successor f, initially:
@@ -192,8 +202,9 @@
* +------+ +------+ +------+
*
* 1. CAS n's value field from non-null to null.
- * Traversals encountering a node with null value ignore it.
- * However, ongoing insertions and deletions might still modify
+ * From this point on, no public operations encountering
+ * the node consider this mapping to exist. However, other
+ * ongoing insertions and deletions might still modify
* n's next pointer.
*
* 2. CAS n's next pointer to point to a new marker node.
@@ -216,7 +227,12 @@
* thread noticed during a traversal a node with null value and
* helped out by marking and/or unlinking. This helping-out
* ensures that no thread can become stuck waiting for progress of
- * the deleting thread.
+ * the deleting thread. The use of marker nodes slightly
+ * complicates helping-out code because traversals must track
+ * consistent reads of up to four nodes (b, n, marker, f), not
+ * just (b, n, f), although the next field of a marker is
+ * immutable, and once a next field is CAS'ed to point to a
+ * marker, it never again changes, so this requires less care.
*
* Skip lists add indexing to this scheme, so that the base-level
* traversals start close to the locations being found, inserted
@@ -226,101 +242,113 @@
* b) that are not (structurally) deleted, otherwise retrying
* after processing the deletion.
*
- * Index levels are maintained using CAS to link and unlink
- * successors ("right" fields). Races are allowed in index-list
- * operations that can (rarely) fail to link in a new index node.
- * (We can't do this of course for data nodes.) However, even
- * when this happens, the index lists correctly guide search.
- * This can impact performance, but since skip lists are
- * probabilistic anyway, the net result is that under contention,
- * the effective "p" value may be lower than its nominal value.
+ * Index levels are maintained as lists with volatile next fields,
+ * using CAS to link and unlink. Races are allowed in index-list
+ * operations that can (rarely) fail to link in a new index node
+ * or delete one. (We can't do this of course for data nodes.)
+ * However, even when this happens, the index lists remain sorted,
+ * so correctly serve as indices. This can impact performance,
+ * but since skip lists are probabilistic anyway, the net result
+ * is that under contention, the effective "p" value may be lower
+ * than its nominal value. And race windows are kept small enough
+ * that in practice these failures are rare, even under a lot of
+ * contention.
*
- * Index insertion and deletion sometimes require a separate
- * traversal pass occurring after the base-level action, to add or
- * remove index nodes. This adds to single-threaded overhead, but
- * improves contended multithreaded performance by narrowing
- * interference windows, and allows deletion to ensure that all
- * index nodes will be made unreachable upon return from a public
- * remove operation, thus avoiding unwanted garbage retention.
+ * The fact that retries (for both base and index lists) are
+ * relatively cheap due to indexing allows some minor
+ * simplifications of retry logic. Traversal restarts are
+ * performed after most "helping-out" CASes. This isn't always
+ * strictly necessary, but the implicit backoffs tend to help
+ * reduce other downstream failed CAS's enough to outweigh restart
+ * cost. This worsens the worst case, but seems to improve even
+ * highly contended cases.
+ *
+ * Unlike most skip-list implementations, index insertion and
+ * deletion here require a separate traversal pass occurring after
+ * the base-level action, to add or remove index nodes. This adds
+ * to single-threaded overhead, but improves contended
+ * multithreaded performance by narrowing interference windows,
+ * and allows deletion to ensure that all index nodes will be made
+ * unreachable upon return from a public remove operation, thus
+ * avoiding unwanted garbage retention. This is more important
+ * here than in some other data structures because we cannot null
+ * out node fields referencing user keys since they might still be
+ * read by other ongoing traversals.
*
* Indexing uses skip list parameters that maintain good search
* performance while using sparser-than-usual indices: The
- * hardwired parameters k=1, p=0.5 (see method doPut) mean that
- * about one-quarter of the nodes have indices. Of those that do,
- * half have one level, a quarter have two, and so on (see Pugh's
- * Skip List Cookbook, sec 3.4), up to a maximum of 62 levels
- * (appropriate for up to 2^63 elements). The expected total
- * space requirement for a map is slightly less than for the
- * current implementation of java.util.TreeMap.
+ * hardwired parameters k=1, p=0.5 (see method doPut) mean
+ * that about one-quarter of the nodes have indices. Of those that
+ * do, half have one level, a quarter have two, and so on (see
+ * Pugh's Skip List Cookbook, sec 3.4). The expected total space
+ * requirement for a map is slightly less than for the current
+ * implementation of java.util.TreeMap.
*
* Changing the level of the index (i.e, the height of the
- * tree-like structure) also uses CAS. Creation of an index with
- * height greater than the current level adds a level to the head
- * index by CAS'ing on a new top-most head. To maintain good
- * performance after a lot of removals, deletion methods
- * heuristically try to reduce the height if the topmost levels
- * appear to be empty. This may encounter races in which it is
- * possible (but rare) to reduce and "lose" a level just as it is
- * about to contain an index (that will then never be
- * encountered). This does no structural harm, and in practice
- * appears to be a better option than allowing unrestrained growth
- * of levels.
+ * tree-like structure) also uses CAS. The head index has initial
+ * level/height of one. Creation of an index with height greater
+ * than the current level adds a level to the head index by
+ * CAS'ing on a new top-most head. To maintain good performance
+ * after a lot of removals, deletion methods heuristically try to
+ * reduce the height if the topmost levels appear to be empty.
+ * This may encounter races in which it possible (but rare) to
+ * reduce and "lose" a level just as it is about to contain an
+ * index (that will then never be encountered). This does no
+ * structural harm, and in practice appears to be a better option
+ * than allowing unrestrained growth of levels.
*
- * This class provides concurrent-reader-style memory consistency,
- * ensuring that read-only methods report status and/or values no
- * staler than those holding at method entry. This is done by
- * performing all publication and structural updates using
- * (volatile) CAS, placing an acquireFence in a few access
- * methods, and ensuring that linked objects are transitively
- * acquired via dependent reads (normally once) unless performing
- * a volatile-mode CAS operation (that also acts as an acquire and
- * release). This form of fence-hoisting is similar to RCU and
- * related techniques (see McKenney's online book
- * https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html)
- * It minimizes overhead that may otherwise occur when using so
- * many volatile-mode reads. Using explicit acquireFences is
- * logistically easier than targeting particular fields to be read
- * in acquire mode: fences are just hoisted up as far as possible,
- * to the entry points or loop headers of a few methods. A
- * potential disadvantage is that these few remaining fences are
- * not easily optimized away by compilers under exclusively
- * single-thread use. It requires some care to avoid volatile
- * mode reads of other fields. (Note that the memory semantics of
- * a reference dependently read in plain mode exactly once are
- * equivalent to those for atomic opaque mode.) Iterators and
- * other traversals encounter each node and value exactly once.
- * Other operations locate an element (or position to insert an
- * element) via a sequence of dereferences. This search is broken
- * into two parts. Method findPredecessor (and its specialized
- * embeddings) searches index nodes only, returning a base-level
- * predecessor of the key. Callers carry out the base-level
- * search, restarting if encountering a marker preventing link
- * modification. In some cases, it is possible to encounter a
- * node multiple times while descending levels. For mutative
- * operations, the reported value is validated using CAS (else
- * retrying), preserving linearizability with respect to each
- * other. Others may return any (non-null) value holding in the
- * course of the method call. (Search-based methods also include
- * some useless-looking explicit null checks designed to allow
- * more fields to be nulled out upon removal, to reduce floating
- * garbage, but which is not currently done, pending discovery of
- * a way to do this with less impact on other operations.)
+ * The code for all this is more verbose than you'd like. Most
+ * operations entail locating an element (or position to insert an
+ * element). The code to do this can't be nicely factored out
+ * because subsequent uses require a snapshot of predecessor
+ * and/or successor and/or value fields which can't be returned
+ * all at once, at least not without creating yet another object
+ * to hold them -- creating such little objects is an especially
+ * bad idea for basic internal search operations because it adds
+ * to GC overhead. (This is one of the few times I've wished Java
+ * had macros.) Instead, some traversal code is interleaved within
+ * insertion and removal operations. The control logic to handle
+ * all the retry conditions is sometimes twisty. Most search is
+ * broken into 2 parts. findPredecessor() searches index nodes
+ * only, returning a base-level predecessor of the key. findNode()
+ * finishes out the base-level search. Even with this factoring,
+ * there is a fair amount of near-duplication of code to handle
+ * variants.
*
* To produce random values without interference across threads,
* we use within-JDK thread local random support (via the
* "secondary seed", to avoid interference with user-level
* ThreadLocalRandom.)
*
+ * A previous version of this class wrapped non-comparable keys
+ * with their comparators to emulate Comparables when using
+ * comparators vs Comparables. However, JVMs now appear to better
+ * handle infusing comparator-vs-comparable choice into search
+ * loops. Static method cpr(comparator, x, y) is used for all
+ * comparisons, which works well as long as the comparator
+ * argument is set up outside of loops (thus sometimes passed as
+ * an argument to internal methods) to avoid field re-reads.
+ *
* For explanation of algorithms sharing at least a couple of
* features with this one, see Mikhail Fomitchev's thesis
* (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis
* (http://www.cl.cam.ac.uk/users/kaf24/), and Hakan Sundell's
* thesis (http://www.cs.chalmers.se/~phs/).
*
+ * Given the use of tree-like index nodes, you might wonder why
+ * this doesn't use some kind of search tree instead, which would
+ * support somewhat faster search operations. The reason is that
+ * there are no known efficient lock-free insertion and deletion
+ * algorithms for search trees. The immutability of the "down"
+ * links of index nodes (as opposed to mutable "left" fields in
+ * true trees) makes this tractable using only CAS operations.
+ *
* Notation guide for local variables
- * Node: b, n, f, p for predecessor, node, successor, aux
+ * Node: b, n, f for predecessor, node, successor
* Index: q, r, d for index node, right, down.
+ * t for another index node
* Head: h
+ * Levels: j
* Keys: k, key
* Values: v, value
* Comparisons: c
@@ -329,6 +357,16 @@
private static final long serialVersionUID = -8627078645895051609L;
/**
+ * Special value used to identify base-level header.
+ */
+ static final Object BASE_HEADER = new Object();
+
+ /**
+ * The topmost head index of the skiplist.
+ */
+ private transient volatile HeadIndex<K,V> head;
+
+ /**
* The comparator used to maintain order in this map, or null if
* using natural ordering. (Non-private to simplify access in
* nested classes.)
@@ -336,152 +374,316 @@
*/
final Comparator<? super K> comparator;
- /** Lazily initialized topmost index of the skiplist. */
- private transient Index<K,V> head;
- /** Lazily initialized element count */
- private transient LongAdder adder;
/** Lazily initialized key set */
private transient KeySet<K,V> keySet;
- /** Lazily initialized values collection */
- private transient Values<K,V> values;
/** Lazily initialized entry set */
private transient EntrySet<K,V> entrySet;
- /** Lazily initialized descending map */
- private transient SubMap<K,V> descendingMap;
+ /** Lazily initialized values collection */
+ private transient Values<K,V> values;
+ /** Lazily initialized descending key set */
+ private transient ConcurrentNavigableMap<K,V> descendingMap;
+
+ /**
+ * Initializes or resets state. Needed by constructors, clone,
+ * clear, readObject. and ConcurrentSkipListSet.clone.
+ * (Note that comparator must be separately initialized.)
+ */
+ private void initialize() {
+ keySet = null;
+ entrySet = null;
+ values = null;
+ descendingMap = null;
+ head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
+ null, null, 1);
+ }
+
+ /**
+ * compareAndSet head node.
+ */
+ private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
+ }
+
+ /* ---------------- Nodes -------------- */
/**
* Nodes hold keys and values, and are singly linked in sorted
* order, possibly with some intervening marker nodes. The list is
- * headed by a header node accessible as head.node. Headers and
- * marker nodes have null keys. The val field (but currently not
- * the key field) is nulled out upon deletion.
+ * headed by a dummy node accessible as head.node. The value field
+ * is declared only as Object because it takes special non-V
+ * values for marker and header nodes.
*/
static final class Node<K,V> {
- final K key; // currently, never detached
- V val;
- Node<K,V> next;
- Node(K key, V value, Node<K,V> next) {
+ final K key;
+ volatile Object value;
+ volatile Node<K,V> next;
+
+ /**
+ * Creates a new regular node.
+ */
+ Node(K key, Object value, Node<K,V> next) {
this.key = key;
- this.val = value;
+ this.value = value;
this.next = next;
}
+
+ /**
+ * Creates a new marker node. A marker is distinguished by
+ * having its value field point to itself. Marker nodes also
+ * have null keys, a fact that is exploited in a few places,
+ * but this doesn't distinguish markers from the base-level
+ * header node (head.node), which also has a null key.
+ */
+ Node(Node<K,V> next) {
+ this.key = null;
+ this.value = this;
+ this.next = next;
+ }
+
+ /**
+ * compareAndSet value field.
+ */
+ boolean casValue(Object cmp, Object val) {
+ return U.compareAndSwapObject(this, VALUE, cmp, val);
+ }
+
+ /**
+ * compareAndSet next field.
+ */
+ boolean casNext(Node<K,V> cmp, Node<K,V> val) {
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
+ }
+
+ /**
+ * Returns true if this node is a marker. This method isn't
+ * actually called in any current code checking for markers
+ * because callers will have already read value field and need
+ * to use that read (not another done here) and so directly
+ * test if value points to node.
+ *
+ * @return true if this node is a marker node
+ */
+ boolean isMarker() {
+ return value == this;
+ }
+
+ /**
+ * Returns true if this node is the header of base-level list.
+ * @return true if this node is header node
+ */
+ boolean isBaseHeader() {
+ return value == BASE_HEADER;
+ }
+
+ /**
+ * Tries to append a deletion marker to this node.
+ * @param f the assumed current successor of this node
+ * @return true if successful
+ */
+ boolean appendMarker(Node<K,V> f) {
+ return casNext(f, new Node<K,V>(f));
+ }
+
+ /**
+ * Helps out a deletion by appending marker or unlinking from
+ * predecessor. This is called during traversals when value
+ * field seen to be null.
+ * @param b predecessor
+ * @param f successor
+ */
+ void helpDelete(Node<K,V> b, Node<K,V> f) {
+ /*
+ * Rechecking links and then doing only one of the
+ * help-out stages per call tends to minimize CAS
+ * interference among helping threads.
+ */
+ if (f == next && this == b.next) {
+ if (f == null || f.value != f) // not already marked
+ casNext(f, new Node<K,V>(f));
+ else
+ b.casNext(this, f.next);
+ }
+ }
+
+ /**
+ * Returns value if this node contains a valid key-value pair,
+ * else null.
+ * @return this node's value if it isn't a marker or header or
+ * is deleted, else null
+ */
+ V getValidValue() {
+ Object v = value;
+ if (v == this || v == BASE_HEADER)
+ return null;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+
+ /**
+ * Creates and returns a new SimpleImmutableEntry holding current
+ * mapping if this node holds a valid value, else null.
+ * @return new entry or null
+ */
+ AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
+ Object v = value;
+ if (v == null || v == this || v == BASE_HEADER)
+ return null;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
+ }
+
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
+ private static final long NEXT;
+
+ static {
+ try {
+ VALUE = U.objectFieldOffset
+ (Node.class.getDeclaredField("value"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
}
+ /* ---------------- Indexing -------------- */
+
/**
- * Index nodes represent the levels of the skip list.
+ * Index nodes represent the levels of the skip list. Note that
+ * even though both Nodes and Indexes have forward-pointing
+ * fields, they have different types and are handled in different
+ * ways, that can't nicely be captured by placing field in a
+ * shared abstract class.
*/
- static final class Index<K,V> {
- final Node<K,V> node; // currently, never detached
+ static class Index<K,V> {
+ final Node<K,V> node;
final Index<K,V> down;
- Index<K,V> right;
+ volatile Index<K,V> right;
+
+ /**
+ * Creates index node with given values.
+ */
Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
this.node = node;
this.down = down;
this.right = right;
}
+
+ /**
+ * compareAndSet right field.
+ */
+ final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
+ return U.compareAndSwapObject(this, RIGHT, cmp, val);
+ }
+
+ /**
+ * Returns true if the node this indexes has been deleted.
+ * @return true if indexed node is known to be deleted
+ */
+ final boolean indexesDeletedNode() {
+ return node.value == null;
+ }
+
+ /**
+ * Tries to CAS newSucc as successor. To minimize races with
+ * unlink that may lose this index node, if the node being
+ * indexed is known to be deleted, it doesn't try to link in.
+ * @param succ the expected current successor
+ * @param newSucc the new successor
+ * @return true if successful
+ */
+ final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
+ Node<K,V> n = node;
+ newSucc.right = succ;
+ return n.value != null && casRight(succ, newSucc);
+ }
+
+ /**
+ * Tries to CAS right field to skip over apparent successor
+ * succ. Fails (forcing a retraversal by caller) if this node
+ * is known to be deleted.
+ * @param succ the expected current successor
+ * @return true if successful
+ */
+ final boolean unlink(Index<K,V> succ) {
+ return node.value != null && casRight(succ, succ.right);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long RIGHT;
+ static {
+ try {
+ RIGHT = U.objectFieldOffset
+ (Index.class.getDeclaredField("right"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
}
- /* ---------------- Utilities -------------- */
+ /* ---------------- Head nodes -------------- */
+
+ /**
+ * Nodes heading each level keep track of their level.
+ */
+ static final class HeadIndex<K,V> extends Index<K,V> {
+ final int level;
+ HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
+ super(node, down, right);
+ this.level = level;
+ }
+ }
+
+ /* ---------------- Comparison utilities -------------- */
/**
* Compares using comparator or natural ordering if null.
* Called only by methods that have performed required type checks.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
- static int cpr(Comparator c, Object x, Object y) {
+ static final int cpr(Comparator c, Object x, Object y) {
return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
}
- /**
- * Returns the header for base node list, or null if uninitialized
- */
- final Node<K,V> baseHead() {
- Index<K,V> h;
- VarHandle.acquireFence();
- return ((h = head) == null) ? null : h.node;
- }
-
- /**
- * Tries to unlink deleted node n from predecessor b (if both
- * exist), by first splicing in a marker if not already present.
- * Upon return, node n is sure to be unlinked from b, possibly
- * via the actions of some other thread.
- *
- * @param b if nonnull, predecessor
- * @param n if nonnull, node known to be deleted
- */
- static <K,V> void unlinkNode(Node<K,V> b, Node<K,V> n) {
- if (b != null && n != null) {
- Node<K,V> f, p;
- for (;;) {
- if ((f = n.next) != null && f.key == null) {
- p = f.next; // already marked
- break;
- }
- else if (NEXT.compareAndSet(n, f,
- new Node<K,V>(null, null, f))) {
- p = f; // add marker
- break;
- }
- }
- NEXT.compareAndSet(b, n, p);
- }
- }
-
- /**
- * Adds to element count, initializing adder if necessary
- *
- * @param c count to add
- */
- private void addCount(long c) {
- LongAdder a;
- do {} while ((a = adder) == null &&
- !ADDER.compareAndSet(this, null, a = new LongAdder()));
- a.add(c);
- }
-
- /**
- * Returns element count, initializing adder if necessary.
- */
- final long getAdderCount() {
- LongAdder a; long c;
- do {} while ((a = adder) == null &&
- !ADDER.compareAndSet(this, null, a = new LongAdder()));
- return ((c = a.sum()) <= 0L) ? 0L : c; // ignore transient negatives
- }
-
/* ---------------- Traversal -------------- */
/**
- * Returns an index node with key strictly less than given key.
- * Also unlinks indexes to deleted nodes found along the way.
- * Callers rely on this side-effect of clearing indices to deleted
- * nodes.
- *
- * @param key if nonnull the key
- * @return a predecessor node of key, or null if uninitialized or null key
+ * Returns a base-level node with key strictly less than given key,
+ * or the base-level header if there is no such node. Also
+ * unlinks indexes to deleted nodes found along the way. Callers
+ * rely on this side-effect of clearing indices to deleted nodes.
+ * @param key the key
+ * @return a predecessor of key
*/
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
- Index<K,V> q;
- VarHandle.acquireFence();
- if ((q = head) == null || key == null)
- return null;
- else {
- for (Index<K,V> r, d;;) {
- while ((r = q.right) != null) {
- Node<K,V> p; K k;
- if ((p = r.node) == null || (k = p.key) == null ||
- p.val == null) // unlink index to deleted node
- RIGHT.compareAndSet(q, r, r.right);
- else if (cpr(cmp, key, k) > 0)
+ if (key == null)
+ throw new NullPointerException(); // don't postpone errors
+ for (;;) {
+ for (Index<K,V> q = head, r = q.right, d;;) {
+ if (r != null) {
+ Node<K,V> n = r.node;
+ K k = n.key;
+ if (n.value == null) {
+ if (!q.unlink(r))
+ break; // restart
+ r = q.right; // reread r
+ continue;
+ }
+ if (cpr(cmp, key, k) > 0) {
q = r;
- else
- break;
+ r = r.right;
+ continue;
+ }
}
- if ((d = q.down) != null)
- q = d;
- else
+ if ((d = q.down) == null)
return q.node;
+ q = d;
+ r = d.right;
}
}
}
@@ -491,11 +693,41 @@
* deleted nodes seen along the way. Repeatedly traverses at
* base-level looking for key starting at predecessor returned
* from findPredecessor, processing base-level deletions as
- * encountered. Restarts occur, at traversal step encountering
- * node n, if n's key field is null, indicating it is a marker, so
- * its predecessor is deleted before continuing, which we help do
- * by re-finding a valid predecessor. The traversal loops in
- * doPut, doRemove, and findNear all include the same checks.
+ * encountered. Some callers rely on this side-effect of clearing
+ * deleted nodes.
+ *
+ * Restarts occur, at traversal step centered on node n, if:
+ *
+ * (1) After reading n's next field, n is no longer assumed
+ * predecessor b's current successor, which means that
+ * we don't have a consistent 3-node snapshot and so cannot
+ * unlink any subsequent deleted nodes encountered.
+ *
+ * (2) n's value field is null, indicating n is deleted, in
+ * which case we help out an ongoing structural deletion
+ * before retrying. Even though there are cases where such
+ * unlinking doesn't require restart, they aren't sorted out
+ * here because doing so would not usually outweigh cost of
+ * restarting.
+ *
+ * (3) n is a marker or n's predecessor's value field is null,
+ * indicating (among other possibilities) that
+ * findPredecessor returned a deleted node. We can't unlink
+ * the node because we don't know its predecessor, so rely
+ * on another call to findPredecessor to notice and return
+ * some earlier predecessor, which it will do. This check is
+ * only strictly needed at beginning of loop, (and the
+ * b.value check isn't strictly needed at all) but is done
+ * each iteration to help avoid contention with other
+ * threads by callers that will fail to be able to change
+ * links, and so will retry anyway.
+ *
+ * The traversal loops in doPut, doRemove, and findNear all
+ * include the same three kinds of checks. And specialized
+ * versions appear in findFirst, and findLast and their variants.
+ * They can't easily share code because each uses the reads of
+ * fields held in locals occurring in the orders they were
+ * performed.
*
* @param key the key
* @return node holding key, or null if no such
@@ -504,81 +736,67 @@
if (key == null)
throw new NullPointerException(); // don't postpone errors
Comparator<? super K> cmp = comparator;
- Node<K,V> b;
- outer: while ((b = findPredecessor(key, cmp)) != null) {
- for (;;) {
- Node<K,V> n; K k; V v; int c;
- if ((n = b.next) == null)
- break outer; // empty
- else if ((k = n.key) == null)
- break; // b is deleted
- else if ((v = n.val) == null)
- unlinkNode(b, n); // n is deleted
- else if ((c = cpr(cmp, key, k)) > 0)
- b = n;
- else if (c == 0)
- return n;
- else
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
+ if (n == null)
break outer;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) == 0)
+ return n;
+ if (c < 0)
+ break outer;
+ b = n;
+ n = f;
}
}
return null;
}
/**
- * Gets value for key. Same idea as findNode, except skips over
- * deletions and markers, and returns first encountered value to
- * avoid possibly inconsistent rereads.
+ * Gets value for key. Almost the same as findNode, but returns
+ * the found value (to avoid retries during re-reads)
*
* @param key the key
* @return the value, or null if absent
*/
private V doGet(Object key) {
- Index<K,V> q;
- VarHandle.acquireFence();
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
- V result = null;
- if ((q = head) != null) {
- outer: for (Index<K,V> r, d;;) {
- while ((r = q.right) != null) {
- Node<K,V> p; K k; V v; int c;
- if ((p = r.node) == null || (k = p.key) == null ||
- (v = p.val) == null)
- RIGHT.compareAndSet(q, r, r.right);
- else if ((c = cpr(cmp, key, k)) > 0)
- q = r;
- else if (c == 0) {
- result = v;
- break outer;
- }
- else
- break;
- }
- if ((d = q.down) != null)
- q = d;
- else {
- Node<K,V> b, n;
- if ((b = q.node) != null) {
- while ((n = b.next) != null) {
- V v; int c;
- K k = n.key;
- if ((v = n.val) == null || k == null ||
- (c = cpr(cmp, key, k)) > 0)
- b = n;
- else {
- if (c == 0)
- result = v;
- break;
- }
- }
- }
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
+ if (n == null)
+ break outer;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
break;
}
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) == 0) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ if (c < 0)
+ break outer;
+ b = n;
+ n = f;
}
}
- return result;
+ return null;
}
/* ---------------- Insertion -------------- */
@@ -586,160 +804,126 @@
/**
* Main insertion method. Adds element if not present, or
* replaces value if present and onlyIfAbsent is false.
- *
* @param key the key
* @param value the value that must be associated with key
* @param onlyIfAbsent if should not insert if already present
* @return the old value, or null if newly inserted
*/
private V doPut(K key, V value, boolean onlyIfAbsent) {
+ Node<K,V> z; // added node
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
- for (;;) {
- Index<K,V> h; Node<K,V> b;
- VarHandle.acquireFence();
- int levels = 0; // number of levels descended
- if ((h = head) == null) { // try to initialize
- Node<K,V> base = new Node<K,V>(null, null, null);
- h = new Index<K,V>(base, null, null);
- b = (HEAD.compareAndSet(this, null, h)) ? base : null;
- }
- else {
- for (Index<K,V> q = h, r, d;;) { // count while descending
- while ((r = q.right) != null) {
- Node<K,V> p; K k;
- if ((p = r.node) == null || (k = p.key) == null ||
- p.val == null)
- RIGHT.compareAndSet(q, r, r.right);
- else if (cpr(cmp, key, k) > 0)
- q = r;
- else
- break;
- }
- if ((d = q.down) != null) {
- ++levels;
- q = d;
- }
- else {
- b = q.node;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ if (n != null) {
+ Object v; int c;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
break;
}
- }
- }
- if (b != null) {
- Node<K,V> z = null; // new node, if inserted
- for (;;) { // find insertion point
- Node<K,V> n, p; K k; V v; int c;
- if ((n = b.next) == null) {
- if (b.key == null) // if empty, type check key now
- cpr(cmp, key, key);
- c = -1;
- }
- else if ((k = n.key) == null)
- break; // can't append; restart
- else if ((v = n.val) == null) {
- unlinkNode(b, n);
- c = 1;
- }
- else if ((c = cpr(cmp, key, k)) > 0)
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) > 0) {
b = n;
- else if (c == 0 &&
- (onlyIfAbsent || VAL.compareAndSet(n, v, value)))
- return v;
+ n = f;
+ continue;
+ }
+ if (c == 0) {
+ if (onlyIfAbsent || n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ break; // restart if lost race to replace value
+ }
+ // else c < 0; fall through
+ }
- if (c < 0 &&
- NEXT.compareAndSet(b, n,
- p = new Node<K,V>(key, value, n))) {
- z = p;
+ z = new Node<K,V>(key, value, n);
+ if (!b.casNext(n, z))
+ break; // restart if lost race to append to b
+ break outer;
+ }
+ }
+
+ int rnd = ThreadLocalRandom.nextSecondarySeed();
+ if ((rnd & 0x80000001) == 0) { // test highest and lowest bits
+ int level = 1, max;
+ while (((rnd >>>= 1) & 1) != 0)
+ ++level;
+ Index<K,V> idx = null;
+ HeadIndex<K,V> h = head;
+ if (level <= (max = h.level)) {
+ for (int i = 1; i <= level; ++i)
+ idx = new Index<K,V>(z, idx, null);
+ }
+ else { // try to grow by one level
+ level = max + 1; // hold in array and later pick the one to use
+ @SuppressWarnings("unchecked")Index<K,V>[] idxs =
+ (Index<K,V>[])new Index<?,?>[level+1];
+ for (int i = 1; i <= level; ++i)
+ idxs[i] = idx = new Index<K,V>(z, idx, null);
+ for (;;) {
+ h = head;
+ int oldLevel = h.level;
+ if (level <= oldLevel) // lost race to add level
+ break;
+ HeadIndex<K,V> newh = h;
+ Node<K,V> oldbase = h.node;
+ for (int j = oldLevel+1; j <= level; ++j)
+ newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
+ if (casHead(h, newh)) {
+ h = newh;
+ idx = idxs[level = oldLevel];
break;
}
}
-
- if (z != null) {
- int lr = ThreadLocalRandom.nextSecondarySeed();
- if ((lr & 0x3) == 0) { // add indices with 1/4 prob
- int hr = ThreadLocalRandom.nextSecondarySeed();
- long rnd = ((long)hr << 32) | ((long)lr & 0xffffffffL);
- int skips = levels; // levels to descend before add
- Index<K,V> x = null;
- for (;;) { // create at most 62 indices
- x = new Index<K,V>(z, x, null);
- if (rnd >= 0L || --skips < 0)
+ }
+ // find insertion points and splice in
+ splice: for (int insertionLevel = level;;) {
+ int j = h.level;
+ for (Index<K,V> q = h, r = q.right, t = idx;;) {
+ if (q == null || t == null)
+ break splice;
+ if (r != null) {
+ Node<K,V> n = r.node;
+ // compare before deletion check avoids needing recheck
+ int c = cpr(cmp, key, n.key);
+ if (n.value == null) {
+ if (!q.unlink(r))
break;
- else
- rnd <<= 1;
+ r = q.right;
+ continue;
}
- if (addIndices(h, skips, x, cmp) && skips < 0 &&
- head == h) { // try to add new level
- Index<K,V> hx = new Index<K,V>(z, x, null);
- Index<K,V> nh = new Index<K,V>(h.node, h, hx);
- HEAD.compareAndSet(this, h, nh);
+ if (c > 0) {
+ q = r;
+ r = r.right;
+ continue;
}
- if (z.val == null) // deleted while adding indices
- findPredecessor(key, cmp); // clean
}
- addCount(1L);
- return null;
+
+ if (j == insertionLevel) {
+ if (!q.link(r, t))
+ break; // restart
+ if (t.node.value == null) {
+ findNode(key);
+ break splice;
+ }
+ if (--insertionLevel == 0)
+ break splice;
+ }
+
+ if (--j >= insertionLevel && j < level)
+ t = t.down;
+ q = q.down;
+ r = q.right;
}
}
}
- }
-
- /**
- * Add indices after an insertion. Descends iteratively to the
- * highest level of insertion, then recursively, to chain index
- * nodes to lower ones. Returns null on (staleness) failure,
- * disabling higher-level insertions. Recursion depths are
- * exponentially less probable.
- *
- * @param q starting index for current level
- * @param skips levels to skip before inserting
- * @param x index for this insertion
- * @param cmp comparator
- */
- static <K,V> boolean addIndices(Index<K,V> q, int skips, Index<K,V> x,
- Comparator<? super K> cmp) {
- Node<K,V> z; K key;
- if (x != null && (z = x.node) != null && (key = z.key) != null &&
- q != null) { // hoist checks
- boolean retrying = false;
- for (;;) { // find splice point
- Index<K,V> r, d; int c;
- if ((r = q.right) != null) {
- Node<K,V> p; K k;
- if ((p = r.node) == null || (k = p.key) == null ||
- p.val == null) {
- RIGHT.compareAndSet(q, r, r.right);
- c = 0;
- }
- else if ((c = cpr(cmp, key, k)) > 0)
- q = r;
- else if (c == 0)
- break; // stale
- }
- else
- c = -1;
-
- if (c < 0) {
- if ((d = q.down) != null && skips > 0) {
- --skips;
- q = d;
- }
- else if (d != null && !retrying &&
- !addIndices(d, 0, x.down, cmp))
- break;
- else {
- x.right = r;
- if (RIGHT.compareAndSet(q, r, x))
- return true;
- else
- retrying = true; // re-find splice point
- }
- }
- }
- }
- return false;
+ return null;
}
/* ---------------- Deletion -------------- */
@@ -749,6 +933,15 @@
* deletion marker, unlinks predecessor, removes associated index
* nodes, and possibly reduces head index level.
*
+ * Index nodes are cleared out simply by calling findPredecessor.
+ * which unlinks indexes to deleted nodes found along path to key,
+ * which will include the indexes to this node. This is done
+ * unconditionally. We can't check beforehand whether there are
+ * index nodes because it might be the case that some or all
+ * indexes hadn't been inserted yet for this node during initial
+ * search for it, and we'd like to ensure lack of garbage
+ * retention, so must call to be sure.
+ *
* @param key the key
* @param value if non-null, the value that must be
* associated with key
@@ -758,36 +951,43 @@
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
- V result = null;
- Node<K,V> b;
- outer: while ((b = findPredecessor(key, cmp)) != null &&
- result == null) {
- for (;;) {
- Node<K,V> n; K k; V v; int c;
- if ((n = b.next) == null)
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
+ if (n == null)
break outer;
- else if ((k = n.key) == null)
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
break;
- else if ((v = n.val) == null)
- unlinkNode(b, n);
- else if ((c = cpr(cmp, key, k)) > 0)
- b = n;
- else if (c < 0)
- break outer;
- else if (value != null && !value.equals(v))
- break outer;
- else if (VAL.compareAndSet(n, v, null)) {
- result = v;
- unlinkNode(b, n);
- break; // loop to clean up
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
}
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) < 0)
+ break outer;
+ if (c > 0) {
+ b = n;
+ n = f;
+ continue;
+ }
+ if (value != null && !value.equals(v))
+ break outer;
+ if (!n.casValue(v, null))
+ break;
+ if (!n.appendMarker(f) || !b.casNext(n, f))
+ findNode(key); // retry via findNode
+ else {
+ findPredecessor(key, cmp); // clean index
+ if (head.right == null)
+ tryReduceLevel();
+ }
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
}
}
- if (result != null) {
- tryReduceLevel();
- addCount(-1L);
- }
- return result;
+ return null;
}
/**
@@ -811,132 +1011,77 @@
* reduction.
*/
private void tryReduceLevel() {
- Index<K,V> h, d, e;
- if ((h = head) != null && h.right == null &&
- (d = h.down) != null && d.right == null &&
- (e = d.down) != null && e.right == null &&
- HEAD.compareAndSet(this, h, d) &&
- h.right != null) // recheck
- HEAD.compareAndSet(this, d, h); // try to backout
+ HeadIndex<K,V> h = head;
+ HeadIndex<K,V> d;
+ HeadIndex<K,V> e;
+ if (h.level > 3 &&
+ (d = (HeadIndex<K,V>)h.down) != null &&
+ (e = (HeadIndex<K,V>)d.down) != null &&
+ e.right == null &&
+ d.right == null &&
+ h.right == null &&
+ casHead(h, d) && // try to set
+ h.right != null) // recheck
+ casHead(d, h); // try to backout
}
/* ---------------- Finding and removing first element -------------- */
/**
- * Gets first valid node, unlinking deleted nodes if encountered.
+ * Specialized variant of findNode to get first valid node.
* @return first node or null if empty
*/
final Node<K,V> findFirst() {
- Node<K,V> b, n;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if (n.val == null)
- unlinkNode(b, n);
- else
- return n;
- }
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
+ return null;
+ if (n.value != null)
+ return n;
+ n.helpDelete(b, n.next);
}
- return null;
- }
-
- /**
- * Entry snapshot version of findFirst
- */
- final AbstractMap.SimpleImmutableEntry<K,V> findFirstEntry() {
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) == null)
- unlinkNode(b, n);
- else
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
- }
- }
- return null;
}
/**
* Removes first entry; returns its snapshot.
* @return null if empty, else snapshot of first entry
*/
- private AbstractMap.SimpleImmutableEntry<K,V> doRemoveFirstEntry() {
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) == null || VAL.compareAndSet(n, v, null)) {
- K k = n.key;
- unlinkNode(b, n);
- if (v != null) {
- tryReduceLevel();
- findPredecessor(k, comparator); // clean index
- addCount(-1L);
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
- }
- }
- }
- }
- return null;
- }
-
- /* ---------------- Finding and removing last element -------------- */
-
- /**
- * Specialized version of find to get last valid node.
- * @return last node or null if empty
- */
- final Node<K,V> findLast() {
- outer: for (;;) {
- Index<K,V> q; Node<K,V> b;
- VarHandle.acquireFence();
- if ((q = head) == null)
- break;
- for (Index<K,V> r, d;;) {
- while ((r = q.right) != null) {
- Node<K,V> p;
- if ((p = r.node) == null || p.val == null)
- RIGHT.compareAndSet(q, r, r.right);
- else
- q = r;
- }
- if ((d = q.down) != null)
- q = d;
- else {
- b = q.node;
- break;
- }
- }
- if (b != null) {
- for (;;) {
- Node<K,V> n;
- if ((n = b.next) == null) {
- if (b.key == null) // empty
- break outer;
- else
- return b;
- }
- else if (n.key == null)
- break;
- else if (n.val == null)
- unlinkNode(b, n);
- else
- b = n;
- }
- }
- }
- return null;
- }
-
- /**
- * Entry version of findLast
- * @return Entry for last node or null if empty
- */
- final AbstractMap.SimpleImmutableEntry<K,V> findLastEntry() {
- for (;;) {
- Node<K,V> n; V v;
- if ((n = findLast()) == null)
+ private Map.Entry<K,V> doRemoveFirstEntry() {
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
return null;
- if ((v = n.val) != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
+ Node<K,V> f = n.next;
+ if (n != b.next)
+ continue;
+ Object v = n.value;
+ if (v == null) {
+ n.helpDelete(b, f);
+ continue;
+ }
+ if (!n.casValue(v, null))
+ continue;
+ if (!n.appendMarker(f) || !b.casNext(n, f))
+ findFirst(); // retry
+ clearIndexToFirst();
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, vv);
+ }
+ }
+
+ /**
+ * Clears out index nodes associated with deleted first entry.
+ */
+ private void clearIndexToFirst() {
+ for (;;) {
+ for (Index<K,V> q = head;;) {
+ Index<K,V> r = q.right;
+ if (r != null && r.indexesDeletedNode() && !q.unlink(r))
+ break;
+ if ((q = q.down) == null) {
+ if (head.right == null)
+ tryReduceLevel();
+ return;
+ }
+ }
}
}
@@ -946,54 +1091,121 @@
* @return null if empty, else snapshot of last entry
*/
private Map.Entry<K,V> doRemoveLastEntry() {
- outer: for (;;) {
- Index<K,V> q; Node<K,V> b;
- VarHandle.acquireFence();
- if ((q = head) == null)
- break;
+ for (;;) {
+ Node<K,V> b = findPredecessorOfLast();
+ Node<K,V> n = b.next;
+ if (n == null) {
+ if (b.isBaseHeader()) // empty
+ return null;
+ else
+ continue; // all b's successors are deleted; retry
+ }
for (;;) {
- Index<K,V> d, r; Node<K,V> p;
- while ((r = q.right) != null) {
- if ((p = r.node) == null || p.val == null)
- RIGHT.compareAndSet(q, r, r.right);
- else if (p.next != null)
- q = r; // continue only if a successor
- else
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ Object v = n.value;
+ if (v == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if (f != null) {
+ b = n;
+ n = f;
+ continue;
+ }
+ if (!n.casValue(v, null))
+ break;
+ K key = n.key;
+ if (!n.appendMarker(f) || !b.casNext(n, f))
+ findNode(key); // retry via findNode
+ else { // clean index
+ findPredecessor(key, comparator);
+ if (head.right == null)
+ tryReduceLevel();
+ }
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
+ }
+ }
+ }
+
+ /* ---------------- Finding and removing last element -------------- */
+
+ /**
+ * Specialized version of find to get last valid node.
+ * @return last node or null if empty
+ */
+ final Node<K,V> findLast() {
+ /*
+ * findPredecessor can't be used to traverse index level
+ * because this doesn't use comparisons. So traversals of
+ * both levels are folded together.
+ */
+ Index<K,V> q = head;
+ for (;;) {
+ Index<K,V> d, r;
+ if ((r = q.right) != null) {
+ if (r.indexesDeletedNode()) {
+ q.unlink(r);
+ q = head; // restart
+ }
+ else
+ q = r;
+ } else if ((d = q.down) != null) {
+ q = d;
+ } else {
+ for (Node<K,V> b = q.node, n = b.next;;) {
+ if (n == null)
+ return b.isBaseHeader() ? null : b;
+ Node<K,V> f = n.next; // inconsistent read
+ if (n != b.next)
break;
+ Object v = n.value;
+ if (v == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ b = n;
+ n = f;
+ }
+ q = head; // restart
+ }
+ }
+ }
+
+ /**
+ * Specialized variant of findPredecessor to get predecessor of last
+ * valid node. Needed when removing the last entry. It is possible
+ * that all successors of returned node will have been deleted upon
+ * return, in which case this method can be retried.
+ * @return likely predecessor of last node
+ */
+ private Node<K,V> findPredecessorOfLast() {
+ for (;;) {
+ for (Index<K,V> q = head;;) {
+ Index<K,V> d, r;
+ if ((r = q.right) != null) {
+ if (r.indexesDeletedNode()) {
+ q.unlink(r);
+ break; // must restart
+ }
+ // proceed as far across as possible without overshooting
+ if (r.node.next != null) {
+ q = r;
+ continue;
+ }
}
if ((d = q.down) != null)
q = d;
- else {
- b = q.node;
- break;
- }
- }
- if (b != null) {
- for (;;) {
- Node<K,V> n; K k; V v;
- if ((n = b.next) == null) {
- if (b.key == null) // empty
- break outer;
- else
- break; // retry
- }
- else if ((k = n.key) == null)
- break;
- else if ((v = n.val) == null)
- unlinkNode(b, n);
- else if (n.next != null)
- b = n;
- else if (VAL.compareAndSet(n, v, null)) {
- unlinkNode(b, n);
- tryReduceLevel();
- findPredecessor(k, comparator); // clean index
- addCount(-1L);
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
- }
- }
+ else
+ return q.node;
}
}
- return null;
}
/* ---------------- Relational operations -------------- */
@@ -1013,52 +1225,47 @@
final Node<K,V> findNear(K key, int rel, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException();
- Node<K,V> result;
- outer: for (Node<K,V> b;;) {
- if ((b = findPredecessor(key, cmp)) == null) {
- result = null;
- break; // empty
- }
- for (;;) {
- Node<K,V> n; K k; int c;
- if ((n = b.next) == null) {
- result = ((rel & LT) != 0 && b.key != null) ? b : null;
- break outer;
- }
- else if ((k = n.key) == null)
+ for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v;
+ if (n == null)
+ return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
break;
- else if (n.val == null)
- unlinkNode(b, n);
- else if (((c = cpr(cmp, key, k)) == 0 && (rel & EQ) != 0) ||
- (c < 0 && (rel & LT) == 0)) {
- result = n;
- break outer;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
}
- else if (c <= 0 && (rel & LT) != 0) {
- result = (b.key != null) ? b : null;
- break outer;
- }
- else
- b = n;
+ if (b.value == null || v == n) // b is deleted
+ break;
+ int c = cpr(cmp, key, n.key);
+ if ((c == 0 && (rel & EQ) != 0) ||
+ (c < 0 && (rel & LT) == 0))
+ return n;
+ if ( c <= 0 && (rel & LT) != 0)
+ return b.isBaseHeader() ? null : b;
+ b = n;
+ n = f;
}
}
- return result;
}
/**
- * Variant of findNear returning SimpleImmutableEntry
+ * Returns SimpleImmutableEntry for results of findNear.
* @param key the key
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return Entry fitting relation, or null if no such
*/
- final AbstractMap.SimpleImmutableEntry<K,V> findNearEntry(K key, int rel,
- Comparator<? super K> cmp) {
+ final AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
+ Comparator<? super K> cmp = comparator;
for (;;) {
- Node<K,V> n; V v;
- if ((n = findNear(key, rel, cmp)) == null)
+ Node<K,V> n = findNear(key, rel, cmp);
+ if (n == null)
return null;
- if ((v = n.val) != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
+ AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
+ if (e != null)
+ return e;
}
}
@@ -1070,6 +1277,7 @@
*/
public ConcurrentSkipListMap() {
this.comparator = null;
+ initialize();
}
/**
@@ -1082,6 +1290,7 @@
*/
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
this.comparator = comparator;
+ initialize();
}
/**
@@ -1097,6 +1306,7 @@
*/
public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
this.comparator = null;
+ initialize();
putAll(m);
}
@@ -1111,7 +1321,8 @@
*/
public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
this.comparator = m.comparator();
- buildFromSorted(m); // initializes transients
+ initialize();
+ buildFromSorted(m);
}
/**
@@ -1125,10 +1336,7 @@
@SuppressWarnings("unchecked")
ConcurrentSkipListMap<K,V> clone =
(ConcurrentSkipListMap<K,V>) super.clone();
- clone.keySet = null;
- clone.entrySet = null;
- clone.values = null;
- clone.descendingMap = null;
+ clone.initialize();
clone.buildFromSorted(this);
return clone;
} catch (CloneNotSupportedException e) {
@@ -1144,49 +1352,58 @@
private void buildFromSorted(SortedMap<K, ? extends V> map) {
if (map == null)
throw new NullPointerException();
+
+ HeadIndex<K,V> h = head;
+ Node<K,V> basepred = h.node;
+
+ // Track the current rightmost node at each level. Uses an
+ // ArrayList to avoid committing to initial or maximum level.
+ ArrayList<Index<K,V>> preds = new ArrayList<>();
+
+ // initialize
+ for (int i = 0; i <= h.level; ++i)
+ preds.add(null);
+ Index<K,V> q = h;
+ for (int i = h.level; i > 0; --i) {
+ preds.set(i, q);
+ q = q.down;
+ }
+
Iterator<? extends Map.Entry<? extends K, ? extends V>> it =
map.entrySet().iterator();
-
- /*
- * Add equally spaced indices at log intervals, using the bits
- * of count during insertion. The maximum possible resulting
- * level is less than the number of bits in a long (64). The
- * preds array tracks the current rightmost node at each
- * level.
- */
- @SuppressWarnings("unchecked")
- Index<K,V>[] preds = (Index<K,V>[])new Index<?,?>[64];
- Node<K,V> bp = new Node<K,V>(null, null, null);
- Index<K,V> h = preds[0] = new Index<K,V>(bp, null, null);
- long count = 0;
-
while (it.hasNext()) {
Map.Entry<? extends K, ? extends V> e = it.next();
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
+ do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
K k = e.getKey();
V v = e.getValue();
if (k == null || v == null)
throw new NullPointerException();
Node<K,V> z = new Node<K,V>(k, v, null);
- bp = bp.next = z;
- if ((++count & 3L) == 0L) {
- long m = count >>> 2;
- int i = 0;
- Index<K,V> idx = null, q;
- do {
+ basepred.next = z;
+ basepred = z;
+ if (j > 0) {
+ Index<K,V> idx = null;
+ for (int i = 1; i <= j; ++i) {
idx = new Index<K,V>(z, idx, null);
- if ((q = preds[i]) == null)
- preds[i] = h = new Index<K,V>(h.node, h, idx);
- else
- preds[i] = q.right = idx;
- } while (++i < preds.length && ((m >>>= 1) & 1L) != 0L);
+ if (i > h.level)
+ h = new HeadIndex<K,V>(h.node, h, idx, i);
+
+ if (i < preds.size()) {
+ preds.get(i).right = idx;
+ preds.set(i, idx);
+ } else
+ preds.add(idx);
+ }
}
}
- if (count != 0L) {
- VarHandle.releaseFence(); // emulate volatile stores
- addCount(count);
- head = h;
- VarHandle.fullFence();
- }
+ head = h;
}
/* ---------------- Serialization -------------- */
@@ -1208,14 +1425,11 @@
s.defaultWriteObject();
// Write out keys and values (alternating)
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) != null) {
- s.writeObject(n.key);
- s.writeObject(v);
- }
- b = n;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v = n.getValidValue();
+ if (v != null) {
+ s.writeObject(n.key);
+ s.writeObject(v);
}
}
s.writeObject(null);
@@ -1233,47 +1447,64 @@
throws java.io.IOException, ClassNotFoundException {
// Read in the Comparator and any hidden stuff
s.defaultReadObject();
+ // Reset transients
+ initialize();
- // Same idea as buildFromSorted
- @SuppressWarnings("unchecked")
- Index<K,V>[] preds = (Index<K,V>[])new Index<?,?>[64];
- Node<K,V> bp = new Node<K,V>(null, null, null);
- Index<K,V> h = preds[0] = new Index<K,V>(bp, null, null);
- Comparator<? super K> cmp = comparator;
- K prevKey = null;
- long count = 0;
+ /*
+ * This is nearly identical to buildFromSorted, but is
+ * distinct because readObject calls can't be nicely adapted
+ * as the kind of iterator needed by buildFromSorted. (They
+ * can be, but doing so requires type cheats and/or creation
+ * of adapter classes.) It is simpler to just adapt the code.
+ */
+
+ HeadIndex<K,V> h = head;
+ Node<K,V> basepred = h.node;
+ ArrayList<Index<K,V>> preds = new ArrayList<>();
+ for (int i = 0; i <= h.level; ++i)
+ preds.add(null);
+ Index<K,V> q = h;
+ for (int i = h.level; i > 0; --i) {
+ preds.set(i, q);
+ q = q.down;
+ }
for (;;) {
- K k = (K)s.readObject();
+ Object k = s.readObject();
if (k == null)
break;
- V v = (V)s.readObject();
+ Object v = s.readObject();
if (v == null)
throw new NullPointerException();
- if (prevKey != null && cpr(cmp, prevKey, k) > 0)
- throw new IllegalStateException("out of order");
- prevKey = k;
- Node<K,V> z = new Node<K,V>(k, v, null);
- bp = bp.next = z;
- if ((++count & 3L) == 0L) {
- long m = count >>> 2;
- int i = 0;
- Index<K,V> idx = null, q;
+ K key = (K) k;
+ V val = (V) v;
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
+ Node<K,V> z = new Node<K,V>(key, val, null);
+ basepred.next = z;
+ basepred = z;
+ if (j > 0) {
+ Index<K,V> idx = null;
+ for (int i = 1; i <= j; ++i) {
idx = new Index<K,V>(z, idx, null);
- if ((q = preds[i]) == null)
- preds[i] = h = new Index<K,V>(h.node, h, idx);
- else
- preds[i] = q.right = idx;
- } while (++i < preds.length && ((m >>>= 1) & 1L) != 0L);
+ if (i > h.level)
+ h = new HeadIndex<K,V>(h.node, h, idx, i);
+
+ if (i < preds.size()) {
+ preds.get(i).right = idx;
+ preds.set(i, idx);
+ } else
+ preds.add(idx);
+ }
}
}
- if (count != 0L) {
- VarHandle.releaseFence();
- addCount(count);
- head = h;
- VarHandle.fullFence();
- }
+ head = h;
}
/* ------ Map API methods ------ */
@@ -1374,30 +1605,42 @@
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) != null && value.equals(v))
- return true;
- else
- b = n;
- }
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v = n.getValidValue();
+ if (v != null && value.equals(v))
+ return true;
}
return false;
}
/**
- * {@inheritDoc}
+ * Returns the number of key-value mappings in this map. If this map
+ * contains more than {@code Integer.MAX_VALUE} elements, it
+ * returns {@code Integer.MAX_VALUE}.
+ *
+ * <p>Beware that, unlike in most collections, this method is
+ * <em>NOT</em> a constant-time operation. Because of the
+ * asynchronous nature of these maps, determining the current
+ * number of elements requires traversing them all to count them.
+ * Additionally, it is possible for the size to change during
+ * execution of this method, in which case the returned result
+ * will be inaccurate. Thus, this method is typically not very
+ * useful in concurrent applications.
+ *
+ * @return the number of elements in this map
*/
public int size() {
- long c;
- return ((baseHead() == null) ? 0 :
- ((c = getAdderCount()) >= Integer.MAX_VALUE) ?
- Integer.MAX_VALUE : (int) c);
+ long count = 0;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ if (n.getValidValue() != null)
+ ++count;
+ }
+ return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
}
/**
- * {@inheritDoc}
+ * Returns {@code true} if this map contains no key-value mappings.
+ * @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty() {
return findFirst() == null;
@@ -1407,33 +1650,7 @@
* Removes all of the mappings from this map.
*/
public void clear() {
- Index<K,V> h, r, d; Node<K,V> b;
- VarHandle.acquireFence();
- while ((h = head) != null) {
- if ((r = h.right) != null) // remove indices
- RIGHT.compareAndSet(h, r, null);
- else if ((d = h.down) != null) // remove levels
- HEAD.compareAndSet(this, h, d);
- else {
- long count = 0L;
- if ((b = h.node) != null) { // remove nodes
- Node<K,V> n; V v;
- while ((n = b.next) != null) {
- if ((v = n.val) != null &&
- VAL.compareAndSet(n, v, null)) {
- --count;
- v = null;
- }
- if (v == null)
- unlinkNode(b, n);
- }
- }
- if (count != 0L)
- addCount(count);
- else
- break;
- }
- }
+ initialize();
}
/**
@@ -1479,15 +1696,16 @@
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (key == null || remappingFunction == null)
throw new NullPointerException();
- Node<K,V> n; V v;
+ Node<K,V> n; Object v;
while ((n = findNode(key)) != null) {
- if ((v = n.val) != null) {
- V r = remappingFunction.apply(key, v);
+ if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ V r = remappingFunction.apply(key, vv);
if (r != null) {
- if (VAL.compareAndSet(n, v, r))
+ if (n.casValue(vv, r))
return r;
}
- else if (doRemove(key, v) != null)
+ else if (doRemove(key, vv) != null)
break;
}
}
@@ -1512,19 +1730,20 @@
if (key == null || remappingFunction == null)
throw new NullPointerException();
for (;;) {
- Node<K,V> n; V v; V r;
+ Node<K,V> n; Object v; V r;
if ((n = findNode(key)) == null) {
if ((r = remappingFunction.apply(key, null)) == null)
break;
if (doPut(key, r, true) == null)
return r;
}
- else if ((v = n.val) != null) {
- if ((r = remappingFunction.apply(key, v)) != null) {
- if (VAL.compareAndSet(n, v, r))
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(key, vv)) != null) {
+ if (n.casValue(vv, r))
return r;
}
- else if (doRemove(key, v) != null)
+ else if (doRemove(key, vv) != null)
break;
}
}
@@ -1551,17 +1770,18 @@
if (key == null || value == null || remappingFunction == null)
throw new NullPointerException();
for (;;) {
- Node<K,V> n; V v; V r;
+ Node<K,V> n; Object v; V r;
if ((n = findNode(key)) == null) {
if (doPut(key, value, true) == null)
return value;
}
- else if ((v = n.val) != null) {
- if ((r = remappingFunction.apply(v, value)) != null) {
- if (VAL.compareAndSet(n, v, r))
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(vv, value)) != null) {
+ if (n.casValue(vv, r))
return r;
}
- else if (doRemove(key, v) != null)
+ else if (doRemove(key, vv) != null)
return null;
}
}
@@ -1585,11 +1805,9 @@
* The set's spliterator additionally reports {@link Spliterator#CONCURRENT},
* {@link Spliterator#NONNULL}, {@link Spliterator#SORTED} and
* {@link Spliterator#ORDERED}, with an encounter order that is ascending
- * key order.
- *
- * <p>The {@linkplain Spliterator#getComparator() spliterator's comparator}
- * is {@code null} if the {@linkplain #comparator() map's comparator}
- * is {@code null}.
+ * key order. The spliterator's comparator (see
+ * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+ * the map's comparator (see {@link #comparator()}) is {@code null}.
* Otherwise, the spliterator's comparator is the same as or imposes the
* same total ordering as the map's comparator.
*
@@ -1609,15 +1827,13 @@
* @return a navigable set view of the keys in this map
*/
public NavigableSet<K> keySet() {
- KeySet<K,V> ks;
- if ((ks = keySet) != null) return ks;
- return keySet = new KeySet<>(this);
+ KeySet<K,V> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet<K,V> ks;
- if ((ks = keySet) != null) return ks;
- return keySet = new KeySet<>(this);
+ KeySet<K,V> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<>(this));
}
/**
@@ -1640,9 +1856,8 @@
* <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*/
public Collection<V> values() {
- Values<K,V> vs;
- if ((vs = values) != null) return vs;
- return values = new Values<>(this);
+ Values<K,V> vs = values;
+ return (vs != null) ? vs : (values = new Values<>(this));
}
/**
@@ -1673,16 +1888,14 @@
* sorted in ascending key order
*/
public Set<Map.Entry<K,V>> entrySet() {
- EntrySet<K,V> es;
- if ((es = entrySet) != null) return es;
- return entrySet = new EntrySet<K,V>(this);
+ EntrySet<K,V> es = entrySet;
+ return (es != null) ? es : (entrySet = new EntrySet<K,V>(this));
}
public ConcurrentNavigableMap<K,V> descendingMap() {
- ConcurrentNavigableMap<K,V> dm;
- if ((dm = descendingMap) != null) return dm;
- return descendingMap =
- new SubMap<K,V>(this, null, false, null, false, true);
+ ConcurrentNavigableMap<K,V> dm = descendingMap;
+ return (dm != null) ? dm : (descendingMap = new SubMap<K,V>
+ (this, null, false, null, false, true));
}
public NavigableSet<K> descendingKeySet() {
@@ -1710,61 +1923,19 @@
return false;
Map<?,?> m = (Map<?,?>) o;
try {
- Comparator<? super K> cmp = comparator;
- @SuppressWarnings("unchecked")
- Iterator<Map.Entry<?,?>> it =
- (Iterator<Map.Entry<?,?>>)m.entrySet().iterator();
- if (m instanceof SortedMap &&
- ((SortedMap<?,?>)m).comparator() == cmp) {
- Node<K,V> b, n;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- K k; V v;
- if ((v = n.val) != null && (k = n.key) != null) {
- if (!it.hasNext())
- return false;
- Map.Entry<?,?> e = it.next();
- Object mk = e.getKey();
- Object mv = e.getValue();
- if (mk == null || mv == null)
- return false;
- try {
- if (cpr(cmp, k, mk) != 0)
- return false;
- } catch (ClassCastException cce) {
- return false;
- }
- if (!mv.equals(v))
- return false;
- }
- b = n;
- }
- }
- return !it.hasNext();
+ for (Map.Entry<K,V> e : this.entrySet())
+ if (! e.getValue().equals(m.get(e.getKey())))
+ return false;
+ for (Map.Entry<?,?> e : m.entrySet()) {
+ Object k = e.getKey();
+ Object v = e.getValue();
+ if (k == null || v == null || !v.equals(get(k)))
+ return false;
}
- else {
- while (it.hasNext()) {
- V v;
- Map.Entry<?,?> e = it.next();
- Object mk = e.getKey();
- Object mv = e.getValue();
- if (mk == null || mv == null ||
- (v = get(mk)) == null || !v.equals(mv))
- return false;
- }
- Node<K,V> b, n;
- if ((b = baseHead()) != null) {
- K k; V v; Object mv;
- while ((n = b.next) != null) {
- if ((v = n.val) != null && (k = n.key) != null &&
- ((mv = m.get(k)) == null || !mv.equals(v)))
- return false;
- b = n;
- }
- }
- return true;
- }
- } catch (ClassCastException | NullPointerException unused) {
+ return true;
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
return false;
}
}
@@ -1810,13 +1981,13 @@
if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
for (;;) {
- Node<K,V> n; V v;
+ Node<K,V> n; Object v;
if ((n = findNode(key)) == null)
return false;
- if ((v = n.val) != null) {
+ if ((v = n.value) != null) {
if (!oldValue.equals(v))
return false;
- if (VAL.compareAndSet(n, v, newValue))
+ if (n.casValue(v, newValue))
return true;
}
}
@@ -1835,11 +2006,13 @@
if (key == null || value == null)
throw new NullPointerException();
for (;;) {
- Node<K,V> n; V v;
+ Node<K,V> n; Object v;
if ((n = findNode(key)) == null)
return null;
- if ((v = n.val) != null && VAL.compareAndSet(n, v, value))
- return v;
+ if ((v = n.value) != null && n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
}
}
@@ -1949,7 +2122,7 @@
* @throws NullPointerException if the specified key is null
*/
public Map.Entry<K,V> lowerEntry(K key) {
- return findNearEntry(key, LT, comparator);
+ return getNear(key, LT);
}
/**
@@ -1972,7 +2145,7 @@
* @throws NullPointerException if the specified key is null
*/
public Map.Entry<K,V> floorEntry(K key) {
- return findNearEntry(key, LT|EQ, comparator);
+ return getNear(key, LT|EQ);
}
/**
@@ -1995,7 +2168,7 @@
* @throws NullPointerException if the specified key is null
*/
public Map.Entry<K,V> ceilingEntry(K key) {
- return findNearEntry(key, GT|EQ, comparator);
+ return getNear(key, GT|EQ);
}
/**
@@ -2018,7 +2191,7 @@
* @throws NullPointerException if the specified key is null
*/
public Map.Entry<K,V> higherEntry(K key) {
- return findNearEntry(key, GT, comparator);
+ return getNear(key, GT);
}
/**
@@ -2038,7 +2211,14 @@
* the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> firstEntry() {
- return findFirstEntry();
+ for (;;) {
+ Node<K,V> n = findFirst();
+ if (n == null)
+ return null;
+ AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
+ if (e != null)
+ return e;
+ }
}
/**
@@ -2048,7 +2228,14 @@
* the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> lastEntry() {
- return findLastEntry();
+ for (;;) {
+ Node<K,V> n = findLast();
+ if (n == null)
+ return null;
+ AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
+ if (e != null)
+ return e;
+ }
}
/**
@@ -2071,10 +2258,11 @@
return doRemoveLastEntry();
}
+
/* ---------------- Iterators -------------- */
/**
- * Base of iterator classes
+ * Base of iterator classes:
*/
abstract class Iter<T> implements Iterator<T> {
/** the last node returned by next() */
@@ -2086,7 +2274,14 @@
/** Initializes ascending iterator for entire range. */
Iter() {
- advance(baseHead());
+ while ((next = findFirst()) != null) {
+ Object x = next.value;
+ if (x != null && x != next) {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ break;
+ }
+ }
}
public final boolean hasNext() {
@@ -2094,58 +2289,54 @@
}
/** Advances next to higher entry. */
- final void advance(Node<K,V> b) {
- Node<K,V> n = null;
- V v = null;
- if ((lastReturned = b) != null) {
- while ((n = b.next) != null && (v = n.val) == null)
- b = n;
+ final void advance() {
+ if (next == null)
+ throw new NoSuchElementException();
+ lastReturned = next;
+ while ((next = next.next) != null) {
+ Object x = next.value;
+ if (x != null && x != next) {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ break;
+ }
}
- nextValue = v;
- next = n;
}
- public final void remove() {
- Node<K,V> n; K k;
- if ((n = lastReturned) == null || (k = n.key) == null)
+ public void remove() {
+ Node<K,V> l = lastReturned;
+ if (l == null)
throw new IllegalStateException();
// It would not be worth all of the overhead to directly
// unlink from here. Using remove is fast enough.
- ConcurrentSkipListMap.this.remove(k);
+ ConcurrentSkipListMap.this.remove(l.key);
lastReturned = null;
}
+
}
final class ValueIterator extends Iter<V> {
public V next() {
- V v;
- if ((v = nextValue) == null)
- throw new NoSuchElementException();
- advance(next);
+ V v = nextValue;
+ advance();
return v;
}
}
final class KeyIterator extends Iter<K> {
public K next() {
- Node<K,V> n;
- if ((n = next) == null)
- throw new NoSuchElementException();
- K k = n.key;
- advance(n);
- return k;
+ Node<K,V> n = next;
+ advance();
+ return n.key;
}
}
final class EntryIterator extends Iter<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
- Node<K,V> n;
- if ((n = next) == null)
- throw new NoSuchElementException();
- K k = n.key;
+ Node<K,V> n = next;
V v = nextValue;
- advance(n);
- return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
+ advance();
+ return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
}
}
@@ -2202,7 +2393,9 @@
Collection<?> c = (Collection<?>) o;
try {
return containsAll(c) && c.containsAll(this);
- } catch (ClassCastException | NullPointerException unused) {
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
return false;
}
}
@@ -2327,7 +2520,9 @@
Collection<?> c = (Collection<?>) o;
try {
return containsAll(c) && c.containsAll(this);
- } catch (ClassCastException | NullPointerException unused) {
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
return false;
}
}
@@ -2369,7 +2564,7 @@
* @serial include
*/
static final class SubMap<K,V> extends AbstractMap<K,V>
- implements ConcurrentNavigableMap<K,V>, Serializable {
+ implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {
private static final long serialVersionUID = -7647078645895051609L;
/** Underlying map */
@@ -2387,8 +2582,8 @@
// Lazily initialized view holders
private transient KeySet<K,V> keySetView;
- private transient Values<K,V> valuesView;
- private transient EntrySet<K,V> entrySetView;
+ private transient Set<Map.Entry<K,V>> entrySetView;
+ private transient Collection<V> valuesView;
/**
* Creates a new submap, initializing all fields.
@@ -2447,7 +2642,9 @@
if (k == null) // pass by markers and headers
return true;
int c = cpr(cmp, k, hi);
- return c < 0 || (c == 0 && hiInclusive);
+ if (c > 0 || (c == 0 && !hiInclusive))
+ return false;
+ return true;
}
/**
@@ -2505,34 +2702,38 @@
Map.Entry<K,V> lowestEntry() {
Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n; V v;
- if ((n = loNode(cmp)) == null || !isBeforeEnd(n, cmp))
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (!isBeforeEnd(n, cmp))
return null;
- else if ((v = n.val) != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
+ Map.Entry<K,V> e = n.createSnapshot();
+ if (e != null)
+ return e;
}
}
Map.Entry<K,V> highestEntry() {
Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n; V v;
- if ((n = hiNode(cmp)) == null || !inBounds(n.key, cmp))
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
- else if ((v = n.val) != null)
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
+ Map.Entry<K,V> e = n.createSnapshot();
+ if (e != null)
+ return e;
}
}
Map.Entry<K,V> removeLowest() {
Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n; K k; V v;
- if ((n = loNode(cmp)) == null)
+ Node<K,V> n = loNode(cmp);
+ if (n == null)
return null;
- else if (!inBounds((k = n.key), cmp))
+ K k = n.key;
+ if (!inBounds(k, cmp))
return null;
- else if ((v = m.doRemove(k, null)) != null)
+ V v = m.doRemove(k, null);
+ if (v != null)
return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
}
}
@@ -2540,18 +2741,20 @@
Map.Entry<K,V> removeHighest() {
Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n; K k; V v;
- if ((n = hiNode(cmp)) == null)
+ Node<K,V> n = hiNode(cmp);
+ if (n == null)
return null;
- else if (!inBounds((k = n.key), cmp))
+ K k = n.key;
+ if (!inBounds(k, cmp))
return null;
- else if ((v = m.doRemove(k, null)) != null)
+ V v = m.doRemove(k, null);
+ if (v != null)
return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
}
}
/**
- * Submap version of ConcurrentSkipListMap.findNearEntry.
+ * Submap version of ConcurrentSkipListMap.getNearEntry.
*/
Map.Entry<K,V> getNearEntry(K key, int rel) {
Comparator<? super K> cmp = m.comparator;
@@ -2565,12 +2768,15 @@
return ((rel & LT) != 0) ? null : lowestEntry();
if (tooHigh(key, cmp))
return ((rel & LT) != 0) ? highestEntry() : null;
- AbstractMap.SimpleImmutableEntry<K,V> e =
- m.findNearEntry(key, rel, cmp);
- if (e == null || !inBounds(e.getKey(), cmp))
- return null;
- else
- return e;
+ for (;;) {
+ Node<K,V> n = m.findNear(key, rel, cmp);
+ if (n == null || !inBounds(n.key, cmp))
+ return null;
+ K k = n.key;
+ V v = n.getValidValue();
+ if (v != null)
+ return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);
+ }
}
// Almost the same as getNearEntry, except for keys
@@ -2605,8 +2811,10 @@
Node<K,V> n = m.findNear(key, rel, cmp);
if (n == null || !inBounds(n.key, cmp))
return null;
- if (n.val != null)
- return n.key;
+ K k = n.key;
+ V v = n.getValidValue();
+ if (v != null)
+ return k;
}
}
@@ -2637,7 +2845,7 @@
for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
isBeforeEnd(n, cmp);
n = n.next) {
- if (n.val != null)
+ if (n.getValidValue() != null)
++count;
}
return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count;
@@ -2655,7 +2863,7 @@
for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
isBeforeEnd(n, cmp);
n = n.next) {
- V v = n.val;
+ V v = n.getValidValue();
if (v != null && value.equals(v))
return true;
}
@@ -2667,7 +2875,7 @@
for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
isBeforeEnd(n, cmp);
n = n.next) {
- if (n.val != null)
+ if (n.getValidValue() != null)
m.remove(n.key);
}
}
@@ -2841,27 +3049,23 @@
/* ---------------- Submap Views -------------- */
public NavigableSet<K> keySet() {
- KeySet<K,V> ks;
- if ((ks = keySetView) != null) return ks;
- return keySetView = new KeySet<>(this);
+ KeySet<K,V> ks = keySetView;
+ return (ks != null) ? ks : (keySetView = new KeySet<>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet<K,V> ks;
- if ((ks = keySetView) != null) return ks;
- return keySetView = new KeySet<>(this);
+ KeySet<K,V> ks = keySetView;
+ return (ks != null) ? ks : (keySetView = new KeySet<>(this));
}
public Collection<V> values() {
- Values<K,V> vs;
- if ((vs = valuesView) != null) return vs;
- return valuesView = new Values<>(this);
+ Collection<V> vs = valuesView;
+ return (vs != null) ? vs : (valuesView = new Values<>(this));
}
public Set<Map.Entry<K,V>> entrySet() {
- EntrySet<K,V> es;
- if ((es = entrySetView) != null) return es;
- return entrySetView = new EntrySet<K,V>(this);
+ Set<Map.Entry<K,V>> es = entrySetView;
+ return (es != null) ? es : (entrySetView = new EntrySet<K,V>(this));
}
public NavigableSet<K> descendingKeySet() {
@@ -2881,18 +3085,19 @@
V nextValue;
SubMapIter() {
- VarHandle.acquireFence();
Comparator<? super K> cmp = m.comparator;
for (;;) {
next = isDescending ? hiNode(cmp) : loNode(cmp);
if (next == null)
break;
- V x = next.val;
- if (x != null) {
+ Object x = next.value;
+ if (x != null && x != next) {
if (! inBounds(next.key, cmp))
next = null;
- else
- nextValue = x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -2918,12 +3123,14 @@
next = next.next;
if (next == null)
break;
- V x = next.val;
- if (x != null) {
+ Object x = next.value;
+ if (x != null && x != next) {
if (tooHigh(next.key, cmp))
next = null;
- else
- nextValue = x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -2935,12 +3142,14 @@
next = m.findNear(lastReturned.key, LT, cmp);
if (next == null)
break;
- V x = next.val;
- if (x != null) {
+ Object x = next.value;
+ if (x != null && x != next) {
if (tooLow(next.key, cmp))
next = null;
- else
- nextValue = x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -3020,28 +3229,22 @@
public void forEach(BiConsumer<? super K, ? super V> action) {
if (action == null) throw new NullPointerException();
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) != null)
- action.accept(n.key, v);
- b = n;
- }
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ if ((v = n.getValidValue()) != null)
+ action.accept(n.key, v);
}
}
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
if (function == null) throw new NullPointerException();
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- while ((v = n.val) != null) {
- V r = function.apply(n.key, v);
- if (r == null) throw new NullPointerException();
- if (VAL.compareAndSet(n, v, r))
- break;
- }
- b = n;
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ while ((v = n.getValidValue()) != null) {
+ V r = function.apply(n.key, v);
+ if (r == null) throw new NullPointerException();
+ if (n.casValue(v, r))
+ break;
}
}
}
@@ -3052,16 +3255,13 @@
boolean removeEntryIf(Predicate<? super Entry<K,V>> function) {
if (function == null) throw new NullPointerException();
boolean removed = false;
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) != null) {
- K k = n.key;
- Map.Entry<K,V> e = new AbstractMap.SimpleImmutableEntry<>(k, v);
- if (function.test(e) && remove(k, v))
- removed = true;
- }
- b = n;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v;
+ if ((v = n.getValidValue()) != null) {
+ K k = n.key;
+ Map.Entry<K,V> e = new AbstractMap.SimpleImmutableEntry<>(k, v);
+ if (function.test(e) && remove(k, v))
+ removed = true;
}
}
return removed;
@@ -3073,12 +3273,12 @@
boolean removeValueIf(Predicate<? super V> function) {
if (function == null) throw new NullPointerException();
boolean removed = false;
- Node<K,V> b, n; V v;
- if ((b = baseHead()) != null) {
- while ((n = b.next) != null) {
- if ((v = n.val) != null && function.test(v) && remove(n.key, v))
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v;
+ if ((v = n.getValidValue()) != null) {
+ K k = n.key;
+ if (function.test(v) && remove(k, v))
removed = true;
- b = n;
}
}
return removed;
@@ -3096,27 +3296,30 @@
* off, or the end of row is encountered. Control of the number of
* splits relies on some statistical estimation: The expected
* remaining number of elements of a skip list when advancing
- * either across or down decreases by about 25%.
+ * either across or down decreases by about 25%. To make this
+ * observation useful, we need to know initial size, which we
+ * don't. But we can just use Integer.MAX_VALUE so that we
+ * don't prematurely zero out while splitting.
*/
abstract static class CSLMSpliterator<K,V> {
final Comparator<? super K> comparator;
final K fence; // exclusive upper bound for keys, or null if to end
Index<K,V> row; // the level to split out
Node<K,V> current; // current traversal node; initialize at origin
- long est; // size estimate
+ int est; // pseudo-size estimate
CSLMSpliterator(Comparator<? super K> comparator, Index<K,V> row,
- Node<K,V> origin, K fence, long est) {
+ Node<K,V> origin, K fence, int est) {
this.comparator = comparator; this.row = row;
this.current = origin; this.fence = fence; this.est = est;
}
- public final long estimateSize() { return est; }
+ public final long estimateSize() { return (long)est; }
}
static final class KeySpliterator<K,V> extends CSLMSpliterator<K,V>
implements Spliterator<K> {
KeySpliterator(Comparator<? super K> comparator, Index<K,V> row,
- Node<K,V> origin, K fence, long est) {
+ Node<K,V> origin, K fence, int est) {
super(comparator, row, origin, fence, est);
}
@@ -3128,7 +3331,7 @@
for (Index<K,V> q = row; q != null; q = row = q.down) {
Index<K,V> s; Node<K,V> b, n; K sk;
if ((s = q.right) != null && (b = s.node) != null &&
- (n = b.next) != null && n.val != null &&
+ (n = b.next) != null && n.value != null &&
(sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
(f == null || cpr(cmp, sk, f) < 0)) {
current = n;
@@ -3149,10 +3352,10 @@
Node<K,V> e = current;
current = null;
for (; e != null; e = e.next) {
- K k;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
break;
- if (e.val != null)
+ if ((v = e.value) != null && v != e)
action.accept(k);
}
}
@@ -3163,12 +3366,12 @@
K f = fence;
Node<K,V> e = current;
for (; e != null; e = e.next) {
- K k;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
e = null;
break;
}
- if (e.val != null) {
+ if ((v = e.value) != null && v != e) {
current = e.next;
action.accept(k);
return true;
@@ -3190,23 +3393,21 @@
}
// factory method for KeySpliterator
final KeySpliterator<K,V> keySpliterator() {
- Index<K,V> h; Node<K,V> n; long est;
- VarHandle.acquireFence();
- if ((h = head) == null) {
- n = null;
- est = 0L;
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // ensure h corresponds to origin p
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new KeySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
}
- else {
- n = h.node;
- est = getAdderCount();
- }
- return new KeySpliterator<K,V>(comparator, h, n, null, est);
}
static final class ValueSpliterator<K,V> extends CSLMSpliterator<K,V>
implements Spliterator<V> {
ValueSpliterator(Comparator<? super K> comparator, Index<K,V> row,
- Node<K,V> origin, K fence, long est) {
+ Node<K,V> origin, K fence, int est) {
super(comparator, row, origin, fence, est);
}
@@ -3218,7 +3419,7 @@
for (Index<K,V> q = row; q != null; q = row = q.down) {
Index<K,V> s; Node<K,V> b, n; K sk;
if ((s = q.right) != null && (b = s.node) != null &&
- (n = b.next) != null && n.val != null &&
+ (n = b.next) != null && n.value != null &&
(sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
(f == null || cpr(cmp, sk, f) < 0)) {
current = n;
@@ -3239,11 +3440,13 @@
Node<K,V> e = current;
current = null;
for (; e != null; e = e.next) {
- K k; V v;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
break;
- if ((v = e.val) != null)
- action.accept(v);
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
+ }
}
}
@@ -3253,14 +3456,15 @@
K f = fence;
Node<K,V> e = current;
for (; e != null; e = e.next) {
- K k; V v;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
e = null;
break;
}
- if ((v = e.val) != null) {
+ if ((v = e.value) != null && v != e) {
current = e.next;
- action.accept(v);
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
return true;
}
}
@@ -3276,23 +3480,21 @@
// Almost the same as keySpliterator()
final ValueSpliterator<K,V> valueSpliterator() {
- Index<K,V> h; Node<K,V> n; long est;
- VarHandle.acquireFence();
- if ((h = head) == null) {
- n = null;
- est = 0L;
+ Comparator<? super K> cmp = comparator;
+ for (;;) {
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new ValueSpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
}
- else {
- n = h.node;
- est = getAdderCount();
- }
- return new ValueSpliterator<K,V>(comparator, h, n, null, est);
}
static final class EntrySpliterator<K,V> extends CSLMSpliterator<K,V>
implements Spliterator<Map.Entry<K,V>> {
EntrySpliterator(Comparator<? super K> comparator, Index<K,V> row,
- Node<K,V> origin, K fence, long est) {
+ Node<K,V> origin, K fence, int est) {
super(comparator, row, origin, fence, est);
}
@@ -3304,7 +3506,7 @@
for (Index<K,V> q = row; q != null; q = row = q.down) {
Index<K,V> s; Node<K,V> b, n; K sk;
if ((s = q.right) != null && (b = s.node) != null &&
- (n = b.next) != null && n.val != null &&
+ (n = b.next) != null && n.value != null &&
(sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
(f == null || cpr(cmp, sk, f) < 0)) {
current = n;
@@ -3325,12 +3527,13 @@
Node<K,V> e = current;
current = null;
for (; e != null; e = e.next) {
- K k; V v;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
break;
- if ((v = e.val) != null) {
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
action.accept
- (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
}
}
}
@@ -3341,15 +3544,16 @@
K f = fence;
Node<K,V> e = current;
for (; e != null; e = e.next) {
- K k; V v;
+ K k; Object v;
if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
e = null;
break;
}
- if ((v = e.val) != null) {
+ if ((v = e.value) != null && v != e) {
current = e.next;
+ @SuppressWarnings("unchecked") V vv = (V)v;
action.accept
- (new AbstractMap.SimpleImmutableEntry<K,V>(k, v));
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
return true;
}
}
@@ -3380,37 +3584,26 @@
// Almost the same as keySpliterator()
final EntrySpliterator<K,V> entrySpliterator() {
- Index<K,V> h; Node<K,V> n; long est;
- VarHandle.acquireFence();
- if ((h = head) == null) {
- n = null;
- est = 0L;
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // almost same as key version
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new EntrySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
}
- else {
- n = h.node;
- est = getAdderCount();
- }
- return new EntrySpliterator<K,V>(comparator, h, n, null, est);
}
- // VarHandle mechanics
- private static final VarHandle HEAD;
- private static final VarHandle ADDER;
- private static final VarHandle NEXT;
- private static final VarHandle VAL;
- private static final VarHandle RIGHT;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head",
- Index.class);
- ADDER = l.findVarHandle(ConcurrentSkipListMap.class, "adder",
- LongAdder.class);
- NEXT = l.findVarHandle(Node.class, "next", Node.class);
- VAL = l.findVarHandle(Node.class, "val", Object.class);
- RIGHT = l.findVarHandle(Index.class, "right", Index.class);
+ HEAD = U.objectFieldOffset
+ (ConcurrentSkipListMap.class.getDeclaredField("head"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
index 140bde4..2e11b17 100644
--- a/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
+++ b/ojluni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
@@ -35,18 +35,23 @@
package java.util.concurrent;
-import java.lang.reflect.Field;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
+import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.Spliterator;
+// BEGIN android-note
+// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
+// END android-note
+
/**
* A scalable concurrent {@link NavigableSet} implementation based on
* a {@link ConcurrentSkipListMap}. The elements of the set are kept
@@ -70,12 +75,12 @@
* asynchronous nature of these sets, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
- *
- * <p>Bulk operations that add, remove, or examine multiple elements,
- * such as {@link #addAll}, {@link #removeIf} or {@link #forEach},
- * are <em>not</em> guaranteed to be performed atomically.
- * For example, a {@code forEach} traversal concurrent with an {@code
- * addAll} operation might observe only some of the added elements.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
* <p>This class and its iterators implement all of the
* <em>optional</em> methods of the {@link Set} and {@link Iterator}
@@ -84,10 +89,6 @@
* because {@code null} arguments and return values cannot be reliably
* distinguished from the absence of elements.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @author Doug Lea
* @param <E> the type of elements maintained by this set
* @since 1.6
@@ -309,7 +310,9 @@
Collection<?> c = (Collection<?>) o;
try {
return containsAll(c) && c.containsAll(this);
- } catch (ClassCastException | NullPointerException unused) {
+ } catch (ClassCastException unused) {
+ return false;
+ } catch (NullPointerException unused) {
return false;
}
}
@@ -324,7 +327,7 @@
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the class of an element of this set
* is incompatible with the specified collection
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
@@ -488,9 +491,9 @@
* encounter order that is ascending order. Overriding implementations
* should document the reporting of additional characteristic values.
*
- * <p>The {@linkplain Spliterator#getComparator() spliterator's comparator}
- * is {@code null} if the {@linkplain #comparator() set's comparator}
- * is {@code null}.
+ * <p>The spliterator's comparator (see
+ * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+ * the set's comparator (see {@link #comparator()}) is {@code null}.
* Otherwise, the spliterator's comparator is the same as or imposes the
* same total ordering as the set's comparator.
*
@@ -503,21 +506,18 @@
: ((ConcurrentSkipListMap.SubMap<E,?>)m).new SubMapKeyIterator();
}
- /** Initializes map field; for use in clone. */
+ // Support for resetting map in clone
private void setMap(ConcurrentNavigableMap<E,Object> map) {
- Field mapField = java.security.AccessController.doPrivileged(
- (java.security.PrivilegedAction<Field>) () -> {
- try {
- Field f = ConcurrentSkipListSet.class
- .getDeclaredField("m");
- f.setAccessible(true);
- return f;
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }});
+ U.putObjectVolatile(this, MAP, map);
+ }
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long MAP;
+ static {
try {
- mapField.set(this, map);
- } catch (IllegalAccessException e) {
+ MAP = U.objectFieldOffset
+ (ConcurrentSkipListSet.class.getDeclaredField("m"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
index 912204f..ebcbbef 100644
--- a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
+++ b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
@@ -34,8 +34,7 @@
package java.util.concurrent;
-import java.lang.invoke.VarHandle;
-import java.lang.reflect.Field;
+import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
@@ -51,7 +50,6 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
-import jdk.internal.misc.SharedSecrets;
// Android-changed: Removed javadoc link to collections framework docs
/**
@@ -83,10 +81,6 @@
* actions subsequent to the access or removal of that element from
* the {@code CopyOnWriteArrayList} in another thread.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this list
@@ -135,17 +129,17 @@
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
- Object[] es;
+ Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
- es = ((CopyOnWriteArrayList<?>)c).getArray();
+ elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
- es = c.toArray();
+ elements = c.toArray();
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
- if (es.getClass() != Object[].class)
- es = Arrays.copyOf(es, es.length, Object[].class);
+ if (elements.getClass() != Object[].class)
+ elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
- setArray(es);
+ setArray(elements);
}
/**
@@ -181,19 +175,20 @@
* static version of indexOf, to allow repeated calls without
* needing to re-acquire array each time.
* @param o element to search for
- * @param es the array
- * @param from first index to search
- * @param to one past last index to search
+ * @param elements the array
+ * @param index first index to search
+ * @param fence one past last index to search
* @return index of element, or -1 if absent
*/
- private static int indexOfRange(Object o, Object[] es, int from, int to) {
+ private static int indexOf(Object o, Object[] elements,
+ int index, int fence) {
if (o == null) {
- for (int i = from; i < to; i++)
- if (es[i] == null)
+ for (int i = index; i < fence; i++)
+ if (elements[i] == null)
return i;
} else {
- for (int i = from; i < to; i++)
- if (o.equals(es[i]))
+ for (int i = index; i < fence; i++)
+ if (o.equals(elements[i]))
return i;
}
return -1;
@@ -202,19 +197,18 @@
/**
* static version of lastIndexOf.
* @param o element to search for
- * @param es the array
- * @param from index of first element of range, last element to search
- * @param to one past last element of range, first element to search
+ * @param elements the array
+ * @param index first index to search
* @return index of element, or -1 if absent
*/
- private static int lastIndexOfRange(Object o, Object[] es, int from, int to) {
+ private static int lastIndexOf(Object o, Object[] elements, int index) {
if (o == null) {
- for (int i = to - 1; i >= from; i--)
- if (es[i] == null)
+ for (int i = index; i >= 0; i--)
+ if (elements[i] == null)
return i;
} else {
- for (int i = to - 1; i >= from; i--)
- if (o.equals(es[i]))
+ for (int i = index; i >= 0; i--)
+ if (o.equals(elements[i]))
return i;
}
return -1;
@@ -229,15 +223,16 @@
* @return {@code true} if this list contains the specified element
*/
public boolean contains(Object o) {
- return indexOf(o) >= 0;
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length) >= 0;
}
/**
* {@inheritDoc}
*/
public int indexOf(Object o) {
- Object[] es = getArray();
- return indexOfRange(o, es, 0, es.length);
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length);
}
/**
@@ -256,16 +251,16 @@
* @throws IndexOutOfBoundsException if the specified index is negative
*/
public int indexOf(E e, int index) {
- Object[] es = getArray();
- return indexOfRange(e, es, index, es.length);
+ Object[] elements = getArray();
+ return indexOf(e, elements, index, elements.length);
}
/**
* {@inheritDoc}
*/
public int lastIndexOf(Object o) {
- Object[] es = getArray();
- return lastIndexOfRange(o, es, 0, es.length);
+ Object[] elements = getArray();
+ return lastIndexOf(o, elements, elements.length - 1);
}
/**
@@ -285,8 +280,8 @@
* than or equal to the current size of this list
*/
public int lastIndexOf(E e, int index) {
- Object[] es = getArray();
- return lastIndexOfRange(e, es, 0, index + 1);
+ Object[] elements = getArray();
+ return lastIndexOf(e, elements, index);
}
/**
@@ -301,9 +296,6 @@
CopyOnWriteArrayList<E> clone =
(CopyOnWriteArrayList<E>) super.clone();
clone.resetLock();
- // Unlike in readObject, here we cannot visibility-piggyback on the
- // volatile write in setArray().
- VarHandle.releaseFence();
return clone;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
@@ -325,7 +317,8 @@
* @return an array containing all the elements in this list
*/
public Object[] toArray() {
- return getArray().clone();
+ Object[] elements = getArray();
+ return Arrays.copyOf(elements, elements.length);
}
/**
@@ -368,12 +361,12 @@
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
if (a.length < len)
- return (T[]) Arrays.copyOf(es, len, a.getClass());
+ return (T[]) Arrays.copyOf(elements, len, a.getClass());
else {
- System.arraycopy(es, 0, a, 0, len);
+ System.arraycopy(elements, 0, a, 0, len);
if (a.length > len)
a[len] = null;
return a;
@@ -383,7 +376,7 @@
// Positional Access Operations
@SuppressWarnings("unchecked")
- static <E> E elementAt(Object[] a, int index) {
+ private E get(Object[] a, int index) {
return (E) a[index];
}
@@ -397,7 +390,7 @@
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
- return elementAt(getArray(), index);
+ return get(getArray(), index);
}
/**
@@ -408,13 +401,17 @@
*/
public E set(int index, E element) {
synchronized (lock) {
- Object[] es = getArray();
- E oldValue = elementAt(es, index);
+ Object[] elements = getArray();
+ E oldValue = get(elements, index);
if (oldValue != element) {
- es = es.clone();
- es[index] = element;
- setArray(es);
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len);
+ newElements[index] = element;
+ setArray(newElements);
+ } else {
+ // Not quite a no-op; ensures volatile write semantics
+ setArray(elements);
}
return oldValue;
}
@@ -428,11 +425,11 @@
*/
public boolean add(E e) {
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
- es = Arrays.copyOf(es, len + 1);
- es[len] = e;
- setArray(es);
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len + 1);
+ newElements[len] = e;
+ setArray(newElements);
return true;
}
}
@@ -446,18 +443,18 @@
*/
public void add(int index, E element) {
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException(outOfBounds(index, len));
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
- newElements = Arrays.copyOf(es, len + 1);
+ newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
- System.arraycopy(es, 0, newElements, 0, index);
- System.arraycopy(es, index, newElements, index + 1,
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
@@ -474,20 +471,19 @@
*/
public E remove(int index) {
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
- E oldValue = elementAt(es, index);
+ Object[] elements = getArray();
+ int len = elements.length;
+ E oldValue = get(elements, index);
int numMoved = len - index - 1;
- Object[] newElements;
if (numMoved == 0)
- newElements = Arrays.copyOf(es, len - 1);
+ setArray(Arrays.copyOf(elements, len - 1));
else {
- newElements = new Object[len - 1];
- System.arraycopy(es, 0, newElements, 0, index);
- System.arraycopy(es, index + 1, newElements, index,
+ Object[] newElements = new Object[len - 1];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index + 1, newElements, index,
numMoved);
+ setArray(newElements);
}
- setArray(newElements);
return oldValue;
}
}
@@ -506,8 +502,8 @@
*/
public boolean remove(Object o) {
Object[] snapshot = getArray();
- int index = indexOfRange(o, snapshot, 0, snapshot.length);
- return index >= 0 && remove(o, snapshot, index);
+ int index = indexOf(o, snapshot, 0, snapshot.length);
+ return (index < 0) ? false : remove(o, snapshot, index);
}
/**
@@ -531,7 +527,7 @@
return false;
if (current[index] == o)
break findIndex;
- index = indexOfRange(o, current, index, len);
+ index = indexOf(o, current, index, len);
if (index < 0)
return false;
}
@@ -559,19 +555,19 @@
*/
void removeRange(int fromIndex, int toIndex) {
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int newlen = len - (toIndex - fromIndex);
int numMoved = len - toIndex;
if (numMoved == 0)
- setArray(Arrays.copyOf(es, newlen));
+ setArray(Arrays.copyOf(elements, newlen));
else {
Object[] newElements = new Object[newlen];
- System.arraycopy(es, 0, newElements, 0, fromIndex);
- System.arraycopy(es, toIndex, newElements,
+ System.arraycopy(elements, 0, newElements, 0, fromIndex);
+ System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
@@ -586,8 +582,8 @@
*/
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
- return indexOfRange(e, snapshot, 0, snapshot.length) < 0
- && addIfAbsent(e, snapshot);
+ return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
+ addIfAbsent(e, snapshot);
}
/**
@@ -605,7 +601,7 @@
if (current[i] != snapshot[i]
&& Objects.equals(e, current[i]))
return false;
- if (indexOfRange(e, current, common, len) >= 0)
+ if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
@@ -626,10 +622,10 @@
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
for (Object e : c) {
- if (indexOfRange(e, es, 0, len) < 0)
+ if (indexOf(e, elements, 0, len) < 0)
return false;
}
return true;
@@ -644,16 +640,34 @@
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>),
+ * (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
+ if (c == null) throw new NullPointerException();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Arrays.copyOf(temp, newlen));
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
@@ -665,16 +679,34 @@
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>),
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
+ if (c == null) throw new NullPointerException();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Arrays.copyOf(temp, newlen));
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
@@ -693,18 +725,18 @@
if (cs.length == 0)
return 0;
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
int added = 0;
// uniquify and compact elements in cs
for (int i = 0; i < cs.length; ++i) {
Object e = cs[i];
- if (indexOfRange(e, es, 0, len) < 0 &&
- indexOfRange(e, cs, 0, added) < 0)
+ if (indexOf(e, elements, 0, len) < 0 &&
+ indexOf(e, cs, 0, added) < 0)
cs[added++] = e;
}
if (added > 0) {
- Object[] newElements = Arrays.copyOf(es, len + added);
+ Object[] newElements = Arrays.copyOf(elements, len + added);
System.arraycopy(cs, 0, newElements, len, added);
setArray(newElements);
}
@@ -738,16 +770,15 @@
if (cs.length == 0)
return false;
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
- Object[] newElements;
+ Object[] elements = getArray();
+ int len = elements.length;
if (len == 0 && cs.getClass() == Object[].class)
- newElements = cs;
+ setArray(cs);
else {
- newElements = Arrays.copyOf(es, len + cs.length);
+ Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
+ setArray(newElements);
}
- setArray(newElements);
return true;
}
}
@@ -771,8 +802,8 @@
public boolean addAll(int index, Collection<? extends E> c) {
Object[] cs = c.toArray();
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException(outOfBounds(index, len));
if (cs.length == 0)
@@ -780,11 +811,11 @@
int numMoved = len - index;
Object[] newElements;
if (numMoved == 0)
- newElements = Arrays.copyOf(es, len + cs.length);
+ newElements = Arrays.copyOf(elements, len + cs.length);
else {
newElements = new Object[len + cs.length];
- System.arraycopy(es, 0, newElements, 0, index);
- System.arraycopy(es, index,
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index,
newElements, index + cs.length,
numMoved);
}
@@ -794,106 +825,65 @@
}
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
+ if (action == null) throw new NullPointerException();
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- // A tiny bit set implementation
-
- private static long[] nBits(int n) {
- return new long[((n - 1) >> 6) + 1];
- }
- private static void setBit(long[] bits, int i) {
- bits[i >> 6] |= 1L << i;
- }
- private static boolean isClear(long[] bits, int i) {
- return (bits[i >> 6] & (1L << i)) == 0;
- }
-
- private boolean bulkRemove(Predicate<? super E> filter) {
+ if (filter == null) throw new NullPointerException();
synchronized (lock) {
- return bulkRemove(filter, 0, getArray().length);
- }
- }
-
- boolean bulkRemove(Predicate<? super E> filter, int i, int end) {
- // assert Thread.holdsLock(lock);
- final Object[] es = getArray();
- // Optimize for initial run of survivors
- for (; i < end && !filter.test(elementAt(es, i)); i++)
- ;
- if (i < end) {
- final int beg = i;
- final long[] deathRow = nBits(end - beg);
- int deleted = 1;
- deathRow[0] = 1L; // set bit 0
- for (i = beg + 1; i < end; i++)
- if (filter.test(elementAt(es, i))) {
- setBit(deathRow, i - beg);
- deleted++;
+ final Object[] elements = getArray();
+ final int len = elements.length;
+ int i;
+ for (i = 0; i < len; i++) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ if (filter.test(e)) {
+ int newlen = i;
+ final Object[] newElements = new Object[len - 1];
+ System.arraycopy(elements, 0, newElements, 0, newlen);
+ for (i++; i < len; i++) {
+ @SuppressWarnings("unchecked") E x = (E) elements[i];
+ if (!filter.test(x))
+ newElements[newlen++] = x;
+ }
+ setArray((newlen == len - 1)
+ ? newElements // one match => one copy
+ : Arrays.copyOf(newElements, newlen));
+ return true;
}
- // Did filter reentrantly modify the list?
- if (es != getArray())
- throw new ConcurrentModificationException();
- final Object[] newElts = Arrays.copyOf(es, es.length - deleted);
- int w = beg;
- for (i = beg; i < end; i++)
- if (isClear(deathRow, i - beg))
- newElts[w++] = es[i];
- System.arraycopy(es, i, newElts, w, es.length - i);
- setArray(newElts);
- return true;
- } else {
- if (es != getArray())
- throw new ConcurrentModificationException();
- return false;
+ }
+ return false; // zero matches => zero copies
}
}
public void replaceAll(UnaryOperator<E> operator) {
+ if (operator == null) throw new NullPointerException();
synchronized (lock) {
- replaceAllRange(operator, 0, getArray().length);
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = 0; i < len; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ setArray(newElements);
}
}
- void replaceAllRange(UnaryOperator<E> operator, int i, int end) {
- // assert Thread.holdsLock(lock);
- Objects.requireNonNull(operator);
- final Object[] es = getArray().clone();
- for (; i < end; i++)
- es[i] = operator.apply(elementAt(es, i));
- setArray(es);
- }
-
public void sort(Comparator<? super E> c) {
synchronized (lock) {
- sortRange(c, 0, getArray().length);
+ Object[] elements = getArray();
+ Object[] newElements = Arrays.copyOf(elements, elements.length);
+ @SuppressWarnings("unchecked") E[] es = (E[])newElements;
+ Arrays.sort(es, c);
+ setArray(newElements);
}
}
- @SuppressWarnings("unchecked")
- void sortRange(Comparator<? super E> c, int i, int end) {
- // assert Thread.holdsLock(lock);
- final Object[] es = getArray().clone();
- Arrays.sort(es, i, end, (Comparator<Object>)c);
- setArray(es);
- }
-
/**
* Saves this list to a stream (that is, serializes it).
*
@@ -908,12 +898,12 @@
s.defaultWriteObject();
- Object[] es = getArray();
+ Object[] elements = getArray();
// Write out array length
- s.writeInt(es.length);
+ s.writeInt(elements.length);
// Write out all elements in the proper order.
- for (Object element : es)
+ for (Object element : elements)
s.writeObject(element);
}
@@ -934,13 +924,12 @@
// Read in array length and allocate array
int len = s.readInt();
- SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, len);
- Object[] es = new Object[len];
+ Object[] elements = new Object[len];
// Read in all elements in the proper order.
for (int i = 0; i < len; i++)
- es[i] = s.readObject();
- setArray(es);
+ elements[i] = s.readObject();
+ setArray(elements);
}
/**
@@ -980,19 +969,13 @@
List<?> list = (List<?>)o;
Iterator<?> it = list.iterator();
- for (Object element : getArray())
- if (!it.hasNext() || !Objects.equals(element, it.next()))
+ Object[] elements = getArray();
+ for (int i = 0, len = elements.length; i < len; i++)
+ if (!it.hasNext() || !Objects.equals(elements[i], it.next()))
return false;
- return !it.hasNext();
- }
-
- private static int hashCodeOfRange(Object[] es, int from, int to) {
- int hashCode = 1;
- for (int i = from; i < to; i++) {
- Object x = es[i];
- hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
- }
- return hashCode;
+ if (it.hasNext())
+ return false;
+ return true;
}
/**
@@ -1003,8 +986,10 @@
* @return the hash code value for this list
*/
public int hashCode() {
- Object[] es = getArray();
- return hashCodeOfRange(es, 0, es.length);
+ int hashCode = 1;
+ for (Object x : getArray())
+ hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
+ return hashCode;
}
/**
@@ -1044,12 +1029,12 @@
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public ListIterator<E> listIterator(int index) {
- Object[] es = getArray();
- int len = es.length;
+ Object[] elements = getArray();
+ int len = elements.length;
if (index < 0 || index > len)
throw new IndexOutOfBoundsException(outOfBounds(index, len));
- return new COWIterator<E>(es, index);
+ return new COWIterator<E>(elements, index);
}
/**
@@ -1077,9 +1062,9 @@
/** Index of element to be returned by subsequent call to next. */
private int cursor;
- COWIterator(Object[] es, int initialCursor) {
+ COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
- snapshot = es;
+ snapshot = elements;
}
public boolean hasNext() {
@@ -1109,7 +1094,7 @@
}
public int previousIndex() {
- return cursor - 1;
+ return cursor-1;
}
/**
@@ -1140,13 +1125,14 @@
}
@Override
+ @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = snapshot.length;
- int i = cursor;
+ for (int i = cursor; i < size; i++) {
+ action.accept((E) snapshot[i]);
+ }
cursor = size;
- for (; i < size; i++)
- action.accept(elementAt(snapshot, i));
}
}
@@ -1167,358 +1153,324 @@
*/
public List<E> subList(int fromIndex, int toIndex) {
synchronized (lock) {
- Object[] es = getArray();
- int len = es.length;
- int size = toIndex - fromIndex;
- if (fromIndex < 0 || toIndex > len || size < 0)
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
throw new IndexOutOfBoundsException();
- return new COWSubList(es, fromIndex, size);
+ return new COWSubList<E>(this, fromIndex, toIndex);
}
}
/**
* Sublist for CopyOnWriteArrayList.
+ * This class extends AbstractList merely for convenience, to
+ * avoid having to define addAll, etc. This doesn't hurt, but
+ * is wasteful. This class does not need or use modCount
+ * mechanics in AbstractList, but does need to check for
+ * concurrent modification using similar mechanics. On each
+ * operation, the array that we expect the backing list to use
+ * is checked and updated. Since we do this for all of the
+ * base operations invoked by those defined in AbstractList,
+ * all is well. While inefficient, this is not worth
+ * improving. The kinds of list operations inherited from
+ * AbstractList are already so slow on COW sublists that
+ * adding a bit more space/time doesn't seem even noticeable.
*/
- private class COWSubList implements List<E>, RandomAccess {
+ private static class COWSubList<E>
+ extends AbstractList<E>
+ implements RandomAccess
+ {
+ private final CopyOnWriteArrayList<E> l;
private final int offset;
private int size;
private Object[] expectedArray;
- COWSubList(Object[] es, int offset, int size) {
- // assert Thread.holdsLock(lock);
- expectedArray = es;
- this.offset = offset;
- this.size = size;
+ // only call this holding l's lock
+ COWSubList(CopyOnWriteArrayList<E> list,
+ int fromIndex, int toIndex) {
+ // assert Thread.holdsLock(list.lock);
+ l = list;
+ expectedArray = l.getArray();
+ offset = fromIndex;
+ size = toIndex - fromIndex;
}
+ // only call this holding l's lock
private void checkForComodification() {
- // assert Thread.holdsLock(lock);
- if (getArray() != expectedArray)
+ // assert Thread.holdsLock(l.lock);
+ if (l.getArray() != expectedArray)
throw new ConcurrentModificationException();
}
- private Object[] getArrayChecked() {
- // assert Thread.holdsLock(lock);
- Object[] a = getArray();
- if (a != expectedArray)
- throw new ConcurrentModificationException();
- return a;
- }
-
+ // only call this holding l's lock
private void rangeCheck(int index) {
- // assert Thread.holdsLock(lock);
+ // assert Thread.holdsLock(l.lock);
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException(outOfBounds(index, size));
}
- private void rangeCheckForAdd(int index) {
- // assert Thread.holdsLock(lock);
- if (index < 0 || index > size)
- throw new IndexOutOfBoundsException(outOfBounds(index, size));
- }
-
- public Object[] toArray() {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- return Arrays.copyOfRange(es, offset, offset + size);
- }
-
- @SuppressWarnings("unchecked")
- public <T> T[] toArray(T[] a) {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- if (a.length < size)
- return (T[]) Arrays.copyOfRange(
- es, offset, offset + size, a.getClass());
- else {
- System.arraycopy(es, offset, a, 0, size);
- if (a.length > size)
- a[size] = null;
- return a;
- }
- }
-
- public int indexOf(Object o) {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- int i = indexOfRange(o, es, offset, offset + size);
- return (i == -1) ? -1 : i - offset;
- }
-
- public int lastIndexOf(Object o) {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- int i = lastIndexOfRange(o, es, offset, offset + size);
- return (i == -1) ? -1 : i - offset;
- }
-
- public boolean contains(Object o) {
- return indexOf(o) >= 0;
- }
-
- public boolean containsAll(Collection<?> c) {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- for (Object o : c)
- if (indexOfRange(o, es, offset, offset + size) < 0)
- return false;
- return true;
- }
-
- public boolean isEmpty() {
- return size() == 0;
- }
-
- public String toString() {
- return Arrays.toString(toArray());
- }
-
- public int hashCode() {
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
- return hashCodeOfRange(es, offset, offset + size);
- }
-
- public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof List))
- return false;
- Iterator<?> it = ((List<?>)o).iterator();
-
- final Object[] es;
- final int offset;
- final int size;
- synchronized (lock) {
- es = getArrayChecked();
- offset = this.offset;
- size = this.size;
- }
-
- for (int i = offset, end = offset + size; i < end; i++)
- if (!it.hasNext() || !Objects.equals(es[i], it.next()))
- return false;
- return !it.hasNext();
- }
-
public E set(int index, E element) {
- synchronized (lock) {
+ synchronized (l.lock) {
rangeCheck(index);
checkForComodification();
- E x = CopyOnWriteArrayList.this.set(offset + index, element);
- expectedArray = getArray();
+ E x = l.set(index+offset, element);
+ expectedArray = l.getArray();
return x;
}
}
public E get(int index) {
- synchronized (lock) {
+ synchronized (l.lock) {
rangeCheck(index);
checkForComodification();
- return CopyOnWriteArrayList.this.get(offset + index);
+ return l.get(index+offset);
}
}
public int size() {
- synchronized (lock) {
+ synchronized (l.lock) {
checkForComodification();
return size;
}
}
- public boolean add(E element) {
- synchronized (lock) {
- checkForComodification();
- CopyOnWriteArrayList.this.add(offset + size, element);
- expectedArray = getArray();
- size++;
- }
- return true;
- }
-
public void add(int index, E element) {
- synchronized (lock) {
+ synchronized (l.lock) {
checkForComodification();
- rangeCheckForAdd(index);
- CopyOnWriteArrayList.this.add(offset + index, element);
- expectedArray = getArray();
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException
+ (outOfBounds(index, size));
+ l.add(index+offset, element);
+ expectedArray = l.getArray();
size++;
}
}
- public boolean addAll(Collection<? extends E> c) {
- synchronized (lock) {
- final Object[] oldArray = getArrayChecked();
- boolean modified =
- CopyOnWriteArrayList.this.addAll(offset + size, c);
- size += (expectedArray = getArray()).length - oldArray.length;
- return modified;
- }
- }
-
- public boolean addAll(int index, Collection<? extends E> c) {
- synchronized (lock) {
- rangeCheckForAdd(index);
- final Object[] oldArray = getArrayChecked();
- boolean modified =
- CopyOnWriteArrayList.this.addAll(offset + index, c);
- size += (expectedArray = getArray()).length - oldArray.length;
- return modified;
- }
- }
-
public void clear() {
- synchronized (lock) {
+ synchronized (l.lock) {
checkForComodification();
- removeRange(offset, offset + size);
- expectedArray = getArray();
+ l.removeRange(offset, offset+size);
+ expectedArray = l.getArray();
size = 0;
}
}
public E remove(int index) {
- synchronized (lock) {
+ synchronized (l.lock) {
rangeCheck(index);
checkForComodification();
- E result = CopyOnWriteArrayList.this.remove(offset + index);
- expectedArray = getArray();
+ E result = l.remove(index+offset);
+ expectedArray = l.getArray();
size--;
return result;
}
}
public boolean remove(Object o) {
- synchronized (lock) {
- checkForComodification();
- int index = indexOf(o);
- if (index == -1)
- return false;
- remove(index);
- return true;
- }
+ int index = indexOf(o);
+ if (index == -1)
+ return false;
+ remove(index);
+ return true;
}
public Iterator<E> iterator() {
- return listIterator(0);
- }
-
- public ListIterator<E> listIterator() {
- return listIterator(0);
+ synchronized (l.lock) {
+ checkForComodification();
+ return new COWSubListIterator<E>(l, 0, offset, size);
+ }
}
public ListIterator<E> listIterator(int index) {
- synchronized (lock) {
+ synchronized (l.lock) {
checkForComodification();
- rangeCheckForAdd(index);
- return new COWSubListIterator<E>(
- CopyOnWriteArrayList.this, index, offset, size);
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException
+ (outOfBounds(index, size));
+ return new COWSubListIterator<E>(l, index, offset, size);
}
}
public List<E> subList(int fromIndex, int toIndex) {
- synchronized (lock) {
+ synchronized (l.lock) {
checkForComodification();
if (fromIndex < 0 || toIndex > size || fromIndex > toIndex)
throw new IndexOutOfBoundsException();
- return new COWSubList(expectedArray, fromIndex + offset, toIndex - fromIndex);
+ return new COWSubList<E>(l, fromIndex + offset,
+ toIndex + offset);
}
}
public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- int i, end; final Object[] es;
- synchronized (lock) {
- es = getArrayChecked();
- i = offset;
- end = i + size;
+ if (action == null) throw new NullPointerException();
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) a[i];
+ action.accept(e);
}
- for (; i < end; i++)
- action.accept(elementAt(es, i));
}
public void replaceAll(UnaryOperator<E> operator) {
- synchronized (lock) {
- checkForComodification();
- replaceAllRange(operator, offset, offset + size);
- expectedArray = getArray();
+ if (operator == null) throw new NullPointerException();
+ synchronized (l.lock) {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ l.setArray(expectedArray = newElements);
}
}
public void sort(Comparator<? super E> c) {
- synchronized (lock) {
- checkForComodification();
- sortRange(c, offset, offset + size);
- expectedArray = getArray();
+ synchronized (l.lock) {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ @SuppressWarnings("unchecked") E[] es = (E[])newElements;
+ Arrays.sort(es, lo, hi, c);
+ l.setArray(expectedArray = newElements);
}
}
public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ }
+ return removed;
}
public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ }
+ return removed;
}
public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- private boolean bulkRemove(Predicate<? super E> filter) {
- synchronized (lock) {
- final Object[] oldArray = getArrayChecked();
- boolean modified = CopyOnWriteArrayList.this.bulkRemove(
- filter, offset, offset + size);
- size += (expectedArray = getArray()).length - oldArray.length;
- return modified;
+ if (filter == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ if (!filter.test(e))
+ temp[newSize++] = e;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
}
+ return removed;
}
public Spliterator<E> spliterator() {
- synchronized (lock) {
- return Spliterators.spliterator(
- getArrayChecked(), offset, offset + size,
- Spliterator.IMMUTABLE | Spliterator.ORDERED);
- }
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ return Spliterators.spliterator
+ (a, lo, hi, Spliterator.IMMUTABLE | Spliterator.ORDERED);
}
}
@@ -1531,7 +1483,7 @@
COWSubListIterator(List<E> l, int index, int offset, int size) {
this.offset = offset;
this.size = size;
- it = l.listIterator(index + offset);
+ it = l.listIterator(index+offset);
}
public boolean hasNext() {
@@ -1580,27 +1532,23 @@
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
- while (hasNext()) {
+ while (nextIndex() < size) {
action.accept(it.next());
}
}
}
- /** Initializes the lock; for use when deserializing or cloning. */
+ // Support for resetting lock while deserializing
private void resetLock() {
- Field lockField = java.security.AccessController.doPrivileged(
- (java.security.PrivilegedAction<Field>) () -> {
- try {
- Field f = CopyOnWriteArrayList.class
- .getDeclaredField("lock");
- f.setAccessible(true);
- return f;
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }});
+ U.putObjectVolatile(this, LOCK, new Object());
+ }
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long LOCK;
+ static {
try {
- lockField.set(this, new Object());
- } catch (IllegalAccessException e) {
+ LOCK = U.objectFieldOffset
+ (CopyOnWriteArrayList.class.getDeclaredField("lock"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
index b14a3e8..fb707dd 100644
--- a/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/ojluni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
@@ -45,8 +45,13 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
+// BEGIN android-note
+// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
+// END android-note
+
/**
- * A {@link Set} that uses an internal {@link CopyOnWriteArrayList}
+ * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList}
* for all of its operations. Thus, it shares the same basic properties:
* <ul>
* <li>It is best suited for applications in which set sizes generally
@@ -86,10 +91,6 @@
* }
* }}</pre>
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @see CopyOnWriteArrayList
* @since 1.5
* @author Doug Lea
@@ -340,10 +341,10 @@
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the class of an element of this set
* is incompatible with the specified collection
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
* specified collection does not permit null elements
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>),
+ * (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
@@ -363,10 +364,10 @@
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the class of an element of this set
* is incompatible with the specified collection
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
* specified collection does not permit null elements
- * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>),
+ * (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
@@ -411,16 +412,10 @@
&& compareSets(al.getArray(), (Set<?>) o) == 0);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
public boolean removeIf(Predicate<? super E> filter) {
return al.removeIf(filter);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
public void forEach(Consumer<? super E> action) {
al.forEach(action);
}
diff --git a/ojluni/src/main/java/java/util/concurrent/CountedCompleter.java b/ojluni/src/main/java/java/util/concurrent/CountedCompleter.java
index a91d12d..a29208e 100644
--- a/ojluni/src/main/java/java/util/concurrent/CountedCompleter.java
+++ b/ojluni/src/main/java/java/util/concurrent/CountedCompleter.java
@@ -35,9 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-
/**
* A {@link ForkJoinTask} with a completion action performed when
* triggered and there are no remaining pending actions.
@@ -57,7 +54,8 @@
* decremented; otherwise, the completion action is performed, and if
* this completer itself has a completer, the process is continued
* with its completer. As is the case with related synchronization
- * components such as {@link Phaser} and {@link Semaphore}, these methods
+ * components such as {@link java.util.concurrent.Phaser Phaser} and
+ * {@link java.util.concurrent.Semaphore Semaphore}, these methods
* affect only internal counts; they do not establish any further
* internal bookkeeping. In particular, the identities of pending
* tasks are not maintained. As illustrated below, you can create
@@ -119,114 +117,102 @@
* to complete for some elements than others, either because of
* intrinsic variation (for example I/O) or auxiliary effects such as
* garbage collection. Because CountedCompleters provide their own
- * continuations, other tasks need not block waiting to perform them.
+ * continuations, other threads need not block waiting to perform
+ * them.
*
- * <p>For example, here is an initial version of a utility method that
- * uses divide-by-two recursive decomposition to divide work into
- * single pieces (leaf tasks). Even when work is split into individual
- * calls, tree-based techniques are usually preferable to directly
- * forking leaf tasks, because they reduce inter-thread communication
- * and improve load balancing. In the recursive case, the second of
- * each pair of subtasks to finish triggers completion of their parent
+ * <p>For example, here is an initial version of a class that uses
+ * divide-by-two recursive decomposition to divide work into single
+ * pieces (leaf tasks). Even when work is split into individual calls,
+ * tree-based techniques are usually preferable to directly forking
+ * leaf tasks, because they reduce inter-thread communication and
+ * improve load balancing. In the recursive case, the second of each
+ * pair of subtasks to finish triggers completion of its parent
* (because no result combination is performed, the default no-op
* implementation of method {@code onCompletion} is not overridden).
- * The utility method sets up the root task and invokes it (here,
- * implicitly using the {@link ForkJoinPool#commonPool()}). It is
- * straightforward and reliable (but not optimal) to always set the
- * pending count to the number of child tasks and call {@code
- * tryComplete()} immediately before returning.
+ * A static utility method sets up the base task and invokes it
+ * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
*
* <pre> {@code
- * public static <E> void forEach(E[] array, Consumer<E> action) {
- * class Task extends CountedCompleter<Void> {
- * final int lo, hi;
- * Task(Task parent, int lo, int hi) {
- * super(parent); this.lo = lo; this.hi = hi;
- * }
+ * class MyOperation<E> { void apply(E e) { ... } }
*
- * public void compute() {
- * if (hi - lo >= 2) {
- * int mid = (lo + hi) >>> 1;
- * // must set pending count before fork
- * setPendingCount(2);
- * new Task(this, mid, hi).fork(); // right child
- * new Task(this, lo, mid).fork(); // left child
- * }
- * else if (hi > lo)
- * action.accept(array[lo]);
- * tryComplete();
- * }
+ * class ForEach<E> extends CountedCompleter<Void> {
+ *
+ * public static <E> void forEach(E[] array, MyOperation<E> op) {
+ * new ForEach<E>(null, array, op, 0, array.length).invoke();
* }
- * new Task(null, 0, array.length).invoke();
+ *
+ * final E[] array; final MyOperation<E> op; final int lo, hi;
+ * ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.op = op; this.lo = lo; this.hi = hi;
+ * }
+ *
+ * public void compute() { // version 1
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(2); // must set pending count before fork
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).fork(); // left child
+ * }
+ * else if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
+ * }
* }}</pre>
*
* This design can be improved by noticing that in the recursive case,
* the task has nothing to do after forking its right task, so can
* directly invoke its left task before returning. (This is an analog
- * of tail recursion removal.) Also, when the last action in a task
- * is to fork or invoke a subtask (a "tail call"), the call to {@code
- * tryComplete()} can be optimized away, at the cost of making the
- * pending count look "off by one".
+ * of tail recursion removal.) Also, because the task returns upon
+ * executing its left task (rather than falling through to invoke
+ * {@code tryComplete}) the pending count is set to one:
*
* <pre> {@code
- * public void compute() {
- * if (hi - lo >= 2) {
- * int mid = (lo + hi) >>> 1;
- * setPendingCount(1); // looks off by one, but correct!
- * new Task(this, mid, hi).fork(); // right child
- * new Task(this, lo, mid).compute(); // direct invoke
- * } else {
- * if (hi > lo)
- * action.accept(array[lo]);
- * tryComplete();
- * }
- * }}</pre>
- *
- * As a further optimization, notice that the left task need not even exist.
- * Instead of creating a new one, we can continue using the original task,
- * and add a pending count for each fork. Additionally, because no task
- * in this tree implements an {@link #onCompletion(CountedCompleter)} method,
- * {@code tryComplete} can be replaced with {@link #propagateCompletion}.
- *
- * <pre> {@code
- * public void compute() {
- * int n = hi - lo;
- * for (; n >= 2; n /= 2) {
- * addToPendingCount(1);
- * new Task(this, lo + n/2, lo + n).fork();
- * }
- * if (n > 0)
- * action.accept(array[lo]);
- * propagateCompletion();
- * }}</pre>
- *
- * When pending counts can be precomputed, they can be established in
- * the constructor:
- *
- * <pre> {@code
- * public static <E> void forEach(E[] array, Consumer<E> action) {
- * class Task extends CountedCompleter<Void> {
- * final int lo, hi;
- * Task(Task parent, int lo, int hi) {
- * super(parent, 31 - Integer.numberOfLeadingZeros(hi - lo));
- * this.lo = lo; this.hi = hi;
+ * class ForEach<E> ... {
+ * ...
+ * public void compute() { // version 2
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(1); // only one pending
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).compute(); // direct invoke
* }
- *
- * public void compute() {
- * for (int n = hi - lo; n >= 2; n /= 2)
- * new Task(this, lo + n/2, lo + n).fork();
- * action.accept(array[lo]);
- * propagateCompletion();
+ * else {
+ * if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
* }
* }
- * if (array.length > 0)
- * new Task(null, 0, array.length).invoke();
* }}</pre>
*
- * Additional optimizations of such classes might entail specializing
- * classes for leaf steps, subdividing by say, four, instead of two
- * per iteration, and using an adaptive threshold instead of always
- * subdividing down to single elements.
+ * As a further optimization, notice that the left task need not even exist.
+ * Instead of creating a new one, we can iterate using the original task,
+ * and add a pending count for each fork. Additionally, because no task
+ * in this tree implements an {@link #onCompletion(CountedCompleter)} method,
+ * {@code tryComplete()} can be replaced with {@link #propagateCompletion}.
+ *
+ * <pre> {@code
+ * class ForEach<E> ... {
+ * ...
+ * public void compute() { // version 3
+ * int l = lo, h = hi;
+ * while (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * new ForEach(this, array, op, mid, h).fork(); // right child
+ * h = mid;
+ * }
+ * if (h > l)
+ * op.apply(array[l]);
+ * propagateCompletion();
+ * }
+ * }}</pre>
+ *
+ * Additional optimizations of such classes might entail precomputing
+ * pending counts so that they can be established in constructors,
+ * specializing classes for leaf steps, subdividing by say, four,
+ * instead of two per iteration, and using an adaptive threshold
+ * instead of always subdividing down to single elements.
*
* <p><b>Searching.</b> A tree of CountedCompleters can search for a
* value or property in different parts of a data structure, and
@@ -538,7 +524,7 @@
* @param delta the value to add
*/
public final void addToPendingCount(int delta) {
- PENDING.getAndAdd(this, delta);
+ U.getAndAddInt(this, PENDING, delta);
}
/**
@@ -550,7 +536,7 @@
* @return {@code true} if successful
*/
public final boolean compareAndSetPendingCount(int expected, int count) {
- return PENDING.compareAndSet(this, expected, count);
+ return U.compareAndSwapInt(this, PENDING, expected, count);
}
/**
@@ -562,7 +548,7 @@
public final int decrementPendingCountUnlessZero() {
int c;
do {} while ((c = pending) != 0 &&
- !PENDING.weakCompareAndSet(this, c, c - 1));
+ !U.compareAndSwapInt(this, PENDING, c, c - 1));
return c;
}
@@ -595,7 +581,7 @@
return;
}
}
- else if (PENDING.weakCompareAndSet(a, c, c - 1))
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
return;
}
}
@@ -610,7 +596,7 @@
* not, be invoked for each completer in a computation.
*/
public final void propagateCompletion() {
- CountedCompleter<?> a = this, s;
+ CountedCompleter<?> a = this, s = a;
for (int c;;) {
if ((c = a.pending) == 0) {
if ((a = (s = a).completer) == null) {
@@ -618,7 +604,7 @@
return;
}
}
- else if (PENDING.weakCompareAndSet(a, c, c - 1))
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
return;
}
}
@@ -663,7 +649,7 @@
for (int c;;) {
if ((c = pending) == 0)
return this;
- else if (PENDING.weakCompareAndSet(this, c, c - 1))
+ else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
return null;
}
}
@@ -735,7 +721,7 @@
CountedCompleter<?> a = this, s = a;
while (a.onExceptionalCompletion(ex, s) &&
(a = (s = a).completer) != null && a.status >= 0 &&
- isExceptionalStatus(a.recordExceptionalCompletion(ex)))
+ a.recordExceptionalCompletion(ex) == EXCEPTIONAL)
;
}
@@ -767,15 +753,15 @@
*/
protected void setRawResult(T t) { }
- // VarHandle mechanics
- private static final VarHandle PENDING;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PENDING;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
-
+ PENDING = U.objectFieldOffset
+ (CountedCompleter.class.getDeclaredField("pending"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/CyclicBarrier.java b/ojluni/src/main/java/java/util/concurrent/CyclicBarrier.java
index 269bec4..5e018f1 100644
--- a/ojluni/src/main/java/java/util/concurrent/CyclicBarrier.java
+++ b/ojluni/src/main/java/java/util/concurrent/CyclicBarrier.java
@@ -82,7 +82,8 @@
* public Solver(float[][] matrix) {
* data = matrix;
* N = matrix.length;
- * Runnable barrierAction = () -> mergeRows(...);
+ * Runnable barrierAction =
+ * new Runnable() { public void run() { mergeRows(...); }};
* barrier = new CyclicBarrier(N, barrierAction);
*
* List<Thread> threads = new ArrayList<>(N);
@@ -131,10 +132,10 @@
* <i>happen-before</i> actions following a successful return from the
* corresponding {@code await()} in other threads.
*
+ * @since 1.5
* @see CountDownLatch
*
* @author Doug Lea
- * @since 1.5
*/
public class CyclicBarrier {
/**
@@ -149,8 +150,7 @@
* but no subsequent reset.
*/
private static class Generation {
- Generation() {} // prevent access constructor creation
- boolean broken; // initially false
+ boolean broken; // initially false
}
/** The lock for guarding barrier entry */
diff --git a/ojluni/src/main/java/java/util/concurrent/DelayQueue.java b/ojluni/src/main/java/java/util/concurrent/DelayQueue.java
index bf0858d..04a83d9 100644
--- a/ojluni/src/main/java/java/util/concurrent/DelayQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/DelayQueue.java
@@ -41,11 +41,14 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* An unbounded {@linkplain BlockingQueue blocking queue} of
* {@code Delayed} elements, in which an element can only be taken
@@ -60,15 +63,11 @@
* returns the count of both expired and unexpired elements.
* This queue does not permit null elements.
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
- * The Iterator provided in method {@link #iterator()} is <em>not</em>
- * guaranteed to traverse the elements of the DelayQueue in any
- * particular order.
- *
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the DelayQueue in any particular order.
*
* @since 1.5
* @author Doug Lea
@@ -323,13 +322,40 @@
}
/**
+ * Returns first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private E peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ E first = q.peek();
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
+ }
+
+ /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- return drainTo(c, Integer.MAX_VALUE);
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ int n = 0;
+ for (E e; (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
+ ++n;
+ }
+ return n;
+ } finally {
+ lock.unlock();
+ }
}
/**
@@ -339,7 +365,8 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -348,11 +375,8 @@
lock.lock();
try {
int n = 0;
- for (E first;
- n < maxElements
- && (first = q.peek()) != null
- && first.getDelay(NANOSECONDS) <= 0;) {
- c.add(first); // In this order, in case add() throws.
+ for (E e; n < maxElements && (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
q.poll();
++n;
}
@@ -523,7 +547,8 @@
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
- return (E)array[lastRet = cursor++];
+ lastRet = cursor;
+ return (E)array[cursor++];
}
public void remove() {
diff --git a/ojluni/src/main/java/java/util/concurrent/Exchanger.java b/ojluni/src/main/java/java/util/concurrent/Exchanger.java
index 38c43fe..f01a705 100644
--- a/ojluni/src/main/java/java/util/concurrent/Exchanger.java
+++ b/ojluni/src/main/java/java/util/concurrent/Exchanger.java
@@ -36,10 +36,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-import java.util.concurrent.locks.LockSupport;
-
/**
* A synchronization point at which threads can pair and swap elements
* within pairs. Each thread presents some object on entry to the
@@ -159,7 +155,9 @@
* a value that is enough for common platforms. Additionally,
* extra care elsewhere is taken to avoid other false/unintended
* sharing and to enhance locality, including adding padding (via
- * @Contended) to Nodes, embedding "bound" as an Exchanger field.
+ * @Contended) to Nodes, embedding "bound" as an Exchanger field,
+ * and reworking some park/unpark mechanics compared to
+ * LockSupport versions.
*
* The arena starts out with only one used slot. We expand the
* effective arena size by tracking collisions; i.e., failed CASes
@@ -235,23 +233,29 @@
* As is too common in this sort of code, methods are monolithic
* because most of the logic relies on reads of fields that are
* maintained as local variables so can't be nicely factored --
- * mainly, here, bulky spin->yield->block/cancel code. Note that
- * field Node.item is not declared as volatile even though it is
- * read by releasing threads, because they only do so after CAS
- * operations that must precede access, and all uses by the owning
- * thread are otherwise acceptably ordered by other operations.
- * (Because the actual points of atomicity are slot CASes, it
- * would also be legal for the write to Node.match in a release to
- * be weaker than a full volatile write. However, this is not done
- * because it could allow further postponement of the write,
- * delaying progress.)
+ * mainly, here, bulky spin->yield->block/cancel code), and
+ * heavily dependent on intrinsics (Unsafe) to use inlined
+ * embedded CAS and related memory access operations (that tend
+ * not to be as readily inlined by dynamic compilers when they are
+ * hidden behind other methods that would more nicely name and
+ * encapsulate the intended effects). This includes the use of
+ * putOrderedX to clear fields of the per-thread Nodes between
+ * uses. Note that field Node.item is not declared as volatile
+ * even though it is read by releasing threads, because they only
+ * do so after CAS operations that must precede access, and all
+ * uses by the owning thread are otherwise acceptably ordered by
+ * other operations. (Because the actual points of atomicity are
+ * slot CASes, it would also be legal for the write to Node.match
+ * in a release to be weaker than a full volatile write. However,
+ * this is not done because it could allow further postponement of
+ * the write, delaying progress.)
*/
/**
- * The index distance (as a shift value) between any two used slots
- * in the arena, spacing them out to avoid false sharing.
+ * The byte distance (as a shift value) between any two used slots
+ * in the arena. 1 << ASHIFT should be at least cacheline size.
*/
- private static final int ASHIFT = 5;
+ private static final int ASHIFT = 7;
/**
* The maximum supported arena index. The maximum allocatable
@@ -354,31 +358,27 @@
*/
private final Object arenaExchange(Object item, boolean timed, long ns) {
Node[] a = arena;
- int alen = a.length;
Node p = participant.get();
for (int i = p.index;;) { // access slot at i
- int b, m, c;
- int j = (i << ASHIFT) + ((1 << ASHIFT) - 1);
- if (j < 0 || j >= alen)
- j = alen - 1;
- Node q = (Node)AA.getAcquire(a, j);
- if (q != null && AA.compareAndSet(a, j, q, null)) {
+ int b, m, c; long j; // j is raw array offset
+ Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
+ if (q != null && U.compareAndSwapObject(a, j, q, null)) {
Object v = q.item; // release
q.match = item;
Thread w = q.parked;
if (w != null)
- LockSupport.unpark(w);
+ U.unpark(w);
return v;
}
else if (i <= (m = (b = bound) & MMASK) && q == null) {
p.item = item; // offer
- if (AA.compareAndSet(a, j, null, p)) {
+ if (U.compareAndSwapObject(a, j, null, p)) {
long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
Thread t = Thread.currentThread(); // wait
for (int h = p.hash, spins = SPINS;;) {
Object v = p.match;
if (v != null) {
- MATCH.setRelease(p, null);
+ U.putOrderedObject(p, MATCH, null);
p.item = null; // clear for next use
p.hash = h;
return v;
@@ -391,24 +391,22 @@
(--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield(); // two yields per wait
}
- else if (AA.getAcquire(a, j) != p)
+ else if (U.getObjectVolatile(a, j) != p)
spins = SPINS; // releaser hasn't set match yet
else if (!t.isInterrupted() && m == 0 &&
(!timed ||
(ns = end - System.nanoTime()) > 0L)) {
+ U.putObject(t, BLOCKER, this); // emulate LockSupport
p.parked = t; // minimize window
- if (AA.getAcquire(a, j) == p) {
- if (ns == 0L)
- LockSupport.park(this);
- else
- LockSupport.parkNanos(this, ns);
- }
+ if (U.getObjectVolatile(a, j) == p)
+ U.park(false, ns);
p.parked = null;
+ U.putObject(t, BLOCKER, null);
}
- else if (AA.getAcquire(a, j) == p &&
- AA.compareAndSet(a, j, p, null)) {
+ else if (U.getObjectVolatile(a, j) == p &&
+ U.compareAndSwapObject(a, j, p, null)) {
if (m != 0) // try to shrink
- BOUND.compareAndSet(this, b, b + SEQ - 1);
+ U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
p.item = null;
p.hash = h;
i = p.index >>>= 1; // descend
@@ -430,7 +428,7 @@
i = (i != m || m == 0) ? m : m - 1;
}
else if ((c = p.collides) < m || m == FULL ||
- !BOUND.compareAndSet(this, b, b + SEQ + 1)) {
+ !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
p.collides = c + 1;
i = (i == 0) ? m : i - 1; // cyclically traverse
}
@@ -459,24 +457,24 @@
for (Node q;;) {
if ((q = slot) != null) {
- if (SLOT.compareAndSet(this, q, null)) {
+ if (U.compareAndSwapObject(this, SLOT, q, null)) {
Object v = q.item;
q.match = item;
Thread w = q.parked;
if (w != null)
- LockSupport.unpark(w);
+ U.unpark(w);
return v;
}
// create arena on contention, but continue until slot null
if (NCPU > 1 && bound == 0 &&
- BOUND.compareAndSet(this, 0, SEQ))
+ U.compareAndSwapInt(this, BOUND, 0, SEQ))
arena = new Node[(FULL + 2) << ASHIFT];
}
else if (arena != null)
return null; // caller must reroute to arenaExchange
else {
p.item = item;
- if (SLOT.compareAndSet(this, null, p))
+ if (U.compareAndSwapObject(this, SLOT, null, p))
break;
p.item = null;
}
@@ -499,21 +497,19 @@
spins = SPINS;
else if (!t.isInterrupted() && arena == null &&
(!timed || (ns = end - System.nanoTime()) > 0L)) {
+ U.putObject(t, BLOCKER, this);
p.parked = t;
- if (slot == p) {
- if (ns == 0L)
- LockSupport.park(this);
- else
- LockSupport.parkNanos(this, ns);
- }
+ if (slot == p)
+ U.park(false, ns);
p.parked = null;
+ U.putObject(t, BLOCKER, null);
}
- else if (SLOT.compareAndSet(this, p, null)) {
+ else if (U.compareAndSwapObject(this, SLOT, p, null)) {
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
break;
}
}
- MATCH.setRelease(p, null);
+ U.putOrderedObject(p, MATCH, null);
p.item = null;
p.hash = h;
return v;
@@ -562,9 +558,8 @@
@SuppressWarnings("unchecked")
public V exchange(V x) throws InterruptedException {
Object v;
- Node[] a;
Object item = (x == null) ? NULL_ITEM : x; // translate null args
- if (((a = arena) != null ||
+ if ((arena != null ||
(v = slotExchange(item, false, 0L)) == null) &&
((Thread.interrupted() || // disambiguates null return
(v = arenaExchange(item, false, 0L)) == null)))
@@ -630,20 +625,33 @@
return (v == NULL_ITEM) ? null : (V)v;
}
- // VarHandle mechanics
- private static final VarHandle BOUND;
- private static final VarHandle SLOT;
- private static final VarHandle MATCH;
- private static final VarHandle AA;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long BOUND;
+ private static final long SLOT;
+ private static final long MATCH;
+ private static final long BLOCKER;
+ private static final int ABASE;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- BOUND = l.findVarHandle(Exchanger.class, "bound", int.class);
- SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class);
- MATCH = l.findVarHandle(Node.class, "match", Object.class);
- AA = MethodHandles.arrayElementVarHandle(Node[].class);
+ BOUND = U.objectFieldOffset
+ (Exchanger.class.getDeclaredField("bound"));
+ SLOT = U.objectFieldOffset
+ (Exchanger.class.getDeclaredField("slot"));
+
+ MATCH = U.objectFieldOffset
+ (Node.class.getDeclaredField("match"));
+
+ BLOCKER = U.objectFieldOffset
+ (Thread.class.getDeclaredField("parkBlocker"));
+
+ int scale = U.arrayIndexScale(Node[].class);
+ if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
+ throw new Error("Unsupported array scale");
+ // ABASE absorbs padding in front of element 0
+ ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/Executor.java b/ojluni/src/main/java/java/util/concurrent/Executor.java
index 378cacd..a615705 100644
--- a/ojluni/src/main/java/java/util/concurrent/Executor.java
+++ b/ojluni/src/main/java/java/util/concurrent/Executor.java
@@ -87,12 +87,14 @@
* this.executor = executor;
* }
*
- * public synchronized void execute(Runnable r) {
- * tasks.add(() -> {
- * try {
- * r.run();
- * } finally {
- * scheduleNext();
+ * public synchronized void execute(final Runnable r) {
+ * tasks.add(new Runnable() {
+ * public void run() {
+ * try {
+ * r.run();
+ * } finally {
+ * scheduleNext();
+ * }
* }
* });
* if (active == null) {
diff --git a/ojluni/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/ojluni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
index d60d3dd..a093844 100644
--- a/ojluni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
+++ b/ojluni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
@@ -56,11 +56,13 @@
* void solve(Executor e,
* Collection<Callable<Result>> solvers)
* throws InterruptedException, ExecutionException {
- * CompletionService<Result> cs
- * = new ExecutorCompletionService<>(e);
- * solvers.forEach(cs::submit);
- * for (int i = solvers.size(); i > 0; i--) {
- * Result r = cs.take().get();
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
+ * for (Callable<Result> s : solvers)
+ * ecs.submit(s);
+ * int n = solvers.size();
+ * for (int i = 0; i < n; ++i) {
+ * Result r = ecs.take().get();
* if (r != null)
* use(r);
* }
@@ -74,31 +76,32 @@
* void solve(Executor e,
* Collection<Callable<Result>> solvers)
* throws InterruptedException {
- * CompletionService<Result> cs
- * = new ExecutorCompletionService<>(e);
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
* int n = solvers.size();
* List<Future<Result>> futures = new ArrayList<>(n);
* Result result = null;
* try {
- * solvers.forEach(solver -> futures.add(cs.submit(solver)));
- * for (int i = n; i > 0; i--) {
+ * for (Callable<Result> s : solvers)
+ * futures.add(ecs.submit(s));
+ * for (int i = 0; i < n; ++i) {
* try {
- * Result r = cs.take().get();
+ * Result r = ecs.take().get();
* if (r != null) {
* result = r;
* break;
* }
* } catch (ExecutionException ignore) {}
* }
- * } finally {
- * futures.forEach(future -> future.cancel(true));
+ * }
+ * finally {
+ * for (Future<Result> f : futures)
+ * f.cancel(true);
* }
*
* if (result != null)
* use(result);
* }}</pre>
- *
- * @since 1.5
*/
public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
@@ -174,10 +177,6 @@
this.completionQueue = completionQueue;
}
- /**
- * @throws RejectedExecutionException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- */
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
@@ -185,10 +184,6 @@
return f;
}
- /**
- * @throws RejectedExecutionException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
- */
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
diff --git a/ojluni/src/main/java/java/util/concurrent/Executors.java b/ojluni/src/main/java/java/util/concurrent/Executors.java
index e8cc0c1..565fdeb 100644
--- a/ojluni/src/main/java/java/util/concurrent/Executors.java
+++ b/ojluni/src/main/java/java/util/concurrent/Executors.java
@@ -35,7 +35,6 @@
package java.util.concurrent;
-import static java.lang.ref.Reference.reachabilityFence;
import dalvik.annotation.optimization.ReachabilitySensitive;
import java.security.AccessControlContext;
import java.security.AccessControlException;
@@ -191,7 +190,9 @@
* returned executor is guaranteed not to be reconfigurable to use
* additional threads.
*
- * @param threadFactory the factory to use when creating new threads
+ * @param threadFactory the factory to use when creating new
+ * threads
+ *
* @return the newly created single-threaded Executor
* @throws NullPointerException if threadFactory is null
*/
@@ -230,7 +231,6 @@
* will reuse previously constructed threads when they are
* available, and uses the provided
* ThreadFactory to create new threads when needed.
- *
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
* @throws NullPointerException if threadFactory is null
@@ -253,7 +253,6 @@
* given time. Unlike the otherwise equivalent
* {@code newScheduledThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
- *
* @return the newly created scheduled executor
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
@@ -272,9 +271,9 @@
* equivalent {@code newScheduledThreadPool(1, threadFactory)}
* the returned executor is guaranteed not to be reconfigurable to
* use additional threads.
- *
- * @param threadFactory the factory to use when creating new threads
- * @return the newly created scheduled executor
+ * @param threadFactory the factory to use when creating new
+ * threads
+ * @return a newly created scheduled executor
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
@@ -287,7 +286,7 @@
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
- * @return the newly created scheduled thread pool
+ * @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
@@ -301,7 +300,7 @@
* even if they are idle
* @param threadFactory the factory to use when the executor
* creates a new thread
- * @return the newly created scheduled thread pool
+ * @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if threadFactory is null
*/
@@ -463,9 +462,6 @@
task.run();
return result;
}
- public String toString() {
- return super.toString() + "[Wrapped task = " + task + "]";
- }
}
/**
@@ -492,10 +488,6 @@
throw e.getException();
}
}
-
- public String toString() {
- return super.toString() + "[Wrapped task = " + task + "]";
- }
}
/**
@@ -551,10 +543,6 @@
throw e.getException();
}
}
-
- public String toString() {
- return super.toString() + "[Wrapped task = " + task + "]";
- }
}
/**
@@ -616,7 +604,7 @@
public Thread newThread(final Runnable r) {
return super.newThread(new Runnable() {
public void run() {
- AccessController.doPrivileged(new PrivilegedAction<>() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
Thread.currentThread().setContextClassLoader(ccl);
r.run();
@@ -633,79 +621,47 @@
* of an ExecutorService implementation.
*/
private static class DelegatedExecutorService
- implements ExecutorService {
+ extends AbstractExecutorService {
// Android-added: @ReachabilitySensitive
// Needed for FinalizableDelegatedExecutorService below.
@ReachabilitySensitive
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
- public void execute(Runnable command) {
- try {
- e.execute(command);
- } finally { reachabilityFence(this); }
- }
+ public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
- public List<Runnable> shutdownNow() {
- try {
- return e.shutdownNow();
- } finally { reachabilityFence(this); }
- }
- public boolean isShutdown() {
- try {
- return e.isShutdown();
- } finally { reachabilityFence(this); }
- }
- public boolean isTerminated() {
- try {
- return e.isTerminated();
- } finally { reachabilityFence(this); }
- }
+ public List<Runnable> shutdownNow() { return e.shutdownNow(); }
+ public boolean isShutdown() { return e.isShutdown(); }
+ public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
- try {
- return e.awaitTermination(timeout, unit);
- } finally { reachabilityFence(this); }
+ return e.awaitTermination(timeout, unit);
}
public Future<?> submit(Runnable task) {
- try {
- return e.submit(task);
- } finally { reachabilityFence(this); }
+ return e.submit(task);
}
public <T> Future<T> submit(Callable<T> task) {
- try {
- return e.submit(task);
- } finally { reachabilityFence(this); }
+ return e.submit(task);
}
public <T> Future<T> submit(Runnable task, T result) {
- try {
- return e.submit(task, result);
- } finally { reachabilityFence(this); }
+ return e.submit(task, result);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
- try {
- return e.invokeAll(tasks);
- } finally { reachabilityFence(this); }
+ return e.invokeAll(tasks);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
- try {
- return e.invokeAll(tasks, timeout, unit);
- } finally { reachabilityFence(this); }
+ return e.invokeAll(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
- try {
- return e.invokeAny(tasks);
- } finally { reachabilityFence(this); }
+ return e.invokeAny(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- try {
- return e.invokeAny(tasks, timeout, unit);
- } finally { reachabilityFence(this); }
+ return e.invokeAny(tasks, timeout, unit);
}
}
@@ -714,7 +670,6 @@
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
- @SuppressWarnings("deprecation")
protected void finalize() {
super.shutdown();
}
diff --git a/ojluni/src/main/java/java/util/concurrent/Flow.java b/ojluni/src/main/java/java/util/concurrent/Flow.java
index 727a507..0231790 100644
--- a/ojluni/src/main/java/java/util/concurrent/Flow.java
+++ b/ojluni/src/main/java/java/util/concurrent/Flow.java
@@ -85,9 +85,9 @@
* this.executor = executor;
* }
* public synchronized void request(long n) {
- * if (!completed) {
+ * if (n != 0 && !completed) {
* completed = true;
- * if (n <= 0) {
+ * if (n < 0) {
* IllegalArgumentException ex = new IllegalArgumentException();
* executor.execute(() -> subscriber.onError(ex));
* } else {
diff --git a/ojluni/src/main/java/java/util/concurrent/ForkJoinPool.java b/ojluni/src/main/java/java/util/concurrent/ForkJoinPool.java
index 98b9c76..04ad7d7 100644
--- a/ojluni/src/main/java/java/util/concurrent/ForkJoinPool.java
+++ b/ojluni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -36,19 +36,15 @@
package java.util.concurrent;
import java.lang.Thread.UncaughtExceptionHandler;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-import java.security.AccessController;
import java.security.AccessControlContext;
-import java.security.Permission;
import java.security.Permissions;
-import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.function.Predicate;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.LockSupport;
/**
@@ -67,8 +63,7 @@
* tasks are submitted to the pool from external clients. Especially
* when setting <em>asyncMode</em> to true in constructors, {@code
* ForkJoinPool}s may also be appropriate for use with event-style
- * tasks that are never joined. All worker threads are initialized
- * with {@link Thread#isDaemon} set {@code true}.
+ * tasks that are never joined.
*
* <p>A static {@link #commonPool()} is available and appropriate for
* most applications. The common pool is used by any ForkJoinTask that
@@ -86,9 +81,7 @@
* However, no such adjustments are guaranteed in the face of blocked
* I/O or other unmanaged synchronization. The nested {@link
* ManagedBlocker} interface enables extension of the kinds of
- * synchronization accommodated. The default policies may be
- * overridden using a constructor with parameters corresponding to
- * those documented in class {@link ThreadPoolExecutor}.
+ * synchronization accommodated.
*
* <p>In addition to execution and lifecycle control methods, this
* class provides status check methods (for example
@@ -109,54 +102,48 @@
* async event-style tasks that are not usually joined, in which case
* there is little difference among choice of methods.
*
- * <table class="plain">
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of task execution methods</caption>
* <tr>
* <td></td>
- * <th scope="col"> Call from non-fork/join clients</th>
- * <th scope="col"> Call from within fork/join computations</th>
+ * <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td>
+ * <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left"> Arrange async execution</th>
+ * <td> <b>Arrange async execution</b></td>
* <td> {@link #execute(ForkJoinTask)}</td>
* <td> {@link ForkJoinTask#fork}</td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left"> Await and obtain result</th>
+ * <td> <b>Await and obtain result</b></td>
* <td> {@link #invoke(ForkJoinTask)}</td>
* <td> {@link ForkJoinTask#invoke}</td>
* </tr>
* <tr>
- * <th scope="row" style="text-align:left"> Arrange exec and obtain Future</th>
+ * <td> <b>Arrange exec and obtain Future</b></td>
* <td> {@link #submit(ForkJoinTask)}</td>
* <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td>
* </tr>
* </table>
*
- * <p>The parameters used to construct the common pool may be controlled by
- * setting the following {@linkplain System#getProperty system properties}:
+ * <p>The common pool is by default constructed with default
+ * parameters, but these may be controlled by setting three
+ * {@linkplain System#getProperty system properties}:
* <ul>
* <li>{@code java.util.concurrent.ForkJoinPool.common.parallelism}
* - the parallelism level, a non-negative integer
* <li>{@code java.util.concurrent.ForkJoinPool.common.threadFactory}
- * - the class name of a {@link ForkJoinWorkerThreadFactory}.
- * The {@linkplain ClassLoader#getSystemClassLoader() system class loader}
- * is used to load this class.
+ * - the class name of a {@link ForkJoinWorkerThreadFactory}
* <li>{@code java.util.concurrent.ForkJoinPool.common.exceptionHandler}
- * - the class name of a {@link UncaughtExceptionHandler}.
- * The {@linkplain ClassLoader#getSystemClassLoader() system class loader}
- * is used to load this class.
+ * - the class name of a {@link UncaughtExceptionHandler}
* <li>{@code java.util.concurrent.ForkJoinPool.common.maximumSpares}
* - the maximum number of allowed extra threads to maintain target
* parallelism (default 256).
* </ul>
- * If no thread factory is supplied via a system property, then the
- * common pool uses a factory that uses the system class loader as the
- * {@linkplain Thread#getContextClassLoader() thread context class loader}.
- * In addition, if a {@link SecurityManager} is present, then
- * the common pool uses a factory supplying threads that have no
- * {@link Permissions} enabled.
- *
+ * If a {@link SecurityManager} is present and no factory is
+ * specified, then the default pool uses a factory supplying
+ * threads that have no {@link Permissions} enabled.
+ * The system class loader is used to load these classes.
* Upon any error in establishing these settings, default parameters
* are used. It is possible to disable or limit the use of threads in
* the common pool by setting the parallelism property to zero, and/or
@@ -186,22 +173,17 @@
* functionality and control for a set of worker threads:
* Submissions from non-FJ threads enter into submission queues.
* Workers take these tasks and typically split them into subtasks
- * that may be stolen by other workers. Work-stealing based on
- * randomized scans generally leads to better throughput than
- * "work dealing" in which producers assign tasks to idle threads,
- * in part because threads that have finished other tasks before
- * the signalled thread wakes up (which can be a long time) can
- * take the task instead. Preference rules give first priority to
- * processing tasks from their own queues (LIFO or FIFO, depending
- * on mode), then to randomized FIFO steals of tasks in other
- * queues. This framework began as vehicle for supporting
- * tree-structured parallelism using work-stealing. Over time,
- * its scalability advantages led to extensions and changes to
- * better support more diverse usage contexts. Because most
- * internal methods and nested classes are interrelated, their
- * main rationale and descriptions are presented here; individual
- * methods and nested classes contain only brief comments about
- * details.
+ * that may be stolen by other workers. Preference rules give
+ * first priority to processing tasks from their own queues (LIFO
+ * or FIFO, depending on mode), then to randomized FIFO steals of
+ * tasks in other queues. This framework began as vehicle for
+ * supporting tree-structured parallelism using work-stealing.
+ * Over time, its scalability advantages led to extensions and
+ * changes to better support more diverse usage contexts. Because
+ * most internal methods and nested classes are interrelated,
+ * their main rationale and descriptions are presented here;
+ * individual methods and nested classes contain only brief
+ * comments about details.
*
* WorkQueues
* ==========
@@ -234,10 +216,9 @@
*
* (The actual code needs to null-check and size-check the array,
* uses masking, not mod, for indexing a power-of-two-sized array,
- * adds a release fence for publication, and possibly signals
- * waiting workers to start scanning -- see below.) Both a
- * successful pop and poll mainly entail a CAS of a slot from
- * non-null to null.
+ * properly fences accesses, and possibly signals waiting workers
+ * to start scanning -- see below.) Both a successful pop and
+ * poll mainly entail a CAS of a slot from non-null to null.
*
* The pop operation (always performed by owner) is:
* if ((the task at top slot is not null) and
@@ -249,14 +230,10 @@
* (CAS slot to null))
* increment base and return task;
*
- * There are several variants of each of these. Most uses occur
- * within operations that also interleave contention or emptiness
- * tracking or inspection of elements before extracting them, so
- * must interleave these with the above code. When performed by
- * owner, getAndSet is used instead of CAS (see for example method
- * nextLocalTask) which is usually more efficient, and possible
- * because the top index cannot independently change during the
- * operation.
+ * There are several variants of each of these; for example most
+ * versions of poll pre-screen the CAS by rechecking that the base
+ * has not changed since reading the slot, and most methods only
+ * attempt the CAS if base appears not to be equal to top.
*
* Memory ordering. See "Correct and Efficient Work-Stealing for
* Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
@@ -265,37 +242,33 @@
* algorithms similar to (but different than) the one used here.
* Extracting tasks in array slots via (fully fenced) CAS provides
* primary synchronization. The base and top indices imprecisely
- * guide where to extract from. We do not usually require strict
- * orderings of array and index updates. Many index accesses use
- * plain mode, with ordering constrained by surrounding context
- * (usually with respect to element CASes or the two WorkQueue
- * volatile fields source and phase). When not otherwise already
- * constrained, reads of "base" by queue owners use acquire-mode,
- * and some externally callable methods preface accesses with
- * acquire fences. Additionally, to ensure that index update
- * writes are not coalesced or postponed in loops etc, "opaque"
- * mode is used in a few cases where timely writes are not
- * otherwise ensured. The "locked" versions of push- and pop-
- * based methods for shared queues differ from owned versions
- * because locking already forces some of the ordering.
- *
- * Because indices and slot contents cannot always be consistent,
- * a check that base == top indicates (momentary) emptiness, but
- * otherwise may err on the side of possibly making the queue
- * appear nonempty when a push, pop, or poll have not fully
- * committed, or making it appear empty when an update of top has
- * not yet been visibly written. (Method isEmpty() checks the
- * case of a partially completed removal of the last element.)
- * Because of this, the poll operation, considered individually,
- * is not wait-free. One thief cannot successfully continue until
- * another in-progress one (or, if previously empty, a push)
- * visibly completes. This can stall threads when required to
- * consume from a given queue (see method poll()). However, in
- * the aggregate, we ensure at least probabilistic
+ * guide where to extract from. We do not always require strict
+ * orderings of array and index updates, so sometimes let them be
+ * subject to compiler and processor reorderings. However, the
+ * volatile "base" index also serves as a basis for memory
+ * ordering: Slot accesses are preceded by a read of base,
+ * ensuring happens-before ordering with respect to stealers (so
+ * the slots themselves can be read via plain array reads.) The
+ * only other memory orderings relied on are maintained in the
+ * course of signalling and activation (see below). A check that
+ * base == top indicates (momentary) emptiness, but otherwise may
+ * err on the side of possibly making the queue appear nonempty
+ * when a push, pop, or poll have not fully committed, or making
+ * it appear empty when an update of top has not yet been visibly
+ * written. (Method isEmpty() checks the case of a partially
+ * completed removal of the last element.) Because of this, the
+ * poll operation, considered individually, is not wait-free. One
+ * thief cannot successfully continue until another in-progress
+ * one (or, if previously empty, a push) visibly completes.
+ * However, in the aggregate, we ensure at least probabilistic
* non-blockingness. If an attempted steal fails, a scanning
* thief chooses a different random victim target to try next. So,
* in order for one thief to progress, it suffices for any
- * in-progress poll or new push on any empty queue to complete.
+ * in-progress poll or new push on any empty queue to
+ * complete. (This is why we normally use method pollAt and its
+ * variants that try once at the apparent base index, else
+ * consider alternative actions, rather than method poll, which
+ * retries.)
*
* This approach also enables support of a user mode in which
* local task processing is in FIFO, not LIFO order, simply by
@@ -310,13 +283,16 @@
* choosing existing queues, and may be randomly repositioned upon
* contention with other submitters. In essence, submitters act
* like workers except that they are restricted to executing local
- * tasks that they submitted. Insertion of tasks in shared mode
- * requires a lock but we use only a simple spinlock (using field
- * phase), because submitters encountering a busy queue move to a
- * different position to use or create other queues -- they block
- * only when creating and registering new queues. Because it is
- * used only as a spinlock, unlocking requires only a "releasing"
- * store (using setRelease) unless otherwise signalling.
+ * tasks that they submitted (or in the case of CountedCompleters,
+ * others with the same root task). Insertion of tasks in shared
+ * mode requires a lock but we use only a simple spinlock (using
+ * field qlock), because submitters encountering a busy queue move
+ * on to try or create other queues -- they block only when
+ * creating and registering new queues. Because it is used only as
+ * a spinlock, unlocking requires only a "releasing" store (using
+ * putOrderedInt). The qlock is also used during termination
+ * detection, in which case it is forced to a negative
+ * non-lockable value.
*
* Management
* ==========
@@ -330,36 +306,43 @@
* There are only a few properties that we can globally track or
* maintain, so we pack them into a small number of variables,
* often maintaining atomicity without blocking or locking.
- * Nearly all essentially atomic control state is held in a few
+ * Nearly all essentially atomic control state is held in two
* volatile variables that are by far most often read (not
- * written) as status and consistency checks. We pack as much
- * information into them as we can.
+ * written) as status and consistency checks. (Also, field
+ * "config" holds unchanging configuration state.)
*
* Field "ctl" contains 64 bits holding information needed to
- * atomically decide to add, enqueue (on an event queue), and
- * dequeue and release workers. To enable this packing, we
- * restrict maximum parallelism to (1<<15)-1 (which is far in
- * excess of normal operating range) to allow ids, counts, and
- * their negations (used for thresholding) to fit into 16bit
+ * atomically decide to add, inactivate, enqueue (on an event
+ * queue), dequeue, and/or re-activate workers. To enable this
+ * packing, we restrict maximum parallelism to (1<<15)-1 (which is
+ * far in excess of normal operating range) to allow ids, counts,
+ * and their negations (used for thresholding) to fit into 16bit
* subfields.
*
- * Field "mode" holds configuration parameters as well as lifetime
- * status, atomically and monotonically setting SHUTDOWN, STOP,
- * and finally TERMINATED bits.
+ * Field "runState" holds lifetime status, atomically and
+ * monotonically setting STARTED, SHUTDOWN, STOP, and finally
+ * TERMINATED bits.
+ *
+ * Field "auxState" is a ReentrantLock subclass that also
+ * opportunistically holds some other bookkeeping fields accessed
+ * only when locked. It is mainly used to lock (infrequent)
+ * updates to workQueues. The auxState instance is itself lazily
+ * constructed (see tryInitialize), requiring a double-check-style
+ * bootstrapping use of field runState, and locking a private
+ * static.
*
* Field "workQueues" holds references to WorkQueues. It is
- * updated (only during worker creation and termination) under
- * lock (using field workerNamePrefix as lock), but is otherwise
- * concurrently readable, and accessed directly. We also ensure
- * that uses of the array reference itself never become too stale
- * in case of resizing, by arranging that (re-)reads are separated
- * by at least one acquiring read access. To simplify index-based
- * operations, the array size is always a power of two, and all
- * readers must tolerate null slots. Worker queues are at odd
- * indices. Shared (submission) queues are at even indices, up to
- * a maximum of 64 slots, to limit growth even if the array needs
- * to expand to add more workers. Grouping them together in this
- * way simplifies and speeds up task scanning.
+ * updated (only during worker creation and termination) under the
+ * lock, but is otherwise concurrently readable, and accessed
+ * directly. We also ensure that reads of the array reference
+ * itself never become too stale (for example, re-reading before
+ * each scan). To simplify index-based operations, the array size
+ * is always a power of two, and all readers must tolerate null
+ * slots. Worker queues are at odd indices. Shared (submission)
+ * queues are at even indices, up to a maximum of 64 slots, to
+ * limit growth even if array needs to expand to add more
+ * workers. Grouping them together in this way simplifies and
+ * speeds up task scanning.
*
* All worker thread creation is on-demand, triggered by task
* submissions, replacement of terminated workers, and/or
@@ -378,37 +361,30 @@
* workers unless there appear to be tasks available. On the
* other hand, we must quickly prod them into action when new
* tasks are submitted or generated. In many usages, ramp-up time
- * is the main limiting factor in overall performance, which is
- * compounded at program start-up by JIT compilation and
- * allocation. So we streamline this as much as possible.
+ * to activate workers is the main limiting factor in overall
+ * performance, which is compounded at program start-up by JIT
+ * compilation and allocation. So we streamline this as much as
+ * possible.
*
- * The "ctl" field atomically maintains total worker and
- * "released" worker counts, plus the head of the available worker
- * queue (actually stack, represented by the lower 32bit subfield
- * of ctl). Released workers are those known to be scanning for
- * and/or running tasks. Unreleased ("available") workers are
- * recorded in the ctl stack. These workers are made available for
- * signalling by enqueuing in ctl (see method runWorker). The
- * "queue" is a form of Treiber stack. This is ideal for
- * activating threads in most-recently used order, and improves
+ * The "ctl" field atomically maintains active and total worker
+ * counts as well as a queue to place waiting threads so they can
+ * be located for signalling. Active counts also play the role of
+ * quiescence indicators, so are decremented when workers believe
+ * that there are no more tasks to execute. The "queue" is
+ * actually a form of Treiber stack. A stack is ideal for
+ * activating threads in most-recently used order. This improves
* performance and locality, outweighing the disadvantages of
* being prone to contention and inability to release a worker
- * unless it is topmost on stack. To avoid missed signal problems
- * inherent in any wait/signal design, available workers rescan
- * for (and if found run) tasks after enqueuing. Normally their
- * release status will be updated while doing so, but the released
- * worker ctl count may underestimate the number of active
- * threads. (However, it is still possible to determine quiescence
- * via a validation traversal -- see isQuiescent). After an
- * unsuccessful rescan, available workers are blocked until
- * signalled (see signalWork). The top stack state holds the
- * value of the "phase" field of the worker: its index and status,
- * plus a version counter that, in addition to the count subfields
- * (also serving as version stamps) provide protection against
- * Treiber stack ABA effects.
+ * unless it is topmost on stack. We block/unblock workers after
+ * pushing on the idle worker stack (represented by the lower
+ * 32bit subfield of ctl) when they cannot find work. The top
+ * stack state holds the value of the "scanState" field of the
+ * worker: its index and status, plus a version counter that, in
+ * addition to the count subfields (also serving as version
+ * stamps) provide protection against Treiber stack ABA effects.
*
- * Creating workers. To create a worker, we pre-increment counts
- * (serving as a reservation), and attempt to construct a
+ * Creating workers. To create a worker, we pre-increment total
+ * count (serving as a reservation), and attempt to construct a
* ForkJoinWorkerThread via its factory. Upon construction, the
* new thread invokes registerWorker, where it constructs a
* WorkQueue and is assigned an index in the workQueues array
@@ -430,14 +406,15 @@
* submission queues for existing external threads (see
* externalPush).
*
- * WorkQueue field "phase" is used by both workers and the pool to
- * manage and track whether a worker is UNSIGNALLED (possibly
- * blocked waiting for a signal). When a worker is enqueued its
- * phase field is set. Note that phase field updates lag queue CAS
- * releases so usage requires care -- seeing a negative phase does
- * not guarantee that the worker is available. When queued, the
- * lower 16 bits of scanState must hold its pool index. So we
- * place the index there upon initialization and otherwise keep it
+ * WorkQueue field scanState is used by both workers and the pool
+ * to manage and track whether a worker is UNSIGNALLED (possibly
+ * blocked waiting for a signal). When a worker is inactivated,
+ * its scanState field is set, and is prevented from executing
+ * tasks, even though it must scan once for them to avoid queuing
+ * races. Note that scanState updates lag queue CAS releases so
+ * usage requires care. When queued, the lower 16 bits of
+ * scanState must hold its pool index. So we place the index there
+ * upon initialization (see registerWorker) and otherwise keep it
* there or restore it when necessary.
*
* The ctl field also serves as the basis for memory
@@ -446,68 +423,85 @@
* consumers sync with each other by both writing/CASing ctl (even
* if to its current value). This would be extremely costly. So
* we relax it in several ways: (1) Producers only signal when
- * their queue is possibly empty at some point during a push
- * operation (which requires conservatively checking size zero or
- * one to cover races). (2) Other workers propagate this signal
- * when they find tasks in a queue with size greater than one. (3)
- * Workers only enqueue after scanning (see below) and not finding
- * any tasks. (4) Rather than CASing ctl to its current value in
- * the common case where no action is required, we reduce write
- * contention by equivalently prefacing signalWork when called by
- * an external task producer using a memory access with
- * full-volatile semantics or a "fullFence".
+ * their queue is empty. Other workers propagate this signal (in
+ * method scan) when they find tasks. (2) Workers only enqueue
+ * after scanning (see below) and not finding any tasks. (3)
+ * Rather than CASing ctl to its current value in the common case
+ * where no action is required, we reduce write contention by
+ * equivalently prefacing signalWork when called by an external
+ * task producer using a memory access with full-volatile
+ * semantics or a "fullFence". (4) For internal task producers we
+ * rely on the fact that even if no other workers awaken, the
+ * producer itself will eventually see the task and execute it.
*
- * Almost always, too many signals are issued, in part because a
- * task producer cannot tell if some existing worker is in the
- * midst of finishing one task (or already scanning) and ready to
- * take another without being signalled. So the producer might
- * instead activate a different worker that does not find any
- * work, and then inactivates. This scarcely matters in
- * steady-state computations involving all workers, but can create
- * contention and bookkeeping bottlenecks during ramp-up,
- * ramp-down, and small computations involving only a few workers.
+ * Almost always, too many signals are issued. A task producer
+ * cannot in general tell if some existing worker is in the midst
+ * of finishing one task (or already scanning) and ready to take
+ * another without being signalled. So the producer might instead
+ * activate a different worker that does not find any work, and
+ * then inactivates. This scarcely matters in steady-state
+ * computations involving all workers, but can create contention
+ * and bookkeeping bottlenecks during ramp-up, ramp-down, and small
+ * computations involving only a few workers.
*
- * Scanning. Method scan (from runWorker) performs top-level
- * scanning for tasks. (Similar scans appear in helpQuiesce and
- * pollScan.) Each scan traverses and tries to poll from each
- * queue starting at a random index. Scans are not performed in
- * ideal random permutation order, to reduce cacheline
- * contention. The pseudorandom generator need not have
- * high-quality statistical properties in the long term, but just
- * within computations; We use Marsaglia XorShifts (often via
- * ThreadLocalRandom.nextSecondarySeed), which are cheap and
- * suffice. Scanning also includes contention reduction: When
- * scanning workers fail to extract an apparently existing task,
- * they soon restart at a different pseudorandom index. This form
- * of backoff improves throughput when many threads are trying to
- * take tasks from few queues, which can be common in some usages.
- * Scans do not otherwise explicitly take into account core
- * affinities, loads, cache localities, etc, However, they do
- * exploit temporal locality (which usually approximates these) by
- * preferring to re-poll from the same queue after a successful
- * poll before trying others (see method topLevelExec). However
- * this preference is bounded (see TOP_BOUND_SHIFT) as a safeguard
- * against infinitely unfair looping under unbounded user task
- * recursion, and also to reduce long-term contention when many
- * threads poll few queues holding many small tasks. The bound is
- * high enough to avoid much impact on locality and scheduling
- * overhead.
+ * Scanning. Method scan() performs top-level scanning for tasks.
+ * Each scan traverses (and tries to poll from) each queue in
+ * pseudorandom permutation order by randomly selecting an origin
+ * index and a step value. (The pseudorandom generator need not
+ * have high-quality statistical properties in the long term, but
+ * just within computations; We use 64bit and 32bit Marsaglia
+ * XorShifts, which are cheap and suffice here.) Scanning also
+ * employs contention reduction: When scanning workers fail a CAS
+ * polling for work, they soon restart with a different
+ * pseudorandom scan order (thus likely retrying at different
+ * intervals). This improves throughput when many threads are
+ * trying to take tasks from few queues. Scans do not otherwise
+ * explicitly take into account core affinities, loads, cache
+ * localities, etc, However, they do exploit temporal locality
+ * (which usually approximates these) by preferring to re-poll (up
+ * to POLL_LIMIT times) from the same queue after a successful
+ * poll before trying others. Restricted forms of scanning occur
+ * in methods helpComplete and findNonEmptyStealQueue, and take
+ * similar but simpler forms.
+ *
+ * Deactivation and waiting. Queuing encounters several intrinsic
+ * races; most notably that an inactivating scanning worker can
+ * miss seeing a task produced during a scan. So when a worker
+ * cannot find a task to steal, it inactivates and enqueues, and
+ * then rescans to ensure that it didn't miss one, reactivating
+ * upon seeing one with probability approximately proportional to
+ * probability of a miss. (In most cases, the worker will be
+ * signalled before self-signalling, avoiding cascades of multiple
+ * signals for the same task).
+ *
+ * Workers block (in method awaitWork) using park/unpark;
+ * advertising the need for signallers to unpark by setting their
+ * "parker" fields.
*
* Trimming workers. To release resources after periods of lack of
* use, a worker starting to wait when the pool is quiescent will
- * time out and terminate (see method runWorker) if the pool has
- * remained quiescent for period given by field keepAlive.
+ * time out and terminate (see awaitWork) if the pool has remained
+ * quiescent for period given by IDLE_TIMEOUT_MS, increasing the
+ * period as the number of threads decreases, eventually removing
+ * all workers.
*
* Shutdown and Termination. A call to shutdownNow invokes
* tryTerminate to atomically set a runState bit. The calling
* thread, as well as every other worker thereafter terminating,
- * helps terminate others by cancelling their unprocessed tasks,
- * and waking them up, doing so repeatedly until stable. Calls to
- * non-abrupt shutdown() preface this by checking whether
- * termination should commence by sweeping through queues (until
- * stable) to ensure lack of in-flight submissions and workers
- * about to process them before triggering the "STOP" phase of
- * termination.
+ * helps terminate others by setting their (qlock) status,
+ * cancelling their unprocessed tasks, and waking them up, doing
+ * so repeatedly until stable. Calls to non-abrupt shutdown()
+ * preface this by checking whether termination should commence.
+ * This relies primarily on the active count bits of "ctl"
+ * maintaining consensus -- tryTerminate is called from awaitWork
+ * whenever quiescent. However, external submitters do not take
+ * part in this consensus. So, tryTerminate sweeps through queues
+ * (until stable) to ensure lack of in-flight submissions and
+ * workers about to process them before triggering the "STOP"
+ * phase of termination. (Note: there is an intrinsic conflict if
+ * helpQuiescePool is called when shutdown is enabled. Both wait
+ * for quiescence, but tryTerminate is biased to not trigger until
+ * helpQuiescePool completes.)
*
* Joining Tasks
* =============
@@ -515,12 +509,12 @@
* Any of several actions may be taken when one worker is waiting
* to join a task stolen (or always held) by another. Because we
* are multiplexing many tasks on to a pool of workers, we can't
- * always just let them block (as in Thread.join). We also cannot
- * just reassign the joiner's run-time stack with another and
- * replace it later, which would be a form of "continuation", that
- * even if possible is not necessarily a good idea since we may
- * need both an unblocked task and its continuation to progress.
- * Instead we combine two tactics:
+ * just let them block (as in Thread.join). We also cannot just
+ * reassign the joiner's run-time stack with another and replace
+ * it later, which would be a form of "continuation", that even if
+ * possible is not necessarily a good idea since we may need both
+ * an unblocked task and its continuation to progress. Instead we
+ * combine two tactics:
*
* Helping: Arranging for the joiner to execute some task that it
* would be running if the steal had not occurred.
@@ -533,44 +527,79 @@
* helping a hypothetical compensator: If we can readily tell that
* a possible action of a compensator is to steal and execute the
* task being joined, the joining thread can do so directly,
- * without the need for a compensation thread.
+ * without the need for a compensation thread (although at the
+ * expense of larger run-time stacks, but the tradeoff is
+ * typically worthwhile).
*
* The ManagedBlocker extension API can't use helping so relies
* only on compensation in method awaitBlocker.
*
- * The algorithm in awaitJoin entails a form of "linear helping".
- * Each worker records (in field source) the id of the queue from
- * which it last stole a task. The scan in method awaitJoin uses
- * these markers to try to find a worker to help (i.e., steal back
- * a task from and execute it) that could hasten completion of the
- * actively joined task. Thus, the joiner executes a task that
- * would be on its own local deque if the to-be-joined task had
- * not been stolen. This is a conservative variant of the approach
- * described in Wagner & Calder "Leapfrogging: a portable
- * technique for implementing efficient futures" SIGPLAN Notices,
- * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
- * mainly in that we only record queue ids, not full dependency
- * links. This requires a linear scan of the workQueues array to
- * locate stealers, but isolates cost to when it is needed, rather
- * than adding to per-task overhead. Searches can fail to locate
- * stealers GC stalls and the like delay recording sources.
- * Further, even when accurately identified, stealers might not
- * ever produce a task that the joiner can in turn help with. So,
- * compensation is tried upon failure to find tasks to run.
+ * The algorithm in helpStealer entails a form of "linear
+ * helping". Each worker records (in field currentSteal) the most
+ * recent task it stole from some other worker (or a submission).
+ * It also records (in field currentJoin) the task it is currently
+ * actively joining. Method helpStealer uses these markers to try
+ * to find a worker to help (i.e., steal back a task from and
+ * execute it) that could hasten completion of the actively joined
+ * task. Thus, the joiner executes a task that would be on its
+ * own local deque had the to-be-joined task not been stolen. This
+ * is a conservative variant of the approach described in Wagner &
+ * Calder "Leapfrogging: a portable technique for implementing
+ * efficient futures" SIGPLAN Notices, 1993
+ * (http://portal.acm.org/citation.cfm?id=155354). It differs in
+ * that: (1) We only maintain dependency links across workers upon
+ * steals, rather than use per-task bookkeeping. This sometimes
+ * requires a linear scan of workQueues array to locate stealers,
+ * but often doesn't because stealers leave hints (that may become
+ * stale/wrong) of where to locate them. It is only a hint
+ * because a worker might have had multiple steals and the hint
+ * records only one of them (usually the most current). Hinting
+ * isolates cost to when it is needed, rather than adding to
+ * per-task overhead. (2) It is "shallow", ignoring nesting and
+ * potentially cyclic mutual steals. (3) It is intentionally
+ * racy: field currentJoin is updated only while actively joining,
+ * which means that we miss links in the chain during long-lived
+ * tasks, GC stalls etc (which is OK since blocking in such cases
+ * is usually a good idea). (4) We bound the number of attempts
+ * to find work using checksums and fall back to suspending the
+ * worker and if necessary replacing it with another.
*
- * Compensation does not by default aim to keep exactly the target
+ * Helping actions for CountedCompleters do not require tracking
+ * currentJoins: Method helpComplete takes and executes any task
+ * with the same root as the task being waited on (preferring
+ * local pops to non-local polls). However, this still entails
+ * some traversal of completer chains, so is less efficient than
+ * using CountedCompleters without explicit joins.
+ *
+ * Compensation does not aim to keep exactly the target
* parallelism number of unblocked threads running at any given
* time. Some previous versions of this class employed immediate
* compensations for any blocked join. However, in practice, the
* vast majority of blockages are transient byproducts of GC and
- * other JVM or OS activities that are made worse by replacement
- * when they cause longer-term oversubscription. Rather than
- * impose arbitrary policies, we allow users to override the
- * default of only adding threads upon apparent starvation. The
- * compensation mechanism may also be bounded. Bounds for the
+ * other JVM or OS activities that are made worse by replacement.
+ * Currently, compensation is attempted only after validating that
+ * all purportedly active threads are processing tasks by checking
+ * field WorkQueue.scanState, which eliminates most false
+ * positives. Also, compensation is bypassed (tolerating fewer
+ * threads) in the most common case in which it is rarely
+ * beneficial: when a worker with an empty queue (thus no
+ * continuation tasks) blocks on a join and there still remain
+ * enough threads to ensure liveness.
+ *
+ * Spare threads are removed as soon as they notice that the
+ * target parallelism level has been exceeded, in method
+ * tryDropSpare. (Method scan arranges returns for rechecks upon
+ * each probe via the "bound" parameter.)
+ *
+ * The compensation mechanism may be bounded. Bounds for the
* commonPool (see COMMON_MAX_SPARES) better enable JVMs to cope
* with programming errors and abuse before running out of
- * resources to do so.
+ * resources to do so. In other cases, users may supply factories
+ * that limit thread construction. The effects of bounding in this
+ * pool (like all others) is imprecise. Total worker counts are
+ * decremented when threads deregister, not when they exit and
+ * resources are reclaimed by the JVM and OS. So the number of
+ * simultaneously live threads may transiently exceed bounds.
*
* Common Pool
* ===========
@@ -578,7 +607,9 @@
* The static common pool always exists after static
* initialization. Since it (or any other created pool) need
* never be used, we minimize initial construction overhead and
- * footprint to the setup of about a dozen fields.
+ * footprint to the setup of about a dozen fields, with no nested
+ * allocation. Most bootstrapping occurs within method
+ * externalSubmit during the first submission to the pool.
*
* When external threads submit to the common pool, they can
* perform subtask processing (see externalHelpComplete and
@@ -598,39 +629,31 @@
* InnocuousForkJoinWorkerThread when there is a SecurityManager
* present. These workers have no permissions set, do not belong
* to any user-defined ThreadGroup, and erase all ThreadLocals
- * after executing any top-level task (see
- * WorkQueue.afterTopLevelExec). The associated mechanics (mainly
- * in ForkJoinWorkerThread) may be JVM-dependent and must access
- * particular Thread class fields to achieve this effect.
- *
- * Memory placement
- * ================
- *
- * Performance can be very sensitive to placement of instances of
- * ForkJoinPool and WorkQueues and their queue arrays. To reduce
- * false-sharing impact, the @Contended annotation isolates
- * adjacent WorkQueue instances, as well as the ForkJoinPool.ctl
- * field. WorkQueue arrays are allocated (by their threads) with
- * larger initial sizes than most ever need, mostly to reduce
- * false sharing with current garbage collectors that use cardmark
- * tables.
+ * after executing any top-level task (see WorkQueue.runTask).
+ * The associated mechanics (mainly in ForkJoinWorkerThread) may
+ * be JVM-dependent and must access particular Thread class fields
+ * to achieve this effect.
*
* Style notes
* ===========
*
- * Memory ordering relies mainly on VarHandles. This can be
- * awkward and ugly, but also reflects the need to control
+ * Memory ordering relies mainly on Unsafe intrinsics that carry
+ * the further responsibility of explicitly performing null- and
+ * bounds- checks otherwise carried out implicitly by JVMs. This
+ * can be awkward and ugly, but also reflects the need to control
* outcomes across the unusual cases that arise in very racy code
- * with very few invariants. All fields are read into locals
- * before use, and null-checked if they are references. Array
- * accesses using masked indices include checks (that are always
- * true) that the array length is non-zero to avoid compilers
- * inserting more expensive traps. This is usually done in a
- * "C"-like style of listing declarations at the heads of methods
- * or blocks, and using inline assignments on first encounter.
- * Nearly all explicit checks lead to bypass/return, not exception
- * throws, because they may legitimately arise due to
- * cancellation/revocation during shutdown.
+ * with very few invariants. So these explicit checks would exist
+ * in some form anyway. All fields are read into locals before
+ * use, and null-checked if they are references. This is usually
+ * done in a "C"-like style of listing declarations at the heads
+ * of methods or blocks, and using inline assignments on first
+ * encounter. Array bounds-checks are usually performed by
+ * masking with array.length-1, which relies on the invariant that
+ * these arrays are created with positive lengths, which is itself
+ * paranoically checked. Nearly all explicit checks lead to
+ * bypass/return, not exception throws, because they may
+ * legitimately arise due to cancellation/revocation during
+ * shutdown.
*
* There is a lot of representation-level coupling among classes
* ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask. The
@@ -640,11 +663,10 @@
* representations will need to be accompanied by algorithmic
* changes anyway. Several methods intrinsically sprawl because
* they must accumulate sets of consistent reads of fields held in
- * local variables. Some others are artificially broken up to
- * reduce producer/consumer imbalances due to dynamic compilation.
- * There are also other coding oddities (including several
- * unnecessary-looking hoisted null checks) that help some methods
- * perform reasonably even when interpreted (not compiled).
+ * local variables. There are also other coding oddities
+ * (including several unnecessary-looking hoisted null checks)
+ * that help some methods perform reasonably even when interpreted
+ * (not compiled).
*
* The order of declarations in this file is (with a few exceptions):
* (1) Static utility functions
@@ -680,13 +702,6 @@
public static interface ForkJoinWorkerThreadFactory {
/**
* Returns a new worker thread operating in the given pool.
- * Returning null or throwing an exception may result in tasks
- * never being executed. If this method throws an exception,
- * it is relayed to the caller of the method (for example
- * {@code execute}) causing attempted thread creation. If this
- * method returns null or throws an exception, it is not
- * retried until the next attempted creation (for example
- * another call to {@code execute}).
*
* @param pool the pool this thread works in
* @return the new worker thread, or {@code null} if the request
@@ -696,98 +711,120 @@
public ForkJoinWorkerThread newThread(ForkJoinPool pool);
}
- static AccessControlContext contextWithPermissions(Permission ... perms) {
- Permissions permissions = new Permissions();
- for (Permission perm : perms)
- permissions.add(perm);
- return new AccessControlContext(
- new ProtectionDomain[] { new ProtectionDomain(null, permissions) });
- }
-
/**
* Default ForkJoinWorkerThreadFactory implementation; creates a
- * new ForkJoinWorkerThread using the system class loader as the
- * thread context class loader.
+ * new ForkJoinWorkerThread.
*/
private static final class DefaultForkJoinWorkerThreadFactory
implements ForkJoinWorkerThreadFactory {
- private static final AccessControlContext ACC = contextWithPermissions(
- new RuntimePermission("getClassLoader"),
- new RuntimePermission("setContextClassLoader"));
-
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
- return AccessController.doPrivileged(
- new PrivilegedAction<>() {
- public ForkJoinWorkerThread run() {
- return new ForkJoinWorkerThread(
- pool, ClassLoader.getSystemClassLoader()); }},
- ACC);
+ return new ForkJoinWorkerThread(pool);
}
}
+ /**
+ * Class for artificial tasks that are used to replace the target
+ * of local joins if they are removed from an interior queue slot
+ * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
+ * actually do anything beyond having a unique identity.
+ */
+ private static final class EmptyTask extends ForkJoinTask<Void> {
+ private static final long serialVersionUID = -7721805057305804111L;
+ EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void x) {}
+ public final boolean exec() { return true; }
+ }
+
+ /**
+ * Additional fields and lock created upon initialization.
+ */
+ private static final class AuxState extends ReentrantLock {
+ private static final long serialVersionUID = -6001602636862214147L;
+ volatile long stealCount; // cumulative steal count
+ long indexSeed; // index bits for registerWorker
+ AuxState() {}
+ }
+
// Constants shared across ForkJoinPool and WorkQueue
// Bounds
- static final int SWIDTH = 16; // width of short
static final int SMASK = 0xffff; // short bits == max index
static final int MAX_CAP = 0x7fff; // max #workers - 1
+ static final int EVENMASK = 0xfffe; // even short bits
static final int SQMASK = 0x007e; // max 64 (even) slots
- // Masks and units for WorkQueue.phase and ctl sp subfield
+ // Masks and units for WorkQueue.scanState and ctl sp subfield
static final int UNSIGNALLED = 1 << 31; // must be negative
static final int SS_SEQ = 1 << 16; // version count
- static final int QLOCK = 1; // must be 1
- // Mode bits and sentinels, some also used in WorkQueue id and.source fields
- static final int OWNED = 1; // queue has owner thread
- static final int FIFO = 1 << 16; // fifo queue or access mode
- static final int SHUTDOWN = 1 << 18;
- static final int TERMINATED = 1 << 19;
- static final int STOP = 1 << 31; // must be negative
- static final int QUIET = 1 << 30; // not scanning or working
- static final int DORMANT = QUIET | UNSIGNALLED;
+ // Mode bits for ForkJoinPool.config and WorkQueue.config
+ static final int MODE_MASK = 0xffff << 16; // top half of int
+ static final int SPARE_WORKER = 1 << 17; // set if tc > 0 on creation
+ static final int UNREGISTERED = 1 << 18; // to skip some of deregister
+ static final int FIFO_QUEUE = 1 << 31; // must be negative
+ static final int LIFO_QUEUE = 0; // for clarity
+ static final int IS_OWNED = 1; // low bit 0 if shared
/**
- * Initial capacity of work-stealing queue array.
- * Must be a power of two, at least 2.
+ * The maximum number of task executions from the same queue
+ * before checking other queues, bounding unfairness and impact of
+ * infinite user task recursion. Must be a power of two minus 1.
*/
- static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
-
- /**
- * Maximum capacity for queue arrays. Must be a power of two less
- * than or equal to 1 << (31 - width of array entry) to ensure
- * lack of wraparound of index calculations, but defined to a
- * value a bit less than this to help users trap runaway programs
- * before saturating systems.
- */
- static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
-
- /**
- * The maximum number of top-level polls per worker before
- * checking other queues, expressed as a bit shift to, in effect,
- * multiply by pool size, and then use as random value mask, so
- * average bound is about poolSize*(1<<TOP_BOUND_SHIFT). See
- * above for rationale.
- */
- static final int TOP_BOUND_SHIFT = 10;
+ static final int POLL_LIMIT = (1 << 10) - 1;
/**
* Queues supporting work-stealing as well as external task
* submission. See above for descriptions and algorithms.
+ * Performance on most platforms is very sensitive to placement of
+ * instances of both WorkQueues and their arrays -- we absolutely
+ * do not want multiple WorkQueue instances or multiple queue
+ * arrays sharing cache lines. The @Contended annotation alerts
+ * JVMs to try to keep instances apart.
*/
// Android-removed: @Contended, this hint is not used by the Android runtime.
//@jdk.internal.vm.annotation.Contended
static final class WorkQueue {
- volatile int source; // source queue id, or sentinel
- int id; // pool index, mode, tag
- int base; // index of next slot for poll
- int top; // index of next slot for push
- volatile int phase; // versioned, negative: queued, 1: locked
- int stackPred; // pool stack (ctl) predecessor link
+
+ /**
+ * Capacity of work-stealing queue array upon initialization.
+ * Must be a power of two; at least 4, but should be larger to
+ * reduce or eliminate cacheline sharing among queues.
+ * Currently, it is much larger, as a partial workaround for
+ * the fact that JVMs often place arrays in locations that
+ * share GC bookkeeping (especially cardmarks) such that
+ * per-write accesses encounter serious memory contention.
+ */
+ static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+ /**
+ * Maximum size for queue arrays. Must be a power of two less
+ * than or equal to 1 << (31 - width of array entry) to ensure
+ * lack of wraparound of index calculations, but defined to a
+ * value a bit less than this to help users trap runaway
+ * programs before saturating systems.
+ */
+ static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
+
+ // Instance fields
+
+ volatile int scanState; // versioned, negative if inactive
+ int stackPred; // pool stack (ctl) predecessor
int nsteals; // number of steals
- ForkJoinTask<?>[] array; // the queued tasks; power of 2 size
+ int hint; // randomization and stealer index hint
+ int config; // pool index and mode
+ volatile int qlock; // 1: locked, < 0: terminate; else 0
+ volatile int base; // index of next slot for poll
+ int top; // index of next slot for push
+ ForkJoinTask<?>[] array; // the elements (initially unallocated)
final ForkJoinPool pool; // the containing pool (may be null)
final ForkJoinWorkerThread owner; // owning thread or null if shared
+ volatile Thread parker; // == owner during call to park; else null
+ volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
+
+ // Android-removed: @Contended, this hint is not used by the Android runtime.
+ // @jdk.internal.vm.annotation.Contended("group2") // segregate
+ volatile ForkJoinTask<?> currentSteal; // nonnull when running some task
WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner) {
this.pool = pool;
@@ -797,28 +834,17 @@
}
/**
- * Tries to lock shared queue by CASing phase field.
- */
- final boolean tryLockPhase() {
- return PHASE.compareAndSet(this, 0, 1);
- }
-
- final void releasePhaseLock() {
- PHASE.setRelease(this, 0);
- }
-
- /**
* Returns an exportable index (used by ForkJoinWorkerThread).
*/
final int getPoolIndex() {
- return (id & 0xffff) >>> 1; // ignore odd/even tag bit
+ return (config & 0xffff) >>> 1; // ignore odd/even tag bit
}
/**
* Returns the approximate number of tasks in the queue.
*/
final int queueSize() {
- int n = (int)BASE.getAcquire(this) - top;
+ int n = base - top; // read base first
return (n >= 0) ? 0 : -n; // ignore transient negative
}
@@ -828,12 +854,11 @@
* near-empty queue has at least one unclaimed task.
*/
final boolean isEmpty() {
- ForkJoinTask<?>[] a; int n, cap, b;
- VarHandle.acquireFence(); // needed by external callers
- return ((n = (b = base) - top) >= 0 || // possibly one task
+ ForkJoinTask<?>[] a; int n, al, s;
+ return ((n = base - (s = top)) >= 0 || // possibly one task
(n == -1 && ((a = array) == null ||
- (cap = a.length) == 0 ||
- a[(cap - 1) & b] == null)));
+ (al = a.length) == 0 ||
+ a[(al - 1) & (s - 1)] == null)));
}
/**
@@ -843,99 +868,116 @@
* @throws RejectedExecutionException if array cannot be resized
*/
final void push(ForkJoinTask<?> task) {
- ForkJoinTask<?>[] a;
- int s = top, d, cap, m;
- ForkJoinPool p = pool;
- if ((a = array) != null && (cap = a.length) > 0) {
- QA.setRelease(a, (m = cap - 1) & s, task);
+ U.storeFence(); // ensure safe publication
+ int s = top, al, d; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ a[(al - 1) & s] = task; // relaxed writes OK
top = s + 1;
- if (((d = s - (int)BASE.getAcquire(this)) & ~1) == 0 &&
- p != null) { // size 0 or 1
- VarHandle.fullFence();
+ ForkJoinPool p = pool;
+ if ((d = base - s) == 0 && p != null) {
+ U.fullFence();
p.signalWork();
}
- else if (d == m)
- growArray(false);
+ else if (al + d == 1)
+ growArray();
}
}
/**
- * Version of push for shared queues. Call only with phase lock held.
- * @return true if should signal work
+ * Initializes or doubles the capacity of array. Call either
+ * by owner or with lock held -- it is OK for base, but not
+ * top, to move while resizings are in progress.
*/
- final boolean lockedPush(ForkJoinTask<?> task) {
- ForkJoinTask<?>[] a;
- boolean signal = false;
- int s = top, b = base, cap, d;
- if ((a = array) != null && (cap = a.length) > 0) {
- a[(cap - 1) & s] = task;
- top = s + 1;
- if (b - s + cap - 1 == 0)
- growArray(true);
- else {
- phase = 0; // full volatile unlock
- if (((s - base) & ~1) == 0) // size 0 or 1
- signal = true;
- }
- }
- return signal;
- }
-
- /**
- * Doubles the capacity of array. Call either by owner or with
- * lock held -- it is OK for base, but not top, to move while
- * resizings are in progress.
- */
- final void growArray(boolean locked) {
- ForkJoinTask<?>[] newA = null;
- try {
- ForkJoinTask<?>[] oldA; int oldSize, newSize;
- if ((oldA = array) != null && (oldSize = oldA.length) > 0 &&
- (newSize = oldSize << 1) <= MAXIMUM_QUEUE_CAPACITY &&
- newSize > 0) {
- try {
- newA = new ForkJoinTask<?>[newSize];
- } catch (OutOfMemoryError ex) {
- }
- if (newA != null) { // poll from old array, push to new
- int oldMask = oldSize - 1, newMask = newSize - 1;
- for (int s = top - 1, k = oldMask; k >= 0; --k) {
- ForkJoinTask<?> x = (ForkJoinTask<?>)
- QA.getAndSet(oldA, s & oldMask, null);
- if (x != null)
- newA[s-- & newMask] = x;
- else
- break;
- }
- array = newA;
- VarHandle.releaseFence();
- }
- }
- } finally {
- if (locked)
- phase = 0;
- }
- if (newA == null)
+ final ForkJoinTask<?>[] growArray() {
+ ForkJoinTask<?>[] oldA = array;
+ int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size < INITIAL_QUEUE_CAPACITY || size > MAXIMUM_QUEUE_CAPACITY)
throw new RejectedExecutionException("Queue capacity exceeded");
+ int oldMask, t, b;
+ ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
+ if (oldA != null && (oldMask = oldA.length - 1) > 0 &&
+ (t = top) - (b = base) > 0) {
+ int mask = size - 1;
+ do { // emulate poll from old array, push to new array
+ int index = b & oldMask;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> x = (ForkJoinTask<?>)
+ U.getObjectVolatile(oldA, offset);
+ if (x != null &&
+ U.compareAndSwapObject(oldA, offset, x, null))
+ a[b & mask] = x;
+ } while (++b != t);
+ U.storeFence();
+ }
+ return a;
+ }
+
+ /**
+ * Takes next task, if one exists, in LIFO order. Call only
+ * by owner in unshared queues.
+ */
+ final ForkJoinTask<?> pop() {
+ int b = base, s = top, al, i; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObject(a, offset);
+ if (t != null &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes a task in FIFO order if b is base of queue and a task
+ * can be claimed without contention. Specialized versions
+ * appear in ForkJoinPool methods scan and helpStealer.
+ */
+ final ForkJoinTask<?> pollAt(int b) {
+ ForkJoinTask<?>[] a; int al;
+ if ((a = array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t != null && b++ == base &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
+ return t;
+ }
+ }
+ return null;
}
/**
* Takes next task, if one exists, in FIFO order.
*/
final ForkJoinTask<?> poll() {
- int b, k, cap; ForkJoinTask<?>[] a;
- while ((a = array) != null && (cap = a.length) > 0 &&
- top - (b = base) > 0) {
- ForkJoinTask<?> t = (ForkJoinTask<?>)
- QA.getAcquire(a, k = (cap - 1) & b);
- if (base == b++) {
- if (t == null)
- Thread.yield(); // await index advance
- else if (QA.compareAndSet(a, k, t, null)) {
- BASE.setOpaque(this, b);
- return t;
+ for (;;) {
+ int b = base, s = top, d, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (d = b - s) < 0 &&
+ (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (b++ == base) {
+ if (t != null) {
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
+ return t;
+ }
+ }
+ else if (d == -1)
+ break; // now empty
}
}
+ else
+ break;
}
return null;
}
@@ -944,59 +986,96 @@
* Takes next task, if one exists, in order specified by mode.
*/
final ForkJoinTask<?> nextLocalTask() {
- ForkJoinTask<?> t = null;
- int md = id, b, s, d, cap; ForkJoinTask<?>[] a;
- if ((a = array) != null && (cap = a.length) > 0 &&
- (d = (s = top) - (b = base)) > 0) {
- if ((md & FIFO) == 0 || d == 1) {
- if ((t = (ForkJoinTask<?>)
- QA.getAndSet(a, (cap - 1) & --s, null)) != null)
- TOP.setOpaque(this, s);
- }
- else if ((t = (ForkJoinTask<?>)
- QA.getAndSet(a, (cap - 1) & b++, null)) != null) {
- BASE.setOpaque(this, b);
- }
- else // on contention in FIFO mode, use regular poll
- t = poll();
- }
- return t;
+ return (config < 0) ? poll() : pop();
}
/**
* Returns next task, if one exists, in order specified by mode.
*/
final ForkJoinTask<?> peek() {
- int cap; ForkJoinTask<?>[] a;
- return ((a = array) != null && (cap = a.length) > 0) ?
- a[(cap - 1) & ((id & FIFO) != 0 ? base : top - 1)] : null;
+ int al; ForkJoinTask<?>[] a;
+ return ((a = array) != null && (al = a.length) > 0) ?
+ a[(al - 1) & (config < 0 ? base : top - 1)] : null;
}
/**
* Pops the given task only if it is at the current top.
*/
final boolean tryUnpush(ForkJoinTask<?> task) {
- boolean popped = false;
- int s, cap; ForkJoinTask<?>[] a;
- if ((a = array) != null && (cap = a.length) > 0 &&
- (s = top) != base &&
- (popped = QA.compareAndSet(a, (cap - 1) & --s, task, null)))
- TOP.setOpaque(this, s);
- return popped;
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ if (U.compareAndSwapObject(a, offset, task, null)) {
+ top = s;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Shared version of push. Fails if already locked.
+ *
+ * @return status: > 0 locked, 0 possibly was empty, < 0 was nonempty
+ */
+ final int sharedPush(ForkJoinTask<?> task) {
+ int stat;
+ if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ int b = base, s = top, al, d; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0 &&
+ al - 1 + (d = b - s) > 0) {
+ a[(al - 1) & s] = task;
+ top = s + 1; // relaxed writes OK here
+ qlock = 0;
+ stat = (d < 0 && b == base) ? d : 0;
+ }
+ else {
+ growAndSharedPush(task);
+ stat = 0;
+ }
+ }
+ else
+ stat = 1;
+ return stat;
+ }
+
+ /**
+ * Helper for sharedPush; called only when locked and resize
+ * needed.
+ */
+ private void growAndSharedPush(ForkJoinTask<?> task) {
+ try {
+ growArray();
+ int s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ a[(al - 1) & s] = task;
+ top = s + 1;
+ }
+ } finally {
+ qlock = 0;
+ }
}
/**
* Shared version of tryUnpush.
*/
- final boolean tryLockedUnpush(ForkJoinTask<?> task) {
+ final boolean trySharedUnpush(ForkJoinTask<?> task) {
boolean popped = false;
- int s = top - 1, k, cap; ForkJoinTask<?>[] a;
- if ((a = array) != null && (cap = a.length) > 0 &&
- a[k = (cap - 1) & s] == task && tryLockPhase()) {
- if (top == s + 1 && array == a &&
- (popped = QA.compareAndSet(a, k, task, null)))
- top = s;
- releasePhaseLock();
+ int s = top - 1, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>) U.getObject(a, offset);
+ if (t == task &&
+ U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ if (top == s + 1 && array == a &&
+ U.compareAndSwapObject(a, offset, task, null)) {
+ popped = true;
+ top = s;
+ }
+ U.putOrderedInt(this, QLOCK, 0);
+ }
}
return popped;
}
@@ -1005,150 +1084,252 @@
* Removes and cancels all known tasks, ignoring any exceptions.
*/
final void cancelAll() {
- for (ForkJoinTask<?> t; (t = poll()) != null; )
+ ForkJoinTask<?> t;
+ if ((t = currentJoin) != null) {
+ currentJoin = null;
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+ if ((t = currentSteal) != null) {
+ currentSteal = null;
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+ while ((t = poll()) != null)
ForkJoinTask.cancelIgnoringExceptions(t);
}
// Specialized execution methods
/**
- * Runs the given (stolen) task if nonnull, as well as
- * remaining local tasks and others available from the given
- * queue, up to bound n (to avoid infinite unfairness).
+ * Pops and executes up to POLL_LIMIT tasks or until empty.
*/
- final void topLevelExec(ForkJoinTask<?> t, WorkQueue q, int n) {
- if (t != null && q != null) { // hoist checks
- int nstolen = 1;
- for (;;) {
- t.doExec();
- if (n-- < 0)
- break;
- else if ((t = nextLocalTask()) == null) {
- if ((t = q.poll()) == null)
+ final void localPopAndExec() {
+ for (int nexec = 0;;) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getAndSetObject(a, offset, null);
+ if (t != null) {
+ top = s;
+ (currentSteal = t).doExec();
+ if (++nexec > POLL_LIMIT)
break;
- else
- ++nstolen;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ }
+
+ /**
+ * Polls and executes up to POLL_LIMIT tasks or until empty.
+ */
+ final void localPollAndExec() {
+ for (int nexec = 0;;) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & b++;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getAndSetObject(a, offset, null);
+ if (t != null) {
+ base = b;
+ t.doExec();
+ if (++nexec > POLL_LIMIT)
+ break;
}
}
+ else
+ break;
+ }
+ }
+
+ /**
+ * Executes the given task and (some) remaining local tasks.
+ */
+ final void runTask(ForkJoinTask<?> task) {
+ if (task != null) {
+ task.doExec();
+ if (config < 0)
+ localPollAndExec();
+ else
+ localPopAndExec();
+ int ns = ++nsteals;
ForkJoinWorkerThread thread = owner;
- nsteals += nstolen;
- source = 0;
+ currentSteal = null;
+ if (ns < 0) // collect on overflow
+ transferStealCount(pool);
if (thread != null)
thread.afterTopLevelExec();
}
}
/**
- * If present, removes task from queue and executes it.
+ * Adds steal count to pool steal count if it exists, and resets.
*/
- final void tryRemoveAndExec(ForkJoinTask<?> task) {
- ForkJoinTask<?>[] a; int s, cap;
- if ((a = array) != null && (cap = a.length) > 0 &&
- (s = top) - base > 0) { // traverse from top
- for (int m = cap - 1, ns = s - 1, i = ns; ; --i) {
- int index = i & m;
- ForkJoinTask<?> t = (ForkJoinTask<?>)QA.get(a, index);
- if (t == null)
- break;
- else if (t == task) {
- if (QA.compareAndSet(a, index, t, null)) {
- top = ns; // safely shift down
- for (int j = i; j != ns; ++j) {
- ForkJoinTask<?> f;
- int pindex = (j + 1) & m;
- f = (ForkJoinTask<?>)QA.get(a, pindex);
- QA.setVolatile(a, pindex, null);
- int jindex = j & m;
- QA.setRelease(a, jindex, f);
- }
- VarHandle.releaseFence();
- t.doExec();
- }
- break;
- }
+ final void transferStealCount(ForkJoinPool p) {
+ AuxState aux;
+ if (p != null && (aux = p.auxState) != null) {
+ long s = nsteals;
+ nsteals = 0; // if negative, correct for overflow
+ if (s < 0) s = Integer.MAX_VALUE;
+ aux.lock();
+ try {
+ aux.stealCount += s;
+ } finally {
+ aux.unlock();
}
}
}
/**
- * Tries to pop and run tasks within the target's computation
- * until done, not found, or limit exceeded.
+ * If present, removes from queue and executes the given task,
+ * or any other cancelled task. Used only by awaitJoin.
*
- * @param task root of CountedCompleter computation
- * @param limit max runs, or zero for no limit
- * @param shared true if must lock to extract task
- * @return task status on exit
+ * @return true if queue empty and task not known to be done
*/
- final int helpCC(CountedCompleter<?> task, int limit, boolean shared) {
- int status = 0;
- if (task != null && (status = task.status) >= 0) {
- int s, k, cap; ForkJoinTask<?>[] a;
- while ((a = array) != null && (cap = a.length) > 0 &&
- (s = top) - base > 0) {
- CountedCompleter<?> v = null;
- ForkJoinTask<?> o = a[k = (cap - 1) & (s - 1)];
- if (o instanceof CountedCompleter) {
- CountedCompleter<?> t = (CountedCompleter<?>)o;
- for (CountedCompleter<?> f = t;;) {
- if (f != task) {
- if ((f = f.completer) == null)
- break;
- }
- else if (shared) {
- if (tryLockPhase()) {
- if (top == s && array == a &&
- QA.compareAndSet(a, k, t, null)) {
- top = s - 1;
- v = t;
- }
- releasePhaseLock();
+ final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
+ if (task != null && task.status >= 0) {
+ int b, s, d, al; ForkJoinTask<?>[] a;
+ while ((d = (b = base) - (s = top)) < 0 &&
+ (a = array) != null && (al = a.length) > 0) {
+ for (;;) { // traverse from s to b
+ int index = --s & (al - 1);
+ long offset = (index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t == null)
+ break; // restart
+ else if (t == task) {
+ boolean removed = false;
+ if (s + 1 == top) { // pop
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ removed = true;
}
- break;
}
- else {
- if (QA.compareAndSet(a, k, t, null)) {
- top = s - 1;
- v = t;
- }
- break;
+ else if (base == b) // replace with proxy
+ removed = U.compareAndSwapObject(a, offset, t,
+ new EmptyTask());
+ if (removed) {
+ ForkJoinTask<?> ps = currentSteal;
+ (currentSteal = task).doExec();
+ currentSteal = ps;
}
- }
- }
- if (v != null)
- v.doExec();
- if ((status = task.status) < 0 || v == null ||
- (limit != 0 && --limit == 0))
- break;
- }
- }
- return status;
- }
-
- /**
- * Tries to poll and run AsynchronousCompletionTasks until
- * none found or blocker is released
- *
- * @param blocker the blocker
- */
- final void helpAsyncBlocker(ManagedBlocker blocker) {
- if (blocker != null) {
- int b, k, cap; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
- while ((a = array) != null && (cap = a.length) > 0 &&
- top - (b = base) > 0) {
- t = (ForkJoinTask<?>)QA.getAcquire(a, k = (cap - 1) & b);
- if (blocker.isReleasable())
- break;
- else if (base == b++ && t != null) {
- if (!(t instanceof CompletableFuture.
- AsynchronousCompletionTask))
break;
- else if (QA.compareAndSet(a, k, t, null)) {
- BASE.setOpaque(this, b);
- t.doExec();
+ }
+ else if (t.status < 0 && s + 1 == top) {
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ }
+ break; // was cancelled
+ }
+ else if (++d == 0) {
+ if (base != b) // rescan
+ break;
+ return false;
+ }
+ }
+ if (task.status < 0)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Pops task if in the same CC computation as the given task,
+ * in either shared or owned mode. Used only by helpComplete.
+ */
+ final CountedCompleter<?> popCC(CountedCompleter<?> task, int mode) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & (s - 1);
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (o instanceof CountedCompleter) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> r = t;;) {
+ if (r == task) {
+ if ((mode & IS_OWNED) == 0) {
+ boolean popped = false;
+ if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ if (top == s && array == a &&
+ U.compareAndSwapObject(a, offset,
+ t, null)) {
+ popped = true;
+ top = s - 1;
+ }
+ U.putOrderedInt(this, QLOCK, 0);
+ if (popped)
+ return t;
+ }
+ }
+ else if (U.compareAndSwapObject(a, offset,
+ t, null)) {
+ top = s - 1;
+ return t;
+ }
+ break;
+ }
+ else if ((r = r.completer) == null) // try parent
+ break;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Steals and runs a task in the same CC computation as the
+ * given task if one exists and can be taken without
+ * contention. Otherwise returns a checksum/control value for
+ * use by method helpComplete.
+ *
+ * @return 1 if successful, 2 if retryable (lost to another
+ * stealer), -1 if non-empty but no matching task found, else
+ * the base index, forced negative.
+ */
+ final int pollAndExecCC(CountedCompleter<?> task) {
+ ForkJoinTask<?>[] a;
+ int b = base, s = top, al, h;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (o == null)
+ h = 2; // retryable
+ else if (!(o instanceof CountedCompleter))
+ h = -1; // unmatchable
+ else {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> r = t;;) {
+ if (r == task) {
+ if (b++ == base &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
+ t.doExec();
+ h = 1; // success
+ }
+ else
+ h = 2; // lost CAS
+ break;
+ }
+ else if ((r = r.completer) == null) {
+ h = -1; // unmatched
+ break;
}
}
}
}
+ else
+ h = b | Integer.MIN_VALUE; // to sense movement on re-poll
+ return h;
}
/**
@@ -1156,24 +1337,29 @@
*/
final boolean isApparentlyUnblocked() {
Thread wt; Thread.State s;
- return ((wt = owner) != null &&
+ return (scanState >= 0 &&
+ (wt = owner) != null &&
(s = wt.getState()) != Thread.State.BLOCKED &&
s != Thread.State.WAITING &&
s != Thread.State.TIMED_WAITING);
}
- // VarHandle mechanics.
- static final VarHandle PHASE;
- static final VarHandle BASE;
- static final VarHandle TOP;
+ // Unsafe mechanics. Note that some are (and must be) the same as in FJP
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long QLOCK;
+ private static final int ABASE;
+ private static final int ASHIFT;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- PHASE = l.findVarHandle(WorkQueue.class, "phase", int.class);
- BASE = l.findVarHandle(WorkQueue.class, "base", int.class);
- TOP = l.findVarHandle(WorkQueue.class, "top", int.class);
+ QLOCK = U.objectFieldOffset
+ (WorkQueue.class.getDeclaredField("qlock"));
+ ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
+ int scale = U.arrayIndexScale(ForkJoinTask[].class);
+ if ((scale & (scale - 1)) != 0)
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
@@ -1189,7 +1375,7 @@
/**
* Permission required for callers of methods that may start or
- * kill threads.
+ * kill threads. Also used as a static lock in tryInitialize.
*/
static final RuntimePermission modifyThreadPermission;
@@ -1230,15 +1416,18 @@
// static configuration constants
/**
- * Default idle timeout value (in milliseconds) for the thread
- * triggering quiescence to park waiting for new work
+ * Initial timeout value (in milliseconds) for the thread
+ * triggering quiescence to park waiting for new work. On timeout,
+ * the thread will instead try to shrink the number of workers.
+ * The value should be large enough to avoid overly aggressive
+ * shrinkage during most transient stalls (long GCs etc).
*/
- private static final long DEFAULT_KEEPALIVE = 60_000L;
+ private static final long IDLE_TIMEOUT_MS = 2000L; // 2sec
/**
- * Undershoot tolerance for idle timeouts
+ * Tolerance for idle timeouts, to cope with timer undershoots.
*/
- private static final long TIMEOUT_SLOP = 20L;
+ private static final long TIMEOUT_SLOP_MS = 20L; // 20ms
/**
* The default value for COMMON_MAX_SPARES. Overridable using the
@@ -1258,7 +1447,7 @@
/*
* Bits and masks for field ctl, packed with 4 16 bit subfields:
- * RC: Number of released (unqueued) workers minus target parallelism
+ * AC: Number of active running workers minus target parallelism
* TC: Number of total workers minus target parallelism
* SS: version count and status of top waiting thread
* ID: poolIndex of top of Treiber stack of waiters
@@ -1267,30 +1456,26 @@
* (including version bits) as sp=(int)ctl. The offsets of counts
* by the target parallelism and the positionings of fields makes
* it possible to perform the most common checks via sign tests of
- * fields: When ac is negative, there are not enough unqueued
+ * fields: When ac is negative, there are not enough active
* workers, when tc is negative, there are not enough total
* workers. When sp is non-zero, there are waiting workers. To
* deal with possibly negative fields, we use casts in and out of
* "short" and/or signed shifts to maintain signedness.
*
- * Because it occupies uppermost bits, we can add one release count
- * using getAndAddLong of RC_UNIT, rather than CAS, when returning
+ * Because it occupies uppermost bits, we can add one active count
+ * using getAndAddLong of AC_UNIT, rather than CAS, when returning
* from a blocked join. Other updates entail multiple subfields
* and masking, requiring CAS.
- *
- * The limits packed in field "bounds" are also offset by the
- * parallelism level to make them comparable to the ctl rc and tc
- * fields.
*/
// Lower and upper word masks
private static final long SP_MASK = 0xffffffffL;
private static final long UC_MASK = ~SP_MASK;
- // Release counts
- private static final int RC_SHIFT = 48;
- private static final long RC_UNIT = 0x0001L << RC_SHIFT;
- private static final long RC_MASK = 0xffffL << RC_SHIFT;
+ // Active counts
+ private static final int AC_SHIFT = 48;
+ private static final long AC_UNIT = 0x0001L << AC_SHIFT;
+ private static final long AC_MASK = 0xffffL << AC_SHIFT;
// Total counts
private static final int TC_SHIFT = 32;
@@ -1298,22 +1483,52 @@
private static final long TC_MASK = 0xffffL << TC_SHIFT;
private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign
- // Instance fields
+ // runState bits: SHUTDOWN must be negative, others arbitrary powers of two
+ private static final int STARTED = 1;
+ private static final int STOP = 1 << 1;
+ private static final int TERMINATED = 1 << 2;
+ private static final int SHUTDOWN = 1 << 31;
- volatile long stealCount; // collects worker nsteals
- final long keepAlive; // milliseconds before dropping if idle
- int indexSeed; // next worker index
- final int bounds; // min, max threads packed as shorts
- volatile int mode; // parallelism, runstate, queue mode
- WorkQueue[] workQueues; // main registry
- final String workerNamePrefix; // for worker thread string; sync lock
+ // Instance fields
+ volatile long ctl; // main pool control
+ volatile int runState;
+ final int config; // parallelism, mode
+ AuxState auxState; // lock, steal counts
+ volatile WorkQueue[] workQueues; // main registry
+ final String workerNamePrefix; // to create worker name string
final ForkJoinWorkerThreadFactory factory;
final UncaughtExceptionHandler ueh; // per-worker UEH
- final Predicate<? super ForkJoinPool> saturate;
- // Android-removed: @Contended, this hint is not used by the Android runtime.
- // @jdk.internal.vm.annotation.Contended("fjpctl") // segregate
- volatile long ctl; // main pool control
+ /**
+ * Instantiates fields upon first submission, or upon shutdown if
+ * no submissions. If checkTermination true, also responds to
+ * termination by external calls submitting tasks.
+ */
+ private void tryInitialize(boolean checkTermination) {
+ if (runState == 0) { // bootstrap by locking static field
+ int p = config & SMASK;
+ int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
+ n |= n >>> 1; // create workQueues array with size a power of two
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ n = ((n + 1) << 1) & SMASK;
+ AuxState aux = new AuxState();
+ WorkQueue[] ws = new WorkQueue[n];
+ synchronized (modifyThreadPermission) { // double-check
+ if (runState == 0) {
+ workQueues = ws;
+ auxState = aux;
+ runState = STARTED;
+ }
+ }
+ }
+ if (checkTermination && runState < 0) {
+ tryTerminate(false, false); // help terminate
+ throw new RejectedExecutionException();
+ }
+ }
// Creating, registering and deregistering workers
@@ -1322,14 +1537,18 @@
* count has already been incremented as a reservation. Invokes
* deregisterWorker on any failure.
*
+ * @param isSpare true if this is a spare thread
* @return true if successful
*/
- private boolean createWorker() {
+ private boolean createWorker(boolean isSpare) {
ForkJoinWorkerThreadFactory fac = factory;
Throwable ex = null;
ForkJoinWorkerThread wt = null;
+ WorkQueue q;
try {
if (fac != null && (wt = fac.newThread(this)) != null) {
+ if (isSpare && (q = wt.workQueue) != null)
+ q.config |= SPARE_WORKER;
wt.start();
return true;
}
@@ -1350,10 +1569,10 @@
*/
private void tryAddWorker(long c) {
do {
- long nc = ((RC_MASK & (c + RC_UNIT)) |
+ long nc = ((AC_MASK & (c + AC_UNIT)) |
(TC_MASK & (c + TC_UNIT)));
- if (ctl == c && CTL.compareAndSet(this, c, nc)) {
- createWorker();
+ if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) {
+ createWorker(false);
break;
}
} while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);
@@ -1368,55 +1587,41 @@
*/
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
UncaughtExceptionHandler handler;
- wt.setDaemon(true); // configure thread
+ AuxState aux;
+ wt.setDaemon(true); // configure thread
if ((handler = ueh) != null)
wt.setUncaughtExceptionHandler(handler);
- int tid = 0; // for thread name
- int idbits = mode & FIFO;
- String prefix = workerNamePrefix;
WorkQueue w = new WorkQueue(this, wt);
- if (prefix != null) {
- synchronized (prefix) {
- WorkQueue[] ws = workQueues; int n;
- int s = indexSeed += SEED_INCREMENT;
- idbits |= (s & ~(SMASK | FIFO | DORMANT));
- if (ws != null && (n = ws.length) > 1) {
- int m = n - 1;
- tid = m & ((s << 1) | 1); // odd-numbered indices
- for (int probes = n >>> 1;;) { // find empty slot
- WorkQueue q;
- if ((q = ws[tid]) == null || q.phase == QUIET)
- break;
- else if (--probes == 0) {
- tid = n | 1; // resize below
- break;
+ int i = 0; // assign a pool index
+ int mode = config & MODE_MASK;
+ if ((aux = auxState) != null) {
+ aux.lock();
+ try {
+ int s = (int)(aux.indexSeed += SEED_INCREMENT), n, m;
+ WorkQueue[] ws = workQueues;
+ if (ws != null && (n = ws.length) > 0) {
+ i = (m = n - 1) & ((s << 1) | 1); // odd-numbered indices
+ if (ws[i] != null) { // collision
+ int probes = 0; // step by approx half n
+ int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
+ while (ws[i = (i + step) & m] != null) {
+ if (++probes >= n) {
+ workQueues = ws = Arrays.copyOf(ws, n <<= 1);
+ m = n - 1;
+ probes = 0;
+ }
}
- else
- tid = (tid + 2) & m;
}
- w.phase = w.id = tid | idbits; // now publishable
-
- if (tid < n)
- ws[tid] = w;
- else { // expand array
- int an = n << 1;
- WorkQueue[] as = new WorkQueue[an];
- as[tid] = w;
- int am = an - 1;
- for (int j = 0; j < n; ++j) {
- WorkQueue v; // copy external queue
- if ((v = ws[j]) != null) // position may change
- as[v.id & am & SQMASK] = v;
- if (++j >= n)
- break;
- as[j] = ws[j]; // copy worker
- }
- workQueues = as;
- }
+ w.hint = s; // use as random seed
+ w.config = i | mode;
+ w.scanState = i | (s & 0x7fff0000); // random seq bits
+ ws[i] = w;
}
+ } finally {
+ aux.unlock();
}
- wt.setName(prefix.concat(Integer.toString(tid)));
}
+ wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
return w;
}
@@ -1431,48 +1636,64 @@
*/
final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
WorkQueue w = null;
- int phase = 0;
if (wt != null && (w = wt.workQueue) != null) {
- Object lock = workerNamePrefix;
- int wid = w.id;
- long ns = (long)w.nsteals & 0xffffffffL;
- if (lock != null) {
- synchronized (lock) {
- WorkQueue[] ws; int n, i; // remove index from array
- if ((ws = workQueues) != null && (n = ws.length) > 0 &&
- ws[i = wid & (n - 1)] == w)
- ws[i] = null;
- stealCount += ns;
+ AuxState aux; WorkQueue[] ws; // remove index from array
+ int idx = w.config & SMASK;
+ int ns = w.nsteals;
+ if ((aux = auxState) != null) {
+ aux.lock();
+ try {
+ if ((ws = workQueues) != null && ws.length > idx &&
+ ws[idx] == w)
+ ws[idx] = null;
+ aux.stealCount += ns;
+ } finally {
+ aux.unlock();
}
}
- phase = w.phase;
}
- if (phase != QUIET) { // else pre-adjusted
+ if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted
long c; // decrement counts
- do {} while (!CTL.weakCompareAndSet
- (this, c = ctl, ((RC_MASK & (c - RC_UNIT)) |
- (TC_MASK & (c - TC_UNIT)) |
- (SP_MASK & c))));
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |
+ (TC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & c))));
}
- if (w != null)
+ if (w != null) {
+ w.currentSteal = null;
+ w.qlock = -1; // ensure set
w.cancelAll(); // cancel remaining tasks
-
- if (!tryTerminate(false, false) && // possibly replace worker
- w != null && w.array != null) // avoid repeated failures
- signalWork();
-
+ }
+ while (tryTerminate(false, false) >= 0) { // possibly replace
+ WorkQueue[] ws; int wl, sp; long c;
+ if (w == null || w.array == null ||
+ (ws = workQueues) == null || (wl = ws.length) <= 0)
+ break;
+ else if ((sp = (int)(c = ctl)) != 0) { // wake up replacement
+ if (tryRelease(c, ws[(wl - 1) & sp], AC_UNIT))
+ break;
+ }
+ else if (ex != null && (c & ADD_WORKER) != 0L) {
+ tryAddWorker(c); // create replacement
+ break;
+ }
+ else // don't need replacement
+ break;
+ }
if (ex == null) // help clean on way out
ForkJoinTask.helpExpungeStaleExceptions();
else // rethrow
ForkJoinTask.rethrow(ex);
}
+ // Signalling
+
/**
- * Tries to create or release a worker if too few are running.
+ * Tries to create or activate a worker if too few are active.
*/
final void signalWork() {
for (;;) {
- long c; int sp; WorkQueue[] ws; int i; WorkQueue v;
+ long c; int sp, i; WorkQueue v; WorkQueue[] ws;
if ((c = ctl) >= 0L) // enough workers
break;
else if ((sp = (int)c) == 0) { // no idle workers
@@ -1487,14 +1708,12 @@
else if ((v = ws[i]) == null)
break; // terminating
else {
- int np = sp & ~UNSIGNALLED;
- int vp = v.phase;
- long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + RC_UNIT));
- Thread vt = v.owner;
- if (sp == vp && CTL.compareAndSet(this, c, nc)) {
- v.phase = np;
- if (vt != null && v.source < 0)
- LockSupport.unpark(vt);
+ int ns = sp & ~UNSIGNALLED;
+ int vs = v.scanState;
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
+ if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
break;
}
}
@@ -1502,181 +1721,502 @@
}
/**
- * Tries to decrement counts (sometimes implicitly) and possibly
- * arrange for a compensating worker in preparation for blocking:
- * If not all core workers yet exist, creates one, else if any are
- * unreleased (possibly including caller) releases one, else if
- * fewer than the minimum allowed number of workers running,
- * checks to see that they are all active, and if so creates an
- * extra worker unless over maximum limit and policy is to
- * saturate. Most of these steps can fail due to interference, in
- * which case 0 is returned so caller will retry. A negative
- * return value indicates that the caller doesn't need to
- * re-adjust counts when later unblocked.
+ * Signals and releases worker v if it is top of idle worker
+ * stack. This performs a one-shot version of signalWork only if
+ * there is (apparently) at least one idle worker.
*
- * @return 1: block then adjust, -1: block without adjust, 0 : retry
+ * @param c incoming ctl value
+ * @param v if non-null, a worker
+ * @param inc the increment to active count (zero when compensating)
+ * @return true if successful
*/
- private int tryCompensate(WorkQueue w) {
- int t, n, sp;
- long c = ctl;
- WorkQueue[] ws = workQueues;
- if ((t = (short)(c >>> TC_SHIFT)) >= 0) {
- if (ws == null || (n = ws.length) <= 0 || w == null)
- return 0; // disabled
- else if ((sp = (int)c) != 0) { // replace or release
- WorkQueue v = ws[sp & (n - 1)];
- int wp = w.phase;
- long uc = UC_MASK & ((wp < 0) ? c + RC_UNIT : c);
- int np = sp & ~UNSIGNALLED;
- if (v != null) {
- int vp = v.phase;
- Thread vt = v.owner;
- long nc = ((long)v.stackPred & SP_MASK) | uc;
- if (vp == sp && CTL.compareAndSet(this, c, nc)) {
- v.phase = np;
- if (vt != null && v.source < 0)
- LockSupport.unpark(vt);
- return (wp < 0) ? -1 : 1;
- }
- }
- return 0;
- }
- else if ((int)(c >> RC_SHIFT) - // reduce parallelism
- (short)(bounds & SMASK) > 0) {
- long nc = ((RC_MASK & (c - RC_UNIT)) | (~RC_MASK & c));
- return CTL.compareAndSet(this, c, nc) ? 1 : 0;
- }
- else { // validate
- int md = mode, pc = md & SMASK, tc = pc + t, bc = 0;
- boolean unstable = false;
- for (int i = 1; i < n; i += 2) {
- WorkQueue q; Thread wt; Thread.State ts;
- if ((q = ws[i]) != null) {
- if (q.source == 0) {
- unstable = true;
- break;
- }
- else {
- --tc;
- if ((wt = q.owner) != null &&
- ((ts = wt.getState()) == Thread.State.BLOCKED ||
- ts == Thread.State.WAITING))
- ++bc; // worker is blocking
- }
- }
- }
- if (unstable || tc != 0 || ctl != c)
- return 0; // inconsistent
- else if (t + pc >= MAX_CAP || t >= (bounds >>> SWIDTH)) {
- Predicate<? super ForkJoinPool> sat;
- if ((sat = saturate) != null && sat.test(this))
- return -1;
- else if (bc < pc) { // lagging
- Thread.yield(); // for retry spins
- return 0;
- }
- else
- throw new RejectedExecutionException(
- "Thread limit exceeded replacing blocked worker");
- }
- }
- }
-
- long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK); // expand pool
- return CTL.compareAndSet(this, c, nc) && createWorker() ? 1 : 0;
- }
-
- /**
- * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
- * See above for explanation.
- */
- final void runWorker(WorkQueue w) {
- int r = (w.id ^ ThreadLocalRandom.nextSecondarySeed()) | FIFO; // rng
- w.array = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY]; // initialize
- for (;;) {
- int phase;
- if (scan(w, r)) { // scan until apparently empty
- r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // move (xorshift)
- }
- else if ((phase = w.phase) >= 0) { // enqueue, then rescan
- long np = (w.phase = (phase + SS_SEQ) | UNSIGNALLED) & SP_MASK;
- long c, nc;
- do {
- w.stackPred = (int)(c = ctl);
- nc = ((c - RC_UNIT) & UC_MASK) | np;
- } while (!CTL.weakCompareAndSet(this, c, nc));
- }
- else { // already queued
- int pred = w.stackPred;
- Thread.interrupted(); // clear before park
- w.source = DORMANT; // enable signal
- long c = ctl;
- int md = mode, rc = (md & SMASK) + (int)(c >> RC_SHIFT);
- if (md < 0) // terminating
- break;
- else if (rc <= 0 && (md & SHUTDOWN) != 0 &&
- tryTerminate(false, false))
- break; // quiescent shutdown
- else if (rc <= 0 && pred != 0 && phase == (int)c) {
- long nc = (UC_MASK & (c - TC_UNIT)) | (SP_MASK & pred);
- long d = keepAlive + System.currentTimeMillis();
- LockSupport.parkUntil(this, d);
- if (ctl == c && // drop on timeout if all idle
- d - System.currentTimeMillis() <= TIMEOUT_SLOP &&
- CTL.compareAndSet(this, c, nc)) {
- w.phase = QUIET;
- break;
- }
- }
- else if (w.phase < 0)
- LockSupport.park(this); // OK if spuriously woken
- w.source = 0; // disable signal
- }
- }
- }
-
- /**
- * Scans for and if found executes one or more top-level tasks from a queue.
- *
- * @return true if found an apparently non-empty queue, and
- * possibly ran task(s).
- */
- private boolean scan(WorkQueue w, int r) {
- WorkQueue[] ws; int n;
- if ((ws = workQueues) != null && (n = ws.length) > 0 && w != null) {
- for (int m = n - 1, j = r & m;;) {
- WorkQueue q; int b;
- if ((q = ws[j]) != null && q.top != (b = q.base)) {
- int qid = q.id;
- ForkJoinTask<?>[] a; int cap, k; ForkJoinTask<?> t;
- if ((a = q.array) != null && (cap = a.length) > 0) {
- t = (ForkJoinTask<?>)QA.getAcquire(a, k = (cap - 1) & b);
- if (q.base == b++ && t != null &&
- QA.compareAndSet(a, k, t, null)) {
- q.base = b;
- w.source = qid;
- if (q.top - b > 0)
- signalWork();
- w.topLevelExec(t, q, // random fairness bound
- r & ((n << TOP_BOUND_SHIFT) - 1));
- }
- }
- return true;
- }
- else if (--n > 0)
- j = (j + 1) & m;
- else
- break;
+ private boolean tryRelease(long c, WorkQueue v, long inc) {
+ int sp = (int)c, ns = sp & ~UNSIGNALLED;
+ if (v != null) {
+ int vs = v.scanState;
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + inc));
+ if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
+ return true;
}
}
return false;
}
/**
+ * With approx probability of a missed signal, tries (once) to
+ * reactivate worker w (or some other worker), failing if stale or
+ * known to be already active.
+ *
+ * @param w the worker
+ * @param ws the workQueue array to use
+ * @param r random seed
+ */
+ private void tryReactivate(WorkQueue w, WorkQueue[] ws, int r) {
+ long c; int sp, wl; WorkQueue v;
+ if ((sp = (int)(c = ctl)) != 0 && w != null &&
+ ws != null && (wl = ws.length) > 0 &&
+ ((sp ^ r) & SS_SEQ) == 0 &&
+ (v = ws[(wl - 1) & sp]) != null) {
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
+ int ns = sp & ~UNSIGNALLED;
+ if (w.scanState < 0 &&
+ v.scanState == sp &&
+ U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
+ }
+ }
+ }
+
+ /**
+ * If worker w exists and is active, enqueues and sets status to inactive.
+ *
+ * @param w the worker
+ * @param ss current (non-negative) scanState
+ */
+ private void inactivate(WorkQueue w, int ss) {
+ int ns = (ss + SS_SEQ) | UNSIGNALLED;
+ long lc = ns & SP_MASK, nc, c;
+ if (w != null) {
+ w.scanState = ns;
+ do {
+ nc = lc | (UC_MASK & ((c = ctl) - AC_UNIT));
+ w.stackPred = (int)c;
+ } while (!U.compareAndSwapLong(this, CTL, c, nc));
+ }
+ }
+
+ /**
+ * Possibly blocks worker w waiting for signal, or returns
+ * negative status if the worker should terminate. May return
+ * without status change if multiple stale unparks and/or
+ * interrupts occur.
+ *
+ * @param w the calling worker
+ * @return negative if w should terminate
+ */
+ private int awaitWork(WorkQueue w) {
+ int stat = 0;
+ if (w != null && w.scanState < 0) {
+ long c = ctl;
+ if ((int)(c >> AC_SHIFT) + (config & SMASK) <= 0)
+ stat = timedAwaitWork(w, c); // possibly quiescent
+ else if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // pool terminating
+ else if (w.scanState < 0) {
+ w.parker = Thread.currentThread();
+ if (w.scanState < 0) // recheck after write
+ LockSupport.park(this);
+ w.parker = null;
+ if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // recheck
+ else if (w.scanState < 0)
+ Thread.interrupted(); // clear status
+ }
+ }
+ return stat;
+ }
+
+ /**
+ * Possibly triggers shutdown and tries (once) to block worker
+ * when pool is (or may be) quiescent. Waits up to a duration
+ * determined by number of workers. On timeout, if ctl has not
+ * changed, terminates the worker, which will in turn wake up
+ * another worker to possibly repeat this process.
+ *
+ * @param w the calling worker
+ * @return negative if w should terminate
+ */
+ private int timedAwaitWork(WorkQueue w, long c) {
+ int stat = 0;
+ int scale = 1 - (short)(c >>> TC_SHIFT);
+ long deadline = (((scale <= 0) ? 1 : scale) * IDLE_TIMEOUT_MS +
+ System.currentTimeMillis());
+ if ((runState >= 0 || (stat = tryTerminate(false, false)) > 0) &&
+ w != null && w.scanState < 0) {
+ int ss; AuxState aux;
+ w.parker = Thread.currentThread();
+ if (w.scanState < 0)
+ LockSupport.parkUntil(this, deadline);
+ w.parker = null;
+ if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // pool terminating
+ else if ((ss = w.scanState) < 0 && !Thread.interrupted() &&
+ (int)c == ss && (aux = auxState) != null && ctl == c &&
+ deadline - System.currentTimeMillis() <= TIMEOUT_SLOP_MS) {
+ aux.lock();
+ try { // pre-deregister
+ WorkQueue[] ws;
+ int cfg = w.config, idx = cfg & SMASK;
+ long nc = ((UC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & w.stackPred));
+ if ((runState & STOP) == 0 &&
+ (ws = workQueues) != null &&
+ idx < ws.length && idx >= 0 && ws[idx] == w &&
+ U.compareAndSwapLong(this, CTL, c, nc)) {
+ ws[idx] = null;
+ w.config = cfg | UNREGISTERED;
+ stat = w.qlock = -1;
+ }
+ } finally {
+ aux.unlock();
+ }
+ }
+ }
+ return stat;
+ }
+
+ /**
+ * If the given worker is a spare with no queued tasks, and there
+ * are enough existing workers, drops it from ctl counts and sets
+ * its state to terminated.
+ *
+ * @param w the calling worker -- must be a spare
+ * @return true if dropped (in which case it must not process more tasks)
+ */
+ private boolean tryDropSpare(WorkQueue w) {
+ if (w != null && w.isEmpty()) { // no local tasks
+ long c; int sp, wl; WorkQueue[] ws; WorkQueue v;
+ while ((short)((c = ctl) >> TC_SHIFT) > 0 &&
+ ((sp = (int)c) != 0 || (int)(c >> AC_SHIFT) > 0) &&
+ (ws = workQueues) != null && (wl = ws.length) > 0) {
+ boolean dropped, canDrop;
+ if (sp == 0) { // no queued workers
+ long nc = ((AC_MASK & (c - AC_UNIT)) |
+ (TC_MASK & (c - TC_UNIT)) | (SP_MASK & c));
+ dropped = U.compareAndSwapLong(this, CTL, c, nc);
+ }
+ else if (
+ (v = ws[(wl - 1) & sp]) == null || v.scanState != sp)
+ dropped = false; // stale; retry
+ else {
+ long nc = v.stackPred & SP_MASK;
+ if (w == v || w.scanState >= 0) {
+ canDrop = true; // w unqueued or topmost
+ nc |= ((AC_MASK & c) | // ensure replacement
+ (TC_MASK & (c - TC_UNIT)));
+ }
+ else { // w may be queued
+ canDrop = false; // help uncover
+ nc |= ((AC_MASK & (c + AC_UNIT)) |
+ (TC_MASK & c));
+ }
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = sp & ~UNSIGNALLED;
+ LockSupport.unpark(v.parker);
+ dropped = canDrop;
+ }
+ else
+ dropped = false;
+ }
+ if (dropped) { // pre-deregister
+ int cfg = w.config, idx = cfg & SMASK;
+ if (idx >= 0 && idx < ws.length && ws[idx] == w)
+ ws[idx] = null;
+ w.config = cfg | UNREGISTERED;
+ w.qlock = -1;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
+ */
+ final void runWorker(WorkQueue w) {
+ w.growArray(); // allocate queue
+ int bound = (w.config & SPARE_WORKER) != 0 ? 0 : POLL_LIMIT;
+ long seed = w.hint * 0xdaba0b6eb09322e3L; // initial random seed
+ if ((runState & STOP) == 0) {
+ for (long r = (seed == 0L) ? 1L : seed;;) { // ensure nonzero
+ if (bound == 0 && tryDropSpare(w))
+ break;
+ // high bits of prev seed for step; current low bits for idx
+ int step = (int)(r >>> 48) | 1;
+ r ^= r >>> 12; r ^= r << 25; r ^= r >>> 27; // xorshift
+ if (scan(w, bound, step, (int)r) < 0 && awaitWork(w) < 0)
+ break;
+ }
+ }
+ }
+
+ // Scanning for tasks
+
+ /**
+ * Repeatedly scans for and tries to steal and execute (via
+ * workQueue.runTask) a queued task. Each scan traverses queues in
+ * pseudorandom permutation. Upon finding a non-empty queue, makes
+ * at most the given bound attempts to re-poll (fewer if
+ * contended) on the same queue before returning (impossible
+ * scanState value) 0 to restart scan. Else returns after at least
+ * 1 and at most 32 full scans.
+ *
+ * @param w the worker (via its WorkQueue)
+ * @param bound repoll bound as bitmask (0 if spare)
+ * @param step (circular) index increment per iteration (must be odd)
+ * @param r a random seed for origin index
+ * @return negative if should await signal
+ */
+ private int scan(WorkQueue w, int bound, int step, int r) {
+ int stat = 0, wl; WorkQueue[] ws;
+ if ((ws = workQueues) != null && w != null && (wl = ws.length) > 0) {
+ for (int m = wl - 1,
+ origin = m & r, idx = origin,
+ npolls = 0,
+ ss = w.scanState;;) { // negative if inactive
+ WorkQueue q; ForkJoinTask<?>[] a; int b, al;
+ if ((q = ws[idx]) != null && (b = q.base) - q.top < 0 &&
+ (a = q.array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t == null)
+ break; // empty or busy
+ else if (b++ != q.base)
+ break; // busy
+ else if (ss < 0) {
+ tryReactivate(w, ws, r);
+ break; // retry upon rescan
+ }
+ else if (!U.compareAndSwapObject(a, offset, t, null))
+ break; // contended
+ else {
+ q.base = b;
+ w.currentSteal = t;
+ if (b != q.top) // propagate signal
+ signalWork();
+ w.runTask(t);
+ if (++npolls > bound)
+ break;
+ }
+ }
+ else if (npolls != 0) // rescan
+ break;
+ else if ((idx = (idx + step) & m) == origin) {
+ if (ss < 0) { // await signal
+ stat = ss;
+ break;
+ }
+ else if (r >= 0) {
+ inactivate(w, ss);
+ break;
+ }
+ else
+ r <<= 1; // at most 31 rescans
+ }
+ }
+ }
+ return stat;
+ }
+
+ // Joining tasks
+
+ /**
+ * Tries to steal and run tasks within the target's computation.
+ * Uses a variant of the top-level algorithm, restricted to tasks
+ * with the given task as ancestor: It prefers taking and running
+ * eligible tasks popped from the worker's own queue (via
+ * popCC). Otherwise it scans others, randomly moving on
+ * contention or execution, deciding to give up based on a
+ * checksum (via return codes from pollAndExecCC). The maxTasks
+ * argument supports external usages; internal calls use zero,
+ * allowing unbounded steps (external calls trap non-positive
+ * values).
+ *
+ * @param w caller
+ * @param maxTasks if non-zero, the maximum number of other tasks to run
+ * @return task status on exit
+ */
+ final int helpComplete(WorkQueue w, CountedCompleter<?> task,
+ int maxTasks) {
+ WorkQueue[] ws; int s = 0, wl;
+ if ((ws = workQueues) != null && (wl = ws.length) > 1 &&
+ task != null && w != null) {
+ for (int m = wl - 1,
+ mode = w.config,
+ r = ~mode, // scanning seed
+ origin = r & m, k = origin, // first queue to scan
+ step = 3, // first scan step
+ h = 1, // 1:ran, >1:contended, <0:hash
+ oldSum = 0, checkSum = 0;;) {
+ CountedCompleter<?> p; WorkQueue q; int i;
+ if ((s = task.status) < 0)
+ break;
+ if (h == 1 && (p = w.popCC(task, mode)) != null) {
+ p.doExec(); // run local task
+ if (maxTasks != 0 && --maxTasks == 0)
+ break;
+ origin = k; // reset
+ oldSum = checkSum = 0;
+ }
+ else { // poll other worker queues
+ if ((i = k | 1) < 0 || i > m || (q = ws[i]) == null)
+ h = 0;
+ else if ((h = q.pollAndExecCC(task)) < 0)
+ checkSum += h;
+ if (h > 0) {
+ if (h == 1 && maxTasks != 0 && --maxTasks == 0)
+ break;
+ step = (r >>> 16) | 3;
+ r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
+ k = origin = r & m; // move and restart
+ oldSum = checkSum = 0;
+ }
+ else if ((k = (k + step) & m) == origin) {
+ if (oldSum == (oldSum = checkSum))
+ break;
+ checkSum = 0;
+ }
+ }
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers. Traces currentSteal ->
+ * currentJoin links looking for a thread working on a descendant
+ * of the given task and with a non-empty queue to steal back and
+ * execute tasks from. The first call to this method upon a
+ * waiting join will often entail scanning/search, (which is OK
+ * because the joiner has nothing better to do), but this method
+ * leaves hints in workers to speed up subsequent calls.
+ *
+ * @param w caller
+ * @param task the task to join
+ */
+ private void helpStealer(WorkQueue w, ForkJoinTask<?> task) {
+ if (task != null && w != null) {
+ ForkJoinTask<?> ps = w.currentSteal;
+ WorkQueue[] ws; int wl, oldSum = 0;
+ outer: while (w.tryRemoveAndExec(task) && task.status >= 0 &&
+ (ws = workQueues) != null && (wl = ws.length) > 0) {
+ ForkJoinTask<?> subtask;
+ int m = wl - 1, checkSum = 0; // for stability check
+ WorkQueue j = w, v; // v is subtask stealer
+ descent: for (subtask = task; subtask.status >= 0; ) {
+ for (int h = j.hint | 1, k = 0, i;;) {
+ if ((v = ws[i = (h + (k << 1)) & m]) != null) {
+ if (v.currentSteal == subtask) {
+ j.hint = i;
+ break;
+ }
+ checkSum += v.base;
+ }
+ if (++k > m) // can't find stealer
+ break outer;
+ }
+
+ for (;;) { // help v or descend
+ ForkJoinTask<?>[] a; int b, al;
+ if (subtask.status < 0) // too late to help
+ break descent;
+ checkSum += (b = v.base);
+ ForkJoinTask<?> next = v.currentJoin;
+ ForkJoinTask<?> t = null;
+ if ((a = v.array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t != null && b++ == v.base) {
+ if (j.currentJoin != subtask ||
+ v.currentSteal != subtask ||
+ subtask.status < 0)
+ break descent; // stale
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ v.base = b;
+ w.currentSteal = t;
+ for (int top = w.top;;) {
+ t.doExec(); // help
+ w.currentSteal = ps;
+ if (task.status < 0)
+ break outer;
+ if (w.top == top)
+ break; // run local tasks
+ if ((t = w.pop()) == null)
+ break descent;
+ w.currentSteal = t;
+ }
+ }
+ }
+ }
+ if (t == null && b == v.base && b - v.top >= 0) {
+ if ((subtask = next) == null) { // try to descend
+ if (next == v.currentJoin &&
+ oldSum == (oldSum = checkSum))
+ break outer;
+ break descent;
+ }
+ j = v;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to decrement active count (sometimes implicitly) and
+ * possibly release or create a compensating worker in preparation
+ * for blocking. Returns false (retryable by caller), on
+ * contention, detected staleness, instability, or termination.
+ *
+ * @param w caller
+ */
+ private boolean tryCompensate(WorkQueue w) {
+ boolean canBlock; int wl;
+ long c = ctl;
+ WorkQueue[] ws = workQueues;
+ int pc = config & SMASK;
+ int ac = pc + (int)(c >> AC_SHIFT);
+ int tc = pc + (short)(c >> TC_SHIFT);
+ if (w == null || w.qlock < 0 || pc == 0 || // terminating or disabled
+ ws == null || (wl = ws.length) <= 0)
+ canBlock = false;
+ else {
+ int m = wl - 1, sp;
+ boolean busy = true; // validate ac
+ for (int i = 0; i <= m; ++i) {
+ int k; WorkQueue v;
+ if ((k = (i << 1) | 1) <= m && k >= 0 && (v = ws[k]) != null &&
+ v.scanState >= 0 && v.currentSteal == null) {
+ busy = false;
+ break;
+ }
+ }
+ if (!busy || ctl != c)
+ canBlock = false; // unstable or stale
+ else if ((sp = (int)c) != 0) // release idle worker
+ canBlock = tryRelease(c, ws[m & sp], 0L);
+ else if (tc >= pc && ac > 1 && w.isEmpty()) {
+ long nc = ((AC_MASK & (c - AC_UNIT)) |
+ (~AC_MASK & c)); // uncompensated
+ canBlock = U.compareAndSwapLong(this, CTL, c, nc);
+ }
+ else if (tc >= MAX_CAP ||
+ (this == common && tc >= pc + COMMON_MAX_SPARES))
+ throw new RejectedExecutionException(
+ "Thread limit exceeded replacing blocked worker");
+ else { // similar to tryAddWorker
+ boolean isSpare = (tc >= pc);
+ long nc = (AC_MASK & c) | (TC_MASK & (c + TC_UNIT));
+ canBlock = (U.compareAndSwapLong(this, CTL, c, nc) &&
+ createWorker(isSpare)); // throws on exception
+ }
+ }
+ return canBlock;
+ }
+
+ /**
* Helps and/or blocks until the given task is done or timeout.
- * First tries locally helping, then scans other queues for a task
- * produced by one of w's stealers; compensating and blocking if
- * none are found (rescanning if tryCompensate fails).
*
* @param w caller
* @param task the task
@@ -1685,165 +2225,62 @@
*/
final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
int s = 0;
- int seed = ThreadLocalRandom.nextSecondarySeed();
- if (w != null && task != null &&
- (!(task instanceof CountedCompleter) ||
- (s = w.helpCC((CountedCompleter<?>)task, 0, false)) >= 0)) {
- w.tryRemoveAndExec(task);
- int src = w.source, id = w.id;
- int r = (seed >>> 16) | 1, step = (seed & ~1) | 2;
- s = task.status;
- while (s >= 0) {
- WorkQueue[] ws;
- int n = (ws = workQueues) == null ? 0 : ws.length, m = n - 1;
- while (n > 0) {
- WorkQueue q; int b;
- if ((q = ws[r & m]) != null && q.source == id &&
- q.top != (b = q.base)) {
- ForkJoinTask<?>[] a; int cap, k;
- int qid = q.id;
- if ((a = q.array) != null && (cap = a.length) > 0) {
- ForkJoinTask<?> t = (ForkJoinTask<?>)
- QA.getAcquire(a, k = (cap - 1) & b);
- if (q.source == id && q.base == b++ &&
- t != null && QA.compareAndSet(a, k, t, null)) {
- q.base = b;
- w.source = qid;
- t.doExec();
- w.source = src;
- }
- }
+ if (w != null) {
+ ForkJoinTask<?> prevJoin = w.currentJoin;
+ if (task != null && (s = task.status) >= 0) {
+ w.currentJoin = task;
+ CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
+ (CountedCompleter<?>)task : null;
+ for (;;) {
+ if (cc != null)
+ helpComplete(w, cc, 0);
+ else
+ helpStealer(w, task);
+ if ((s = task.status) < 0)
break;
- }
- else {
- r += step;
- --n;
- }
- }
- if ((s = task.status) < 0)
- break;
- else if (n == 0) { // empty scan
- long ms, ns; int block;
+ long ms, ns;
if (deadline == 0L)
- ms = 0L; // untimed
+ ms = 0L;
else if ((ns = deadline - System.nanoTime()) <= 0L)
- break; // timeout
+ break;
else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
- ms = 1L; // avoid 0 for timed wait
- if ((block = tryCompensate(w)) != 0) {
+ ms = 1L;
+ if (tryCompensate(w)) {
task.internalWait(ms);
- CTL.getAndAdd(this, (block > 0) ? RC_UNIT : 0L);
+ U.getAndAddLong(this, CTL, AC_UNIT);
}
- s = task.status;
+ if ((s = task.status) < 0)
+ break;
}
+ w.currentJoin = prevJoin;
}
}
return s;
}
- /**
- * Runs tasks until {@code isQuiescent()}. Rather than blocking
- * when tasks cannot be found, rescans until all others cannot
- * find tasks either.
- */
- final void helpQuiescePool(WorkQueue w) {
- int prevSrc = w.source;
- int seed = ThreadLocalRandom.nextSecondarySeed();
- int r = seed >>> 16, step = r | 1;
- for (int source = prevSrc, released = -1;;) { // -1 until known
- ForkJoinTask<?> localTask; WorkQueue[] ws;
- while ((localTask = w.nextLocalTask()) != null)
- localTask.doExec();
- if (w.phase >= 0 && released == -1)
- released = 1;
- boolean quiet = true, empty = true;
- int n = (ws = workQueues) == null ? 0 : ws.length;
- for (int m = n - 1; n > 0; r += step, --n) {
- WorkQueue q; int b;
- if ((q = ws[r & m]) != null) {
- int qs = q.source;
- if (q.top != (b = q.base)) {
- quiet = empty = false;
- ForkJoinTask<?>[] a; int cap, k;
- int qid = q.id;
- if ((a = q.array) != null && (cap = a.length) > 0) {
- if (released == 0) { // increment
- released = 1;
- CTL.getAndAdd(this, RC_UNIT);
- }
- ForkJoinTask<?> t = (ForkJoinTask<?>)
- QA.getAcquire(a, k = (cap - 1) & b);
- if (q.base == b++ && t != null &&
- QA.compareAndSet(a, k, t, null)) {
- q.base = b;
- w.source = qid;
- t.doExec();
- w.source = source = prevSrc;
- }
- }
- break;
- }
- else if ((qs & QUIET) == 0)
- quiet = false;
- }
- }
- if (quiet) {
- if (released == 0)
- CTL.getAndAdd(this, RC_UNIT);
- w.source = prevSrc;
- break;
- }
- else if (empty) {
- if (source != QUIET)
- w.source = source = QUIET;
- if (released == 1) { // decrement
- released = 0;
- CTL.getAndAdd(this, RC_MASK & -RC_UNIT);
- }
- }
- }
- }
+ // Specialized scanning
/**
- * Scans for and returns a polled task, if available.
- * Used only for untracked polls.
- *
- * @param submissionsOnly if true, only scan submission queues
+ * Returns a (probably) non-empty steal queue, if one is found
+ * during a scan, else null. This method must be retried by
+ * caller if, by the time it tries to use the queue, it is empty.
*/
- private ForkJoinTask<?> pollScan(boolean submissionsOnly) {
- WorkQueue[] ws; int n;
- rescan: while ((mode & STOP) == 0 && (ws = workQueues) != null &&
- (n = ws.length) > 0) {
- int m = n - 1;
- int r = ThreadLocalRandom.nextSecondarySeed();
- int h = r >>> 16;
- int origin, step;
- if (submissionsOnly) {
- origin = (r & ~1) & m; // even indices and steps
- step = (h & ~1) | 2;
- }
- else {
- origin = r & m;
- step = h | 1;
- }
- boolean nonempty = false;
- for (int i = origin, oldSum = 0, checkSum = 0;;) {
- WorkQueue q;
- if ((q = ws[i]) != null) {
- int b; ForkJoinTask<?> t;
- if (q.top - (b = q.base) > 0) {
- nonempty = true;
- if ((t = q.poll()) != null)
- return t;
- }
- else
- checkSum += b + q.id;
+ private WorkQueue findNonEmptyStealQueue() {
+ WorkQueue[] ws; int wl; // one-shot version of scan loop
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ if ((ws = workQueues) != null && (wl = ws.length) > 0) {
+ int m = wl - 1, origin = r & m;
+ for (int k = origin, oldSum = 0, checkSum = 0;;) {
+ WorkQueue q; int b;
+ if ((q = ws[k]) != null) {
+ if ((b = q.base) - q.top < 0)
+ return q;
+ checkSum += b;
}
- if ((i = (i + step) & m) == origin) {
- if (!nonempty && oldSum == (oldSum = checkSum))
- break rescan;
+ if ((k = (k + 1) & m) == origin) {
+ if (oldSum == (oldSum = checkSum))
+ break;
checkSum = 0;
- nonempty = false;
}
}
}
@@ -1851,132 +2288,61 @@
}
/**
+ * Runs tasks until {@code isQuiescent()}. We piggyback on
+ * active count ctl maintenance, but rather than blocking
+ * when tasks cannot be found, we rescan until all others cannot
+ * find tasks either.
+ */
+ final void helpQuiescePool(WorkQueue w) {
+ ForkJoinTask<?> ps = w.currentSteal; // save context
+ int wc = w.config;
+ for (boolean active = true;;) {
+ long c; WorkQueue q; ForkJoinTask<?> t;
+ if (wc >= 0 && (t = w.pop()) != null) { // run locals if LIFO
+ (w.currentSteal = t).doExec();
+ w.currentSteal = ps;
+ }
+ else if ((q = findNonEmptyStealQueue()) != null) {
+ if (!active) { // re-establish active count
+ active = true;
+ U.getAndAddLong(this, CTL, AC_UNIT);
+ }
+ if ((t = q.pollAt(q.base)) != null) {
+ (w.currentSteal = t).doExec();
+ w.currentSteal = ps;
+ if (++w.nsteals < 0)
+ w.transferStealCount(this);
+ }
+ }
+ else if (active) { // decrement active count without queuing
+ long nc = (AC_MASK & ((c = ctl) - AC_UNIT)) | (~AC_MASK & c);
+ if (U.compareAndSwapLong(this, CTL, c, nc))
+ active = false;
+ }
+ else if ((int)((c = ctl) >> AC_SHIFT) + (config & SMASK) <= 0 &&
+ U.compareAndSwapLong(this, CTL, c, c + AC_UNIT))
+ break;
+ }
+ }
+
+ /**
* Gets and removes a local or stolen task for the given worker.
*
* @return a task, if available
*/
final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
- ForkJoinTask<?> t;
- if (w == null || (t = w.nextLocalTask()) == null)
- t = pollScan(false);
- return t;
- }
-
- // External operations
-
- /**
- * Adds the given task to a submission queue at submitter's
- * current queue, creating one if null or contended.
- *
- * @param task the task. Caller must ensure non-null.
- */
- final void externalPush(ForkJoinTask<?> task) {
- int r; // initialize caller's probe
- if ((r = ThreadLocalRandom.getProbe()) == 0) {
- ThreadLocalRandom.localInit();
- r = ThreadLocalRandom.getProbe();
- }
- for (;;) {
+ for (ForkJoinTask<?> t;;) {
WorkQueue q;
- int md = mode, n;
- WorkQueue[] ws = workQueues;
- if ((md & SHUTDOWN) != 0 || ws == null || (n = ws.length) <= 0)
- throw new RejectedExecutionException();
- else if ((q = ws[(n - 1) & r & SQMASK]) == null) { // add queue
- int qid = (r | QUIET) & ~(FIFO | OWNED);
- Object lock = workerNamePrefix;
- ForkJoinTask<?>[] qa =
- new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
- q = new WorkQueue(this, null);
- q.array = qa;
- q.id = qid;
- q.source = QUIET;
- if (lock != null) { // unless disabled, lock pool to install
- synchronized (lock) {
- WorkQueue[] vs; int i, vn;
- if ((vs = workQueues) != null && (vn = vs.length) > 0 &&
- vs[i = qid & (vn - 1) & SQMASK] == null)
- vs[i] = q; // else another thread already installed
- }
- }
- }
- else if (!q.tryLockPhase()) // move if busy
- r = ThreadLocalRandom.advanceProbe(r);
- else {
- if (q.lockedPush(task))
- signalWork();
- return;
- }
+ if ((t = w.nextLocalTask()) != null)
+ return t;
+ if ((q = findNonEmptyStealQueue()) == null)
+ return null;
+ if ((t = q.pollAt(q.base)) != null)
+ return t;
}
}
/**
- * Pushes a possibly-external submission.
- */
- private <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task) {
- Thread t; ForkJoinWorkerThread w; WorkQueue q;
- if (task == null)
- throw new NullPointerException();
- if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
- (w = (ForkJoinWorkerThread)t).pool == this &&
- (q = w.workQueue) != null)
- q.push(task);
- else
- externalPush(task);
- return task;
- }
-
- /**
- * Returns common pool queue for an external thread.
- */
- static WorkQueue commonSubmitterQueue() {
- ForkJoinPool p = common;
- int r = ThreadLocalRandom.getProbe();
- WorkQueue[] ws; int n;
- return (p != null && (ws = p.workQueues) != null &&
- (n = ws.length) > 0) ?
- ws[(n - 1) & r & SQMASK] : null;
- }
-
- /**
- * Performs tryUnpush for an external submitter.
- */
- final boolean tryExternalUnpush(ForkJoinTask<?> task) {
- int r = ThreadLocalRandom.getProbe();
- WorkQueue[] ws; WorkQueue w; int n;
- return ((ws = workQueues) != null &&
- (n = ws.length) > 0 &&
- (w = ws[(n - 1) & r & SQMASK]) != null &&
- w.tryLockedUnpush(task));
- }
-
- /**
- * Performs helpComplete for an external submitter.
- */
- final int externalHelpComplete(CountedCompleter<?> task, int maxTasks) {
- int r = ThreadLocalRandom.getProbe();
- WorkQueue[] ws; WorkQueue w; int n;
- return ((ws = workQueues) != null && (n = ws.length) > 0 &&
- (w = ws[(n - 1) & r & SQMASK]) != null) ?
- w.helpCC(task, maxTasks, true) : 0;
- }
-
- /**
- * Tries to steal and run tasks within the target's computation.
- * The maxTasks argument supports external usages; internal calls
- * use zero, allowing unbounded steps (external calls trap
- * non-positive values).
- *
- * @param w caller
- * @param maxTasks if non-zero, the maximum number of other tasks to run
- * @return task status on exit
- */
- final int helpComplete(WorkQueue w, CountedCompleter<?> task,
- int maxTasks) {
- return (w == null) ? 0 : w.helpCC(task, maxTasks, false);
- }
-
- /**
* Returns a cheap heuristic guide for task partitioning when
* programmers, frameworks, tools, or languages have little or no
* idea about task granularity. In essence, by offering this
@@ -2020,12 +2386,10 @@
*/
static int getSurplusQueuedTaskCount() {
Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
- if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
- (pool = (wt = (ForkJoinWorkerThread)t).pool) != null &&
- (q = wt.workQueue) != null) {
- int p = pool.mode & SMASK;
- int a = p + (int)(pool.ctl >> RC_SHIFT);
- int n = q.top - q.base;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
+ int n = (q = wt.workQueue).top - q.base;
+ int a = (int)(pool.ctl >> AC_SHIFT) + p;
return n - (a > (p >>>= 1) ? 0 :
a > (p >>>= 1) ? 1 :
a > (p >>>= 1) ? 2 :
@@ -2035,7 +2399,7 @@
return 0;
}
- // Termination
+ // Termination
/**
* Possibly initiates and/or completes termination.
@@ -2043,89 +2407,198 @@
* @param now if true, unconditionally terminate, else only
* if no work and no active workers
* @param enable if true, terminate when next possible
- * @return true if terminating or terminated
+ * @return -1: terminating/terminated, 0: retry if internal caller, else 1
*/
- private boolean tryTerminate(boolean now, boolean enable) {
- int md; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED
+ private int tryTerminate(boolean now, boolean enable) {
+ int rs; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED
- while (((md = mode) & SHUTDOWN) == 0) {
+ while ((rs = runState) >= 0) {
if (!enable || this == common) // cannot shutdown
- return false;
+ return 1;
+ else if (rs == 0)
+ tryInitialize(false); // ensure initialized
else
- MODE.compareAndSet(this, md, md | SHUTDOWN);
+ U.compareAndSwapInt(this, RUNSTATE, rs, rs | SHUTDOWN);
}
- while (((md = mode) & STOP) == 0) { // try to initiate termination
- if (!now) { // check if quiescent & empty
+ if ((rs & STOP) == 0) { // try to initiate termination
+ if (!now) { // check quiescence
for (long oldSum = 0L;;) { // repeat until stable
- boolean running = false;
+ WorkQueue[] ws; WorkQueue w; int b;
long checkSum = ctl;
- WorkQueue[] ws = workQueues;
- if ((md & SMASK) + (int)(checkSum >> RC_SHIFT) > 0)
- running = true;
- else if (ws != null) {
- WorkQueue w;
+ if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0)
+ return 0; // still active workers
+ if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; ++i) {
if ((w = ws[i]) != null) {
- int s = w.source, p = w.phase;
- int d = w.id, b = w.base;
- if (b != w.top ||
- ((d & 1) == 1 && (s >= 0 || p >= 0))) {
- running = true;
- break; // working, scanning, or have work
- }
- checkSum += (((long)s << 48) + ((long)p << 32) +
- ((long)b << 16) + (long)d);
+ checkSum += (b = w.base);
+ if (w.currentSteal != null || b != w.top)
+ return 0; // retry if internal caller
}
}
}
- if (((md = mode) & STOP) != 0)
- break; // already triggered
- else if (running)
- return false;
- else if (workQueues == ws && oldSum == (oldSum = checkSum))
+ if (oldSum == (oldSum = checkSum))
break;
}
}
- if ((md & STOP) == 0)
- MODE.compareAndSet(this, md, md | STOP);
+ do {} while (!U.compareAndSwapInt(this, RUNSTATE,
+ rs = runState, rs | STOP));
}
- while (((md = mode) & TERMINATED) == 0) { // help terminate others
- for (long oldSum = 0L;;) { // repeat until stable
- WorkQueue[] ws; WorkQueue w;
- long checkSum = ctl;
- if ((ws = workQueues) != null) {
- for (int i = 0; i < ws.length; ++i) {
- if ((w = ws[i]) != null) {
- ForkJoinWorkerThread wt = w.owner;
- w.cancelAll(); // clear queues
- if (wt != null) {
+ for (long oldSum = 0L;;) { // repeat until stable
+ WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt;
+ long checkSum = ctl;
+ if ((ws = workQueues) != null) { // help terminate others
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ w.cancelAll(); // clear queues
+ checkSum += w.base;
+ if (w.qlock >= 0) {
+ w.qlock = -1; // racy set OK
+ if ((wt = w.owner) != null) {
try { // unblock join or park
wt.interrupt();
} catch (Throwable ignore) {
}
}
- checkSum += ((long)w.phase << 32) + w.base;
}
}
}
- if (((md = mode) & TERMINATED) != 0 ||
- (workQueues == ws && oldSum == (oldSum = checkSum)))
- break;
}
- if ((md & TERMINATED) != 0)
+ if (oldSum == (oldSum = checkSum))
break;
- else if ((md & SMASK) + (short)(ctl >>> TC_SHIFT) > 0)
- break;
- else if (MODE.compareAndSet(this, md, md | TERMINATED)) {
- synchronized (this) {
- notifyAll(); // for awaitTermination
- }
- break;
+ }
+
+ if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) {
+ runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write
+ synchronized (this) {
+ notifyAll(); // for awaitTermination
}
}
- return true;
+
+ return -1;
+ }
+
+ // External operations
+
+ /**
+ * Constructs and tries to install a new external queue,
+ * failing if the workQueues array already has a queue at
+ * the given index.
+ *
+ * @param index the index of the new queue
+ */
+ private void tryCreateExternalQueue(int index) {
+ AuxState aux;
+ if ((aux = auxState) != null && index >= 0) {
+ WorkQueue q = new WorkQueue(this, null);
+ q.config = index;
+ q.scanState = ~UNSIGNALLED;
+ q.qlock = 1; // lock queue
+ boolean installed = false;
+ aux.lock();
+ try { // lock pool to install
+ WorkQueue[] ws;
+ if ((ws = workQueues) != null && index < ws.length &&
+ ws[index] == null) {
+ ws[index] = q; // else throw away
+ installed = true;
+ }
+ } finally {
+ aux.unlock();
+ }
+ if (installed) {
+ try {
+ q.growArray();
+ } finally {
+ q.qlock = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the given task to a submission queue at submitter's
+ * current queue. Also performs secondary initialization upon the
+ * first submission of the first task to the pool, and detects
+ * first submission by an external thread and creates a new shared
+ * queue if the one at index if empty or contended.
+ *
+ * @param task the task. Caller must ensure non-null.
+ */
+ final void externalPush(ForkJoinTask<?> task) {
+ int r; // initialize caller's probe
+ if ((r = ThreadLocalRandom.getProbe()) == 0) {
+ ThreadLocalRandom.localInit();
+ r = ThreadLocalRandom.getProbe();
+ }
+ for (;;) {
+ WorkQueue q; int wl, k, stat;
+ int rs = runState;
+ WorkQueue[] ws = workQueues;
+ if (rs <= 0 || ws == null || (wl = ws.length) <= 0)
+ tryInitialize(true);
+ else if ((q = ws[k = (wl - 1) & r & SQMASK]) == null)
+ tryCreateExternalQueue(k);
+ else if ((stat = q.sharedPush(task)) < 0)
+ break;
+ else if (stat == 0) {
+ signalWork();
+ break;
+ }
+ else // move if busy
+ r = ThreadLocalRandom.advanceProbe(r);
+ }
+ }
+
+ /**
+ * Pushes a possibly-external submission.
+ */
+ private <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task) {
+ Thread t; ForkJoinWorkerThread w; WorkQueue q;
+ if (task == null)
+ throw new NullPointerException();
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
+ (w = (ForkJoinWorkerThread)t).pool == this &&
+ (q = w.workQueue) != null)
+ q.push(task);
+ else
+ externalPush(task);
+ return task;
+ }
+
+ /**
+ * Returns common pool queue for an external thread.
+ */
+ static WorkQueue commonSubmitterQueue() {
+ ForkJoinPool p = common;
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; int wl;
+ return (p != null && (ws = p.workQueues) != null &&
+ (wl = ws.length) > 0) ?
+ ws[(wl - 1) & r & SQMASK] : null;
+ }
+
+ /**
+ * Performs tryUnpush for an external submitter.
+ */
+ final boolean tryExternalUnpush(ForkJoinTask<?> task) {
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; WorkQueue w; int wl;
+ return ((ws = workQueues) != null &&
+ (wl = ws.length) > 0 &&
+ (w = ws[(wl - 1) & r & SQMASK]) != null &&
+ w.trySharedUnpush(task));
+ }
+
+ /**
+ * Performs helpComplete for an external submitter.
+ */
+ final int externalHelpComplete(CountedCompleter<?> task, int maxTasks) {
+ WorkQueue[] ws; int wl;
+ int r = ThreadLocalRandom.getProbe();
+ return ((ws = workQueues) != null && (wl = ws.length) > 0) ?
+ helpComplete(ws[(wl - 1) & r & SQMASK], task, maxTasks) : 0;
}
// Exported methods
@@ -2134,10 +2607,9 @@
/**
* Creates a {@code ForkJoinPool} with parallelism equal to {@link
- * java.lang.Runtime#availableProcessors}, using defaults for all
- * other parameters (see {@link #ForkJoinPool(int,
- * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean,
- * int, int, int, Predicate, long, TimeUnit)}).
+ * java.lang.Runtime#availableProcessors}, using the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
*
* @throws SecurityException if a security manager exists and
* the caller is not permitted to modify threads
@@ -2146,16 +2618,14 @@
*/
public ForkJoinPool() {
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
- defaultForkJoinWorkerThreadFactory, null, false,
- 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
+ defaultForkJoinWorkerThreadFactory, null, false);
}
/**
* Creates a {@code ForkJoinPool} with the indicated parallelism
- * level, using defaults for all other parameters (see {@link
- * #ForkJoinPool(int, ForkJoinWorkerThreadFactory,
- * UncaughtExceptionHandler, boolean, int, int, int, Predicate,
- * long, TimeUnit)}).
+ * level, the {@linkplain
+ * #defaultForkJoinWorkerThreadFactory default thread factory},
+ * no UncaughtExceptionHandler, and non-async LIFO processing mode.
*
* @param parallelism the parallelism level
* @throws IllegalArgumentException if parallelism less than or
@@ -2166,15 +2636,11 @@
* java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public ForkJoinPool(int parallelism) {
- this(parallelism, defaultForkJoinWorkerThreadFactory, null, false,
- 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
+ this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
}
/**
- * Creates a {@code ForkJoinPool} with the given parameters (using
- * defaults for others -- see {@link #ForkJoinPool(int,
- * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean,
- * int, int, int, Predicate, long, TimeUnit)}).
+ * Creates a {@code ForkJoinPool} with the given parameters.
*
* @param parallelism the parallelism level. For default value,
* use {@link java.lang.Runtime#availableProcessors}.
@@ -2201,186 +2667,43 @@
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
- this(parallelism, factory, handler, asyncMode,
- 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
- }
-
- /**
- * Creates a {@code ForkJoinPool} with the given parameters.
- *
- * @param parallelism the parallelism level. For default value,
- * use {@link java.lang.Runtime#availableProcessors}.
- *
- * @param factory the factory for creating new threads. For
- * default value, use {@link #defaultForkJoinWorkerThreadFactory}.
- *
- * @param handler the handler for internal worker threads that
- * terminate due to unrecoverable errors encountered while
- * executing tasks. For default value, use {@code null}.
- *
- * @param asyncMode if true, establishes local first-in-first-out
- * scheduling mode for forked tasks that are never joined. This
- * mode may be more appropriate than default locally stack-based
- * mode in applications in which worker threads only process
- * event-style asynchronous tasks. For default value, use {@code
- * false}.
- *
- * @param corePoolSize the number of threads to keep in the pool
- * (unless timed out after an elapsed keep-alive). Normally (and
- * by default) this is the same value as the parallelism level,
- * but may be set to a larger value to reduce dynamic overhead if
- * tasks regularly block. Using a smaller value (for example
- * {@code 0}) has the same effect as the default.
- *
- * @param maximumPoolSize the maximum number of threads allowed.
- * When the maximum is reached, attempts to replace blocked
- * threads fail. (However, because creation and termination of
- * different threads may overlap, and may be managed by the given
- * thread factory, this value may be transiently exceeded.) To
- * arrange the same value as is used by default for the common
- * pool, use {@code 256} plus the {@code parallelism} level. (By
- * default, the common pool allows a maximum of 256 spare
- * threads.) Using a value (for example {@code
- * Integer.MAX_VALUE}) larger than the implementation's total
- * thread limit has the same effect as using this limit (which is
- * the default).
- *
- * @param minimumRunnable the minimum allowed number of core
- * threads not blocked by a join or {@link ManagedBlocker}. To
- * ensure progress, when too few unblocked threads exist and
- * unexecuted tasks may exist, new threads are constructed, up to
- * the given maximumPoolSize. For the default value, use {@code
- * 1}, that ensures liveness. A larger value might improve
- * throughput in the presence of blocked activities, but might
- * not, due to increased overhead. A value of zero may be
- * acceptable when submitted tasks cannot have dependencies
- * requiring additional threads.
- *
- * @param saturate if non-null, a predicate invoked upon attempts
- * to create more than the maximum total allowed threads. By
- * default, when a thread is about to block on a join or {@link
- * ManagedBlocker}, but cannot be replaced because the
- * maximumPoolSize would be exceeded, a {@link
- * RejectedExecutionException} is thrown. But if this predicate
- * returns {@code true}, then no exception is thrown, so the pool
- * continues to operate with fewer than the target number of
- * runnable threads, which might not ensure progress.
- *
- * @param keepAliveTime the elapsed time since last use before
- * a thread is terminated (and then later replaced if needed).
- * For the default value, use {@code 60, TimeUnit.SECONDS}.
- *
- * @param unit the time unit for the {@code keepAliveTime} argument
- *
- * @throws IllegalArgumentException if parallelism is less than or
- * equal to zero, or is greater than implementation limit,
- * or if maximumPoolSize is less than parallelism,
- * of if the keepAliveTime is less than or equal to zero.
- * @throws NullPointerException if the factory is null
- * @throws SecurityException if a security manager exists and
- * the caller is not permitted to modify threads
- * because it does not hold {@link
- * java.lang.RuntimePermission}{@code ("modifyThread")}
- * @since 9
- */
- public ForkJoinPool(int parallelism,
- ForkJoinWorkerThreadFactory factory,
- UncaughtExceptionHandler handler,
- boolean asyncMode,
- int corePoolSize,
- int maximumPoolSize,
- int minimumRunnable,
- Predicate<? super ForkJoinPool> saturate,
- long keepAliveTime,
- TimeUnit unit) {
- // check, encode, pack parameters
- if (parallelism <= 0 || parallelism > MAX_CAP ||
- maximumPoolSize < parallelism || keepAliveTime <= 0L)
- throw new IllegalArgumentException();
- if (factory == null)
- throw new NullPointerException();
- long ms = Math.max(unit.toMillis(keepAliveTime), TIMEOUT_SLOP);
-
- int corep = Math.min(Math.max(corePoolSize, parallelism), MAX_CAP);
- long c = ((((long)(-corep) << TC_SHIFT) & TC_MASK) |
- (((long)(-parallelism) << RC_SHIFT) & RC_MASK));
- int m = parallelism | (asyncMode ? FIFO : 0);
- int maxSpares = Math.min(maximumPoolSize, MAX_CAP) - parallelism;
- int minAvail = Math.min(Math.max(minimumRunnable, 0), MAX_CAP);
- int b = ((minAvail - parallelism) & SMASK) | (maxSpares << SWIDTH);
- int n = (parallelism > 1) ? parallelism - 1 : 1; // at least 2 slots
- n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
- n = (n + 1) << 1; // power of two, including space for submission queues
-
- this.workerNamePrefix = "ForkJoinPool-" + nextPoolId() + "-worker-";
- this.workQueues = new WorkQueue[n];
- this.factory = factory;
- this.ueh = handler;
- this.saturate = saturate;
- this.keepAlive = ms;
- this.bounds = b;
- this.mode = m;
- this.ctl = c;
+ this(checkParallelism(parallelism),
+ checkFactory(factory),
+ handler,
+ asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
+ "ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
- private static Object newInstanceFromSystemProperty(String property)
- throws ReflectiveOperationException {
- String className = System.getProperty(property);
- return (className == null)
- ? null
- : ClassLoader.getSystemClassLoader().loadClass(className)
- .getConstructor().newInstance();
+ private static int checkParallelism(int parallelism) {
+ if (parallelism <= 0 || parallelism > MAX_CAP)
+ throw new IllegalArgumentException();
+ return parallelism;
+ }
+
+ private static ForkJoinWorkerThreadFactory checkFactory
+ (ForkJoinWorkerThreadFactory factory) {
+ if (factory == null)
+ throw new NullPointerException();
+ return factory;
}
/**
- * Constructor for common pool using parameters possibly
- * overridden by system properties
+ * Creates a {@code ForkJoinPool} with the given parameters, without
+ * any security checks or parameter validation. Invoked directly by
+ * makeCommonPool.
*/
- private ForkJoinPool(byte forCommonPoolOnly) {
- int parallelism = -1;
- ForkJoinWorkerThreadFactory fac = null;
- UncaughtExceptionHandler handler = null;
- try { // ignore exceptions in accessing/parsing properties
- String pp = System.getProperty
- ("java.util.concurrent.ForkJoinPool.common.parallelism");
- if (pp != null)
- parallelism = Integer.parseInt(pp);
- fac = (ForkJoinWorkerThreadFactory) newInstanceFromSystemProperty(
- "java.util.concurrent.ForkJoinPool.common.threadFactory");
- handler = (UncaughtExceptionHandler) newInstanceFromSystemProperty(
- "java.util.concurrent.ForkJoinPool.common.exceptionHandler");
- } catch (Exception ignore) {
- }
-
- if (fac == null) {
- if (System.getSecurityManager() == null)
- fac = defaultForkJoinWorkerThreadFactory;
- else // use security-managed default
- fac = new InnocuousForkJoinWorkerThreadFactory();
- }
- if (parallelism < 0 && // default 1 less than #cores
- (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
- parallelism = 1;
- if (parallelism > MAX_CAP)
- parallelism = MAX_CAP;
-
- long c = ((((long)(-parallelism) << TC_SHIFT) & TC_MASK) |
- (((long)(-parallelism) << RC_SHIFT) & RC_MASK));
- int b = ((1 - parallelism) & SMASK) | (COMMON_MAX_SPARES << SWIDTH);
- int n = (parallelism > 1) ? parallelism - 1 : 1;
- n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
- n = (n + 1) << 1;
-
- this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
- this.workQueues = new WorkQueue[n];
- this.factory = fac;
+ private ForkJoinPool(int parallelism,
+ ForkJoinWorkerThreadFactory factory,
+ UncaughtExceptionHandler handler,
+ int mode,
+ String workerNamePrefix) {
+ this.workerNamePrefix = workerNamePrefix;
+ this.factory = factory;
this.ueh = handler;
- this.saturate = null;
- this.keepAlive = DEFAULT_KEEPALIVE;
- this.bounds = b;
- this.mode = parallelism;
- this.ctl = c;
+ this.config = (parallelism & SMASK) | mode;
+ long np = (long)(-parallelism); // offset ctl counts
+ this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
/**
@@ -2494,13 +2817,15 @@
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
*/
- @SuppressWarnings("unchecked")
public ForkJoinTask<?> submit(Runnable task) {
if (task == null)
throw new NullPointerException();
- return externalSubmit((task instanceof ForkJoinTask<?>)
- ? (ForkJoinTask<Void>) task // avoid re-wrap
- : new ForkJoinTask.AdaptedRunnableAction(task));
+ ForkJoinTask<?> job;
+ if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+ job = (ForkJoinTask<?>) task;
+ else
+ job = new ForkJoinTask.AdaptedRunnableAction(task);
+ return externalSubmit(job);
}
/**
@@ -2554,8 +2879,8 @@
* @return the targeted parallelism level of this pool
*/
public int getParallelism() {
- int par = mode & SMASK;
- return (par > 0) ? par : 1;
+ int par;
+ return ((par = config & SMASK) > 0) ? par : 1;
}
/**
@@ -2577,7 +2902,7 @@
* @return the number of worker threads
*/
public int getPoolSize() {
- return ((mode & SMASK) + (short)(ctl >>> TC_SHIFT));
+ return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
}
/**
@@ -2587,7 +2912,7 @@
* @return {@code true} if this pool uses async mode
*/
public boolean getAsyncMode() {
- return (mode & FIFO) != 0;
+ return (config & FIFO_QUEUE) != 0;
}
/**
@@ -2599,9 +2924,8 @@
* @return the number of worker threads
*/
public int getRunningThreadCount() {
- WorkQueue[] ws; WorkQueue w;
- VarHandle.acquireFence();
int rc = 0;
+ WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 1; i < ws.length; i += 2) {
if ((w = ws[i]) != null && w.isApparentlyUnblocked())
@@ -2619,7 +2943,7 @@
* @return the number of active threads
*/
public int getActiveThreadCount() {
- int r = (mode & SMASK) + (int)(ctl >> RC_SHIFT);
+ int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
return (r <= 0) ? 0 : r; // suppress momentarily negative values
}
@@ -2635,30 +2959,7 @@
* @return {@code true} if all threads are currently idle
*/
public boolean isQuiescent() {
- for (;;) {
- long c = ctl;
- int md = mode, pc = md & SMASK;
- int tc = pc + (short)(c >>> TC_SHIFT);
- int rc = pc + (int)(c >> RC_SHIFT);
- if ((md & (STOP | TERMINATED)) != 0)
- return true;
- else if (rc > 0)
- return false;
- else {
- WorkQueue[] ws; WorkQueue v;
- if ((ws = workQueues) != null) {
- for (int i = 1; i < ws.length; i += 2) {
- if ((v = ws[i]) != null) {
- if (v.source > 0)
- return false;
- --tc;
- }
- }
- }
- if (tc == 0 && ctl == c)
- return true;
- }
- }
+ return (config & SMASK) + (int)(ctl >> AC_SHIFT) <= 0;
}
/**
@@ -2673,12 +2974,13 @@
* @return the number of steals
*/
public long getStealCount() {
- long count = stealCount;
+ AuxState sc = auxState;
+ long count = (sc == null) ? 0L : sc.stealCount;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 1; i < ws.length; i += 2) {
if ((w = ws[i]) != null)
- count += (long)w.nsteals & 0xffffffffL;
+ count += w.nsteals;
}
}
return count;
@@ -2695,9 +2997,8 @@
* @return the number of queued tasks
*/
public long getQueuedTaskCount() {
+ long count = 0;
WorkQueue[] ws; WorkQueue w;
- VarHandle.acquireFence();
- int count = 0;
if ((ws = workQueues) != null) {
for (int i = 1; i < ws.length; i += 2) {
if ((w = ws[i]) != null)
@@ -2715,9 +3016,8 @@
* @return the number of queued submissions
*/
public int getQueuedSubmissionCount() {
- WorkQueue[] ws; WorkQueue w;
- VarHandle.acquireFence();
int count = 0;
+ WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; i += 2) {
if ((w = ws[i]) != null)
@@ -2735,7 +3035,6 @@
*/
public boolean hasQueuedSubmissions() {
WorkQueue[] ws; WorkQueue w;
- VarHandle.acquireFence();
if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; i += 2) {
if ((w = ws[i]) != null && !w.isEmpty())
@@ -2753,7 +3052,15 @@
* @return the next submission, or {@code null} if none
*/
protected ForkJoinTask<?> pollSubmission() {
- return pollScan(true);
+ WorkQueue[] ws; int wl; WorkQueue w; ForkJoinTask<?> t;
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ if ((ws = workQueues) != null && (wl = ws.length) > 0) {
+ for (int m = wl - 1, i = 0; i < wl; ++i) {
+ if ((w = ws[(i << 1) & m]) != null && (t = w.poll()) != null)
+ return t;
+ }
+ }
+ return null;
}
/**
@@ -2774,9 +3081,8 @@
* @return the number of elements transferred
*/
protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
- WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
- VarHandle.acquireFence();
int count = 0;
+ WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; ++i) {
if ((w = ws[i]) != null) {
@@ -2799,10 +3105,10 @@
*/
public String toString() {
// Use a single pass through workQueues to collect counts
- int md = mode; // read volatile fields first
- long c = ctl;
- long st = stealCount;
long qt = 0L, qs = 0L; int rc = 0;
+ AuxState sc = auxState;
+ long st = (sc == null) ? 0L : sc.stealCount;
+ long c = ctl;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; ++i) {
@@ -2812,22 +3118,22 @@
qs += size;
else {
qt += size;
- st += (long)w.nsteals & 0xffffffffL;
+ st += w.nsteals;
if (w.isApparentlyUnblocked())
++rc;
}
}
}
}
-
- int pc = (md & SMASK);
+ int pc = (config & SMASK);
int tc = pc + (short)(c >>> TC_SHIFT);
- int ac = pc + (int)(c >> RC_SHIFT);
+ int ac = pc + (int)(c >> AC_SHIFT);
if (ac < 0) // ignore transient negative
ac = 0;
- String level = ((md & TERMINATED) != 0 ? "Terminated" :
- (md & STOP) != 0 ? "Terminating" :
- (md & SHUTDOWN) != 0 ? "Shutting down" :
+ int rs = runState;
+ String level = ((rs & TERMINATED) != 0 ? "Terminated" :
+ (rs & STOP) != 0 ? "Terminating" :
+ (rs & SHUTDOWN) != 0 ? "Shutting down" :
"Running");
return super.toString() +
"[" + level +
@@ -2890,7 +3196,7 @@
* @return {@code true} if all tasks have completed following shut down
*/
public boolean isTerminated() {
- return (mode & TERMINATED) != 0;
+ return (runState & TERMINATED) != 0;
}
/**
@@ -2907,8 +3213,8 @@
* @return {@code true} if terminating but not yet terminated
*/
public boolean isTerminating() {
- int md = mode;
- return (md & STOP) != 0 && (md & TERMINATED) == 0;
+ int rs = runState;
+ return (rs & STOP) != 0 && (rs & TERMINATED) == 0;
}
/**
@@ -2917,7 +3223,7 @@
* @return {@code true} if this pool has been shut down
*/
public boolean isShutdown() {
- return (mode & SHUTDOWN) != 0;
+ return (runState & SHUTDOWN) != 0;
}
/**
@@ -2981,19 +3287,30 @@
helpQuiescePool(wt.workQueue);
return true;
}
- else {
- for (long startTime = System.nanoTime();;) {
- ForkJoinTask<?> t;
- if ((t = pollScan(false)) != null)
- t.doExec();
- else if (isQuiescent())
- return true;
- else if ((System.nanoTime() - startTime) > nanos)
+ long startTime = System.nanoTime();
+ WorkQueue[] ws;
+ int r = 0, wl;
+ boolean found = true;
+ while (!isQuiescent() && (ws = workQueues) != null &&
+ (wl = ws.length) > 0) {
+ if (!found) {
+ if ((System.nanoTime() - startTime) > nanos)
return false;
- else
- Thread.yield(); // cannot block
+ Thread.yield(); // cannot block
+ }
+ found = false;
+ for (int m = wl - 1, j = (m + 1) << 2; j >= 0; --j) {
+ ForkJoinTask<?> t; WorkQueue q; int b, k;
+ if ((k = r++ & m) <= m && k >= 0 && (q = ws[k]) != null &&
+ (b = q.base) - q.top < 0) {
+ found = true;
+ if ((t = q.pollAt(b)) != null)
+ t.doExec();
+ break;
+ }
}
}
+ return true;
}
/**
@@ -3106,22 +3423,19 @@
*/
public static void managedBlock(ManagedBlocker blocker)
throws InterruptedException {
- if (blocker == null) throw new NullPointerException();
ForkJoinPool p;
ForkJoinWorkerThread wt;
- WorkQueue w;
Thread t = Thread.currentThread();
if ((t instanceof ForkJoinWorkerThread) &&
- (p = (wt = (ForkJoinWorkerThread)t).pool) != null &&
- (w = wt.workQueue) != null) {
- int block;
+ (p = (wt = (ForkJoinWorkerThread)t).pool) != null) {
+ WorkQueue w = wt.workQueue;
while (!blocker.isReleasable()) {
- if ((block = p.tryCompensate(w)) != 0) {
+ if (p.tryCompensate(w)) {
try {
do {} while (!blocker.isReleasable() &&
!blocker.block());
} finally {
- CTL.getAndAdd(p, (block > 0) ? RC_UNIT : 0L);
+ U.getAndAddLong(p, CTL, AC_UNIT);
}
break;
}
@@ -3133,29 +3447,6 @@
}
}
- /**
- * If the given executor is a ForkJoinPool, poll and execute
- * AsynchronousCompletionTasks from worker's queue until none are
- * available or blocker is released.
- */
- static void helpAsyncBlocker(Executor e, ManagedBlocker blocker) {
- if (e instanceof ForkJoinPool) {
- WorkQueue w; ForkJoinWorkerThread wt; WorkQueue[] ws; int r, n;
- ForkJoinPool p = (ForkJoinPool)e;
- Thread thread = Thread.currentThread();
- if (thread instanceof ForkJoinWorkerThread &&
- (wt = (ForkJoinWorkerThread)thread).pool == p)
- w = wt.workQueue;
- else if ((r = ThreadLocalRandom.getProbe()) != 0 &&
- (ws = p.workQueues) != null && (n = ws.length) > 0)
- w = ws[(n - 1) & r & SQMASK];
- else
- w = null;
- if (w != null)
- w.helpAsyncBlocker(blocker);
- }
- }
-
// AbstractExecutorService overrides. These rely on undocumented
// fact that ForkJoinTask.adapt returns ForkJoinTasks that also
// implement RunnableFuture.
@@ -3168,19 +3459,26 @@
return new ForkJoinTask.AdaptedCallable<T>(callable);
}
- // VarHandle mechanics
- private static final VarHandle CTL;
- private static final VarHandle MODE;
- static final VarHandle QA;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long CTL;
+ private static final long RUNSTATE;
+ private static final int ABASE;
+ private static final int ASHIFT;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- CTL = l.findVarHandle(ForkJoinPool.class, "ctl", long.class);
- MODE = l.findVarHandle(ForkJoinPool.class, "mode", int.class);
- QA = MethodHandles.arrayElementVarHandle(ForkJoinTask[].class);
+ CTL = U.objectFieldOffset
+ (ForkJoinPool.class.getDeclaredField("ctl"));
+ RUNSTATE = U.objectFieldOffset
+ (ForkJoinPool.class.getDeclaredField("runState"));
+ ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
+ int scale = U.arrayIndexScale(ForkJoinTask[].class);
+ if ((scale & (scale - 1)) != 0)
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
// Reduce the risk of rare disastrous classloading in first call to
@@ -3200,11 +3498,52 @@
new DefaultForkJoinWorkerThreadFactory();
modifyThreadPermission = new RuntimePermission("modifyThread");
- common = AccessController.doPrivileged(new PrivilegedAction<>() {
- public ForkJoinPool run() {
- return new ForkJoinPool((byte)0); }});
+ common = java.security.AccessController.doPrivileged
+ (new java.security.PrivilegedAction<ForkJoinPool>() {
+ public ForkJoinPool run() { return makeCommonPool(); }});
- COMMON_PARALLELISM = Math.max(common.mode & SMASK, 1);
+ // report 1 even if threads disabled
+ COMMON_PARALLELISM = Math.max(common.config & SMASK, 1);
+ }
+
+ /**
+ * Creates and returns the common pool, respecting user settings
+ * specified via system properties.
+ */
+ static ForkJoinPool makeCommonPool() {
+ int parallelism = -1;
+ ForkJoinWorkerThreadFactory factory = null;
+ UncaughtExceptionHandler handler = null;
+ try { // ignore exceptions in accessing/parsing properties
+ String pp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.parallelism");
+ String fp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.threadFactory");
+ String hp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
+ if (pp != null)
+ parallelism = Integer.parseInt(pp);
+ if (fp != null)
+ factory = ((ForkJoinWorkerThreadFactory)ClassLoader.
+ getSystemClassLoader().loadClass(fp).newInstance());
+ if (hp != null)
+ handler = ((UncaughtExceptionHandler)ClassLoader.
+ getSystemClassLoader().loadClass(hp).newInstance());
+ } catch (Exception ignore) {
+ }
+ if (factory == null) {
+ if (System.getSecurityManager() == null)
+ factory = defaultForkJoinWorkerThreadFactory;
+ else // use security-managed default
+ factory = new InnocuousForkJoinWorkerThreadFactory();
+ }
+ if (parallelism < 0 && // default 1 less than #cores
+ (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
+ parallelism = 1;
+ if (parallelism > MAX_CAP)
+ parallelism = MAX_CAP;
+ return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
+ "ForkJoinPool.commonPool-worker-");
}
/**
@@ -3217,20 +3556,27 @@
* An ACC to restrict permissions for the factory itself.
* The constructed workers have no permissions set.
*/
- private static final AccessControlContext ACC = contextWithPermissions(
- modifyThreadPermission,
- new RuntimePermission("enableContextClassLoaderOverride"),
- new RuntimePermission("modifyThreadGroup"),
- new RuntimePermission("getClassLoader"),
- new RuntimePermission("setContextClassLoader"));
+ private static final AccessControlContext innocuousAcc;
+ static {
+ Permissions innocuousPerms = new Permissions();
+ innocuousPerms.add(modifyThreadPermission);
+ innocuousPerms.add(new RuntimePermission(
+ "enableContextClassLoaderOverride"));
+ innocuousPerms.add(new RuntimePermission(
+ "modifyThreadGroup"));
+ innocuousAcc = new AccessControlContext(new ProtectionDomain[] {
+ new ProtectionDomain(null, innocuousPerms)
+ });
+ }
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
- return AccessController.doPrivileged(
- new PrivilegedAction<>() {
+ return java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ForkJoinWorkerThread>() {
public ForkJoinWorkerThread run() {
return new ForkJoinWorkerThread.
- InnocuousForkJoinWorkerThread(pool); }},
- ACC);
+ InnocuousForkJoinWorkerThread(pool);
+ }}, innocuousAcc);
}
}
+
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ForkJoinTask.java b/ojluni/src/main/java/java/util/concurrent/ForkJoinTask.java
index fd28e84..efccfa5 100644
--- a/ojluni/src/main/java/java/util/concurrent/ForkJoinTask.java
+++ b/ojluni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -36,8 +36,6 @@
package java.util.concurrent;
import java.io.Serializable;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
@@ -98,7 +96,7 @@
* encountering the exception; minimally only the latter.
*
* <p>It is possible to define and use ForkJoinTasks that may block,
- * but doing so requires three further considerations: (1) Completion
+ * but doing do requires three further considerations: (1) Completion
* of few if any <em>other</em> tasks should be dependent on a task
* that blocks on external synchronization or I/O. Event-style async
* tasks that are never joined (for example, those subclassing {@link
@@ -138,11 +136,11 @@
* {@link #isCompletedNormally} is true if a task completed without
* cancellation or encountering an exception; {@link #isCancelled} is
* true if the task was cancelled (in which case {@link #getException}
- * returns a {@link CancellationException}); and
+ * returns a {@link java.util.concurrent.CancellationException}); and
* {@link #isCompletedAbnormally} is true if a task was either
* cancelled or encountered an exception, in which case {@link
* #getException} will return either the encountered exception or
- * {@link CancellationException}.
+ * {@link java.util.concurrent.CancellationException}.
*
* <p>The ForkJoinTask class is not usually directly subclassed.
* Instead, you subclass one of the abstract classes that support a
@@ -223,59 +221,52 @@
* methods in a way that flows well in javadocs.
*/
- /**
+ /*
* The status field holds run control status bits packed into a
- * single int to ensure atomicity. Status is initially zero, and
- * takes on nonnegative values until completed, upon which it
- * holds (sign bit) DONE, possibly with ABNORMAL (cancelled or
- * exceptional) and THROWN (in which case an exception has been
- * stored). Tasks with dependent blocked waiting joiners have the
- * SIGNAL bit set. Completion of a task with SIGNAL set awakens
- * any waiters via notifyAll. (Waiters also help signal others
- * upon completion.)
+ * single int to minimize footprint and to ensure atomicity (via
+ * CAS). Status is initially zero, and takes on nonnegative
+ * values until completed, upon which status (anded with
+ * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
+ * undergoing blocking waits by other threads have the SIGNAL bit
+ * set. Completion of a stolen task with SIGNAL set awakens any
+ * waiters via notifyAll. Even though suboptimal for some
+ * purposes, we use basic builtin wait/notify to take advantage of
+ * "monitor inflation" in JVMs that we would otherwise need to
+ * emulate to avoid adding further per-task bookkeeping overhead.
+ * We want these monitors to be "fat", i.e., not use biasing or
+ * thin-lock techniques, so use some odd coding idioms that tend
+ * to avoid them, mainly by arranging that every synchronized
+ * block performs a wait, notifyAll or both.
*
* These control bits occupy only (some of) the upper half (16
* bits) of status field. The lower bits are used for user-defined
* tags.
*/
+
+ /** The run status of this task */
volatile int status; // accessed directly by pool and workers
-
- private static final int DONE = 1 << 31; // must be negative
- private static final int ABNORMAL = 1 << 18; // set atomically with DONE
- private static final int THROWN = 1 << 17; // set atomically with ABNORMAL
- private static final int SIGNAL = 1 << 16; // true if joiner waiting
- private static final int SMASK = 0xffff; // short bits for tags
-
- static boolean isExceptionalStatus(int s) { // needed by subclasses
- return (s & THROWN) != 0;
- }
+ static final int DONE_MASK = 0xf0000000; // mask out non-completion bits
+ static final int NORMAL = 0xf0000000; // must be negative
+ static final int CANCELLED = 0xc0000000; // must be < NORMAL
+ static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED
+ static final int SIGNAL = 0x00010000; // must be >= 1 << 16
+ static final int SMASK = 0x0000ffff; // short bits for tags
/**
- * Sets DONE status and wakes up threads waiting to join this task.
+ * Marks completion and wakes up threads waiting to join this
+ * task.
*
- * @return status on exit
+ * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
+ * @return completion status on exit
*/
- private int setDone() {
- int s;
- if (((s = (int)STATUS.getAndBitwiseOr(this, DONE)) & SIGNAL) != 0)
- synchronized (this) { notifyAll(); }
- return s | DONE;
- }
-
- /**
- * Marks cancelled or exceptional completion unless already done.
- *
- * @param completion must be DONE | ABNORMAL, ORed with THROWN if exceptional
- * @return status on exit
- */
- private int abnormalCompletion(int completion) {
- for (int s, ns;;) {
+ private int setCompletion(int completion) {
+ for (int s;;) {
if ((s = status) < 0)
return s;
- else if (STATUS.weakCompareAndSet(this, s, ns = s | completion)) {
- if ((s & SIGNAL) != 0)
+ if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
+ if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
- return ns;
+ return completion;
}
}
}
@@ -293,11 +284,10 @@
try {
completed = exec();
} catch (Throwable rex) {
- completed = false;
- s = setExceptionalCompletion(rex);
+ return setExceptionalCompletion(rex);
}
if (completed)
- s = setDone();
+ s = setCompletion(NORMAL);
}
return s;
}
@@ -309,7 +299,9 @@
* @param timeout using Object.wait conventions.
*/
final void internalWait(long timeout) {
- if ((int)STATUS.getAndBitwiseOr(this, SIGNAL) >= 0) {
+ int s;
+ if ((s = status) >= 0 && // force completer to issue notify
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
try { wait(timeout); } catch (InterruptedException ie) { }
@@ -324,24 +316,27 @@
* @return status upon completion
*/
private int externalAwaitDone() {
- int s = tryExternalHelp();
- if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
+ int s = ((this instanceof CountedCompleter) ? // try helping
+ ForkJoinPool.common.externalHelpComplete(
+ (CountedCompleter<?>)this, 0) :
+ ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
+ if (s >= 0 && (s = status) >= 0) {
boolean interrupted = false;
- synchronized (this) {
- for (;;) {
- if ((s = status) >= 0) {
- try {
- wait(0L);
- } catch (InterruptedException ie) {
- interrupted = true;
+ do {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
+ try {
+ wait(0L);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
}
- }
- else {
- notifyAll();
- break;
+ else
+ notifyAll();
}
}
- }
+ } while ((s = status) >= 0);
if (interrupted)
Thread.currentThread().interrupt();
}
@@ -352,40 +347,30 @@
* Blocks a non-worker-thread until completion or interruption.
*/
private int externalInterruptibleAwaitDone() throws InterruptedException {
- int s = tryExternalHelp();
- if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
- synchronized (this) {
- for (;;) {
- if ((s = status) >= 0)
- wait(0L);
- else {
- notifyAll();
- break;
+ int s;
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ if ((s = status) >= 0 &&
+ (s = ((this instanceof CountedCompleter) ?
+ ForkJoinPool.common.externalHelpComplete(
+ (CountedCompleter<?>)this, 0) :
+ ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
+ 0)) >= 0) {
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ wait(0L);
+ else
+ notifyAll();
}
}
}
}
- else if (Thread.interrupted())
- throw new InterruptedException();
return s;
}
/**
- * Tries to help with tasks allowed for external callers.
- *
- * @return current status
- */
- private int tryExternalHelp() {
- int s;
- return ((s = status) < 0 ? s:
- (this instanceof CountedCompleter) ?
- ForkJoinPool.common.externalHelpComplete(
- (CountedCompleter<?>)this, 0) :
- ForkJoinPool.common.tryExternalUnpush(this) ?
- doExec() : 0);
- }
-
- /**
* Implementation for join, get, quietlyJoin. Directly handles
* only cases of already-completed, external wait, and
* unfork+exec. Others are relayed to ForkJoinPool.awaitJoin.
@@ -419,24 +404,22 @@
// Exception table support
/**
- * Hash table of exceptions thrown by tasks, to enable reporting
- * by callers. Because exceptions are rare, we don't directly keep
+ * Table of exceptions thrown by tasks, to enable reporting by
+ * callers. Because exceptions are rare, we don't directly keep
* them with task objects, but instead use a weak ref table. Note
* that cancellation exceptions don't appear in the table, but are
* instead recorded as status values.
*
- * The exception table has a fixed capacity.
+ * Note: These statics are initialized below in static block.
*/
- private static final ExceptionNode[] exceptionTable
- = new ExceptionNode[32];
+ private static final ExceptionNode[] exceptionTable;
+ private static final ReentrantLock exceptionTableLock;
+ private static final ReferenceQueue<Object> exceptionTableRefQueue;
- /** Lock protecting access to exceptionTable. */
- private static final ReentrantLock exceptionTableLock
- = new ReentrantLock();
-
- /** Reference queue of stale exceptionally completed tasks. */
- private static final ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue
- = new ReferenceQueue<>();
+ /**
+ * Fixed capacity for exceptionTable.
+ */
+ private static final int EXCEPTION_MAP_CAPACITY = 32;
/**
* Key-value nodes for exception table. The chained hash table
@@ -456,7 +439,7 @@
final long thrower; // use id not ref to avoid weak cycles
final int hashCode; // store task hashCode before weak ref disappears
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
- ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue) {
+ ReferenceQueue<Object> exceptionTableRefQueue) {
super(task, exceptionTableRefQueue);
this.ex = ex;
this.next = next;
@@ -492,7 +475,7 @@
} finally {
lock.unlock();
}
- s = abnormalCompletion(DONE | ABNORMAL | THROWN);
+ s = setCompletion(EXCEPTIONAL);
}
return s;
}
@@ -504,7 +487,7 @@
*/
private int setExceptionalCompletion(Throwable ex) {
int s = recordExceptionalCompletion(ex);
- if ((s & THROWN) != 0)
+ if ((s & DONE_MASK) == EXCEPTIONAL)
internalPropagateException(ex);
return s;
}
@@ -620,8 +603,9 @@
private static void expungeStaleExceptions() {
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
if (x instanceof ExceptionNode) {
+ int hashCode = ((ExceptionNode)x).hashCode;
ExceptionNode[] t = exceptionTable;
- int i = ((ExceptionNode)x).hashCode & (t.length - 1);
+ int i = hashCode & (t.length - 1);
ExceptionNode e = t[i];
ExceptionNode pred = null;
while (e != null) {
@@ -679,8 +663,10 @@
* Throws exception, if any, associated with the given status.
*/
private void reportException(int s) {
- rethrow((s & THROWN) != 0 ? getThrowableException() :
- new CancellationException());
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s == EXCEPTIONAL)
+ rethrow(getThrowableException());
}
// public methods
@@ -710,19 +696,19 @@
}
/**
- * Returns the result of the computation when it
- * {@linkplain #isDone is done}.
- * This method differs from {@link #get()} in that abnormal
- * completion results in {@code RuntimeException} or {@code Error},
- * not {@code ExecutionException}, and that interrupts of the
- * calling thread do <em>not</em> cause the method to abruptly
- * return by throwing {@code InterruptedException}.
+ * Returns the result of the computation when it {@link #isDone is
+ * done}. This method differs from {@link #get()} in that
+ * abnormal completion results in {@code RuntimeException} or
+ * {@code Error}, not {@code ExecutionException}, and that
+ * interrupts of the calling thread do <em>not</em> cause the
+ * method to abruptly return by throwing {@code
+ * InterruptedException}.
*
* @return the computed result
*/
public final V join() {
int s;
- if (((s = doJoin()) & ABNORMAL) != 0)
+ if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
return getRawResult();
}
@@ -737,7 +723,7 @@
*/
public final V invoke() {
int s;
- if (((s = doInvoke()) & ABNORMAL) != 0)
+ if ((s = doInvoke() & DONE_MASK) != NORMAL)
reportException(s);
return getRawResult();
}
@@ -762,9 +748,9 @@
public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
int s1, s2;
t2.fork();
- if (((s1 = t1.doInvoke()) & ABNORMAL) != 0)
+ if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL)
t1.reportException(s1);
- if (((s2 = t2.doJoin()) & ABNORMAL) != 0)
+ if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL)
t2.reportException(s2);
}
@@ -794,7 +780,7 @@
}
else if (i != 0)
t.fork();
- else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null)
+ else if (t.doInvoke() < NORMAL && ex == null)
ex = t.getException();
}
for (int i = 1; i <= last; ++i) {
@@ -802,7 +788,7 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if ((t.doJoin() & ABNORMAL) != 0)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
@@ -830,7 +816,7 @@
*/
public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) {
if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) {
- invokeAll(tasks.toArray(new ForkJoinTask<?>[0]));
+ invokeAll(tasks.toArray(new ForkJoinTask<?>[tasks.size()]));
return tasks;
}
@SuppressWarnings("unchecked")
@@ -846,7 +832,7 @@
}
else if (i != 0)
t.fork();
- else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null)
+ else if (t.doInvoke() < NORMAL && ex == null)
ex = t.getException();
}
for (int i = 1; i <= last; ++i) {
@@ -854,7 +840,7 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if ((t.doJoin() & ABNORMAL) != 0)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
@@ -891,8 +877,7 @@
* @return {@code true} if this task is now cancelled
*/
public boolean cancel(boolean mayInterruptIfRunning) {
- int s = abnormalCompletion(DONE | ABNORMAL);
- return (s & (ABNORMAL | THROWN)) == ABNORMAL;
+ return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED;
}
public final boolean isDone() {
@@ -900,7 +885,7 @@
}
public final boolean isCancelled() {
- return (status & (ABNORMAL | THROWN)) == ABNORMAL;
+ return (status & DONE_MASK) == CANCELLED;
}
/**
@@ -909,7 +894,7 @@
* @return {@code true} if this task threw an exception or was cancelled
*/
public final boolean isCompletedAbnormally() {
- return (status & ABNORMAL) != 0;
+ return status < NORMAL;
}
/**
@@ -920,7 +905,7 @@
* exception and was not cancelled
*/
public final boolean isCompletedNormally() {
- return (status & (DONE | ABNORMAL)) == DONE;
+ return (status & DONE_MASK) == NORMAL;
}
/**
@@ -931,9 +916,9 @@
* @return the exception, or {@code null} if none
*/
public final Throwable getException() {
- int s = status;
- return ((s & ABNORMAL) == 0 ? null :
- (s & THROWN) == 0 ? new CancellationException() :
+ int s = status & DONE_MASK;
+ return ((s >= NORMAL) ? null :
+ (s == CANCELLED) ? new CancellationException() :
getThrowableException());
}
@@ -977,7 +962,7 @@
setExceptionalCompletion(rex);
return;
}
- setDone();
+ setCompletion(NORMAL);
}
/**
@@ -989,7 +974,7 @@
* @since 1.8
*/
public final void quietlyComplete() {
- setDone();
+ setCompletion(NORMAL);
}
/**
@@ -1006,12 +991,11 @@
public final V get() throws InterruptedException, ExecutionException {
int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
doJoin() : externalInterruptibleAwaitDone();
- if ((s & THROWN) != 0)
- throw new ExecutionException(getThrowableException());
- else if ((s & ABNORMAL) != 0)
+ if ((s &= DONE_MASK) == CANCELLED)
throw new CancellationException();
- else
- return getRawResult();
+ if (s == EXCEPTIONAL)
+ throw new ExecutionException(getThrowableException());
+ return getRawResult();
}
/**
@@ -1051,7 +1035,7 @@
while ((s = status) >= 0 &&
(ns = deadline - System.nanoTime()) > 0L) {
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
- (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(ms); // OK to throw InterruptedException
@@ -1063,13 +1047,15 @@
}
}
if (s >= 0)
- throw new TimeoutException();
- else if ((s & THROWN) != 0)
+ s = status;
+ if ((s &= DONE_MASK) != NORMAL) {
+ if (s == CANCELLED)
+ throw new CancellationException();
+ if (s != EXCEPTIONAL)
+ throw new TimeoutException();
throw new ExecutionException(getThrowableException());
- else if ((s & ABNORMAL) != 0)
- throw new CancellationException();
- else
- return getRawResult();
+ }
+ return getRawResult();
}
/**
@@ -1125,7 +1111,7 @@
* setRawResult(null)}.
*/
public void reinitialize() {
- if ((status & THROWN) != 0)
+ if ((status & DONE_MASK) == EXCEPTIONAL)
clearExceptionalCompletion();
else
status = 0;
@@ -1343,8 +1329,8 @@
*/
public final short setForkJoinTaskTag(short newValue) {
for (int s;;) {
- if (STATUS.weakCompareAndSet(this, s = status,
- (s & ~SMASK) | (newValue & SMASK)))
+ if (U.compareAndSwapInt(this, STATUS, s = status,
+ (s & ~SMASK) | (newValue & SMASK)))
return (short)s;
}
}
@@ -1367,8 +1353,8 @@
for (int s;;) {
if ((short)(s = status) != expect)
return false;
- if (STATUS.weakCompareAndSet(this, s,
- (s & ~SMASK) | (update & SMASK)))
+ if (U.compareAndSwapInt(this, STATUS, s,
+ (s & ~SMASK) | (update & SMASK)))
return true;
}
}
@@ -1391,9 +1377,6 @@
public final void setRawResult(T v) { result = v; }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
- public String toString() {
- return super.toString() + "[Wrapped task = " + runnable + "]";
- }
private static final long serialVersionUID = 5232453952276885070L;
}
@@ -1411,9 +1394,6 @@
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
- public String toString() {
- return super.toString() + "[Wrapped task = " + runnable + "]";
- }
private static final long serialVersionUID = 5232453952276885070L;
}
@@ -1459,9 +1439,6 @@
}
}
public final void run() { invoke(); }
- public String toString() {
- return super.toString() + "[Wrapped task = " + callable + "]";
- }
private static final long serialVersionUID = 2838392045355241008L;
}
@@ -1538,14 +1515,19 @@
setExceptionalCompletion((Throwable)ex);
}
- // VarHandle mechanics
- private static final VarHandle STATUS;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATUS;
+
static {
+ exceptionTableLock = new ReentrantLock();
+ exceptionTableRefQueue = new ReferenceQueue<Object>();
+ exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATUS = l.findVarHandle(ForkJoinTask.class, "status", int.class);
+ STATUS = U.objectFieldOffset
+ (ForkJoinTask.class.getDeclaredField("status"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/ojluni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
index fa47c22..e98ba99 100644
--- a/ojluni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
+++ b/ojluni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -36,8 +36,6 @@
package java.util.concurrent;
import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
/**
@@ -49,9 +47,7 @@
* and termination methods surrounding the main task processing loop.
* If you do create such a subclass, you will also need to supply a
* custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to
- * {@linkplain ForkJoinPool#ForkJoinPool(int, ForkJoinWorkerThreadFactory,
- * UncaughtExceptionHandler, boolean, int, int, int, Predicate, long, TimeUnit)
- * use it} in a {@code ForkJoinPool}.
+ * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}.
*
* @since 1.7
* @author Doug Lea
@@ -70,9 +66,8 @@
* owning thread.
*
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
- * requires that we break quite a lot of encapsulation (via helper
- * methods in ThreadLocalRandom) both here and in the subclass to
- * access and set Thread fields.
+ * requires that we break quite a lot of encapsulation (via Unsafe)
+ * both here and in the subclass to access and set Thread fields.
*/
final ForkJoinPool pool; // the pool this thread works in
@@ -92,28 +87,13 @@
}
/**
- * Version for use by the default pool. Supports setting the
- * context class loader. This is a separate constructor to avoid
- * affecting the protected constructor.
- */
- ForkJoinWorkerThread(ForkJoinPool pool, ClassLoader ccl) {
- super("aForkJoinWorkerThread");
- super.setContextClassLoader(ccl);
- this.pool = pool;
- this.workQueue = pool.registerWorker(this);
- }
-
- /**
* Version for InnocuousForkJoinWorkerThread.
*/
- ForkJoinWorkerThread(ForkJoinPool pool,
- ClassLoader ccl,
- ThreadGroup threadGroup,
+ ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
AccessControlContext acc) {
super(threadGroup, null, "aForkJoinWorkerThread");
- super.setContextClassLoader(ccl);
- ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
- ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
+ U.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, acc);
+ eraseThreadLocals(); // clear before registering
this.pool = pool;
this.workQueue = pool.registerWorker(this);
}
@@ -191,44 +171,66 @@
}
/**
+ * Erases ThreadLocals by nulling out Thread maps.
+ */
+ final void eraseThreadLocals() {
+ U.putObject(this, THREADLOCALS, null);
+ U.putObject(this, INHERITABLETHREADLOCALS, null);
+ }
+
+ /**
* Non-public hook method for InnocuousForkJoinWorkerThread.
*/
void afterTopLevelExec() {
}
+ // Set up to allow setting thread fields in constructor
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long THREADLOCALS;
+ private static final long INHERITABLETHREADLOCALS;
+ private static final long INHERITEDACCESSCONTROLCONTEXT;
+ static {
+ try {
+ THREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocals"));
+ INHERITABLETHREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritableThreadLocals"));
+ INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritedAccessControlContext"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
/**
* A worker thread that has no permissions, is not a member of any
- * user-defined ThreadGroup, uses the system class loader as
- * thread context class loader, and erases all ThreadLocals after
+ * user-defined ThreadGroup, and erases all ThreadLocals after
* running each top-level task.
*/
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
private static final ThreadGroup innocuousThreadGroup =
- AccessController.doPrivileged(new PrivilegedAction<>() {
- public ThreadGroup run() {
- ThreadGroup group = Thread.currentThread().getThreadGroup();
- for (ThreadGroup p; (p = group.getParent()) != null; )
- group = p;
- return new ThreadGroup(
- group, "InnocuousForkJoinWorkerThreadGroup");
- }});
+ createThreadGroup();
/** An AccessControlContext supporting no privileges */
private static final AccessControlContext INNOCUOUS_ACC =
new AccessControlContext(
- new ProtectionDomain[] { new ProtectionDomain(null, null) });
+ new ProtectionDomain[] {
+ new ProtectionDomain(null, null)
+ });
InnocuousForkJoinWorkerThread(ForkJoinPool pool) {
- super(pool,
- ClassLoader.getSystemClassLoader(),
- innocuousThreadGroup,
- INNOCUOUS_ACC);
+ super(pool, innocuousThreadGroup, INNOCUOUS_ACC);
}
@Override // to erase ThreadLocals
void afterTopLevelExec() {
- ThreadLocalRandom.eraseThreadLocals(this);
+ eraseThreadLocals();
+ }
+
+ @Override // to always report system loader
+ public ClassLoader getContextClassLoader() {
+ return ClassLoader.getSystemClassLoader();
}
@Override // to silently fail
@@ -238,5 +240,34 @@
public void setContextClassLoader(ClassLoader cl) {
throw new SecurityException("setContextClassLoader");
}
+
+ /**
+ * Returns a new group with the system ThreadGroup (the
+ * topmost, parent-less group) as parent. Uses Unsafe to
+ * traverse Thread.group and ThreadGroup.parent fields.
+ */
+ private static ThreadGroup createThreadGroup() {
+ try {
+ sun.misc.Unsafe u = sun.misc.Unsafe.getUnsafe();
+ long tg = u.objectFieldOffset
+ (Thread.class.getDeclaredField("group"));
+ long gp = u.objectFieldOffset
+ (ThreadGroup.class.getDeclaredField("parent"));
+ ThreadGroup group = (ThreadGroup)
+ u.getObject(Thread.currentThread(), tg);
+ while (group != null) {
+ ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
+ if (parent == null)
+ return new ThreadGroup(group,
+ "InnocuousForkJoinWorkerThreadGroup");
+ group = parent;
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ // fall through if null as cannot-happen safeguard
+ throw new Error("Cannot create ThreadGroup");
+ }
}
+
}
diff --git a/ojluni/src/main/java/java/util/concurrent/Future.java b/ojluni/src/main/java/java/util/concurrent/Future.java
index 6099c7c..9bd05d6 100644
--- a/ojluni/src/main/java/java/util/concurrent/Future.java
+++ b/ojluni/src/main/java/java/util/concurrent/Future.java
@@ -50,7 +50,8 @@
* declare types of the form {@code Future<?>} and
* return {@code null} as a result of the underlying task.
*
- * <p><b>Sample Usage</b> (Note that the following classes are all
+ * <p>
+ * <b>Sample Usage</b> (Note that the following classes are all
* made-up.)
*
* <pre> {@code
@@ -58,9 +59,13 @@
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
- * void showSearch(String target) throws InterruptedException {
- * Callable<String> task = () -> searcher.search(target);
- * Future<String> future = executor.submit(task);
+ * void showSearch(final String target)
+ * throws InterruptedException {
+ * Future<String> future
+ * = executor.submit(new Callable<String>() {
+ * public String call() {
+ * return searcher.search(target);
+ * }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
@@ -72,7 +77,11 @@
* implements {@code Runnable}, and so may be executed by an {@code Executor}.
* For example, the above construction with {@code submit} could be replaced by:
* <pre> {@code
- * FutureTask<String> future = new FutureTask<>(task);
+ * FutureTask<String> future =
+ * new FutureTask<>(new Callable<String>() {
+ * public String call() {
+ * return searcher.search(target);
+ * }});
* executor.execute(future);}</pre>
*
* <p>Memory consistency effects: Actions taken by the asynchronous computation
diff --git a/ojluni/src/main/java/java/util/concurrent/FutureTask.java b/ojluni/src/main/java/java/util/concurrent/FutureTask.java
index e913ef3..62c2bfc 100644
--- a/ojluni/src/main/java/java/util/concurrent/FutureTask.java
+++ b/ojluni/src/main/java/java/util/concurrent/FutureTask.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
/**
@@ -71,6 +69,9 @@
* cancellation races. Sync control in the current design relies
* on a "state" field updated via CAS to track completion, along
* with a simple Treiber stack to hold waiting threads.
+ *
+ * Style note: As usual, we bypass overhead of using
+ * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
*/
/**
@@ -162,8 +163,9 @@
}
public boolean cancel(boolean mayInterruptIfRunning) {
- if (!(state == NEW && STATE.compareAndSet
- (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
+ if (!(state == NEW &&
+ U.compareAndSwapInt(this, STATE, NEW,
+ mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
@@ -172,7 +174,7 @@
if (t != null)
t.interrupt();
} finally { // final state
- STATE.setRelease(this, INTERRUPTED);
+ U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
@@ -226,9 +228,9 @@
* @param v the value
*/
protected void set(V v) {
- if (STATE.compareAndSet(this, NEW, COMPLETING)) {
+ if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
- STATE.setRelease(this, NORMAL); // final state
+ U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
@@ -244,16 +246,16 @@
* @param t the cause of failure
*/
protected void setException(Throwable t) {
- if (STATE.compareAndSet(this, NEW, COMPLETING)) {
+ if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = t;
- STATE.setRelease(this, EXCEPTIONAL); // final state
+ U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
finishCompletion();
}
}
public void run() {
if (state != NEW ||
- !RUNNER.compareAndSet(this, null, Thread.currentThread()))
+ !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
@@ -294,7 +296,7 @@
*/
protected boolean runAndReset() {
if (state != NEW ||
- !RUNNER.compareAndSet(this, null, Thread.currentThread()))
+ !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
@@ -361,7 +363,7 @@
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
- if (WAITERS.weakCompareAndSet(this, q, null)) {
+ if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
@@ -423,7 +425,8 @@
q = new WaitNode();
}
else if (!queued)
- queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
+ queued = U.compareAndSwapObject(this, WAITERS,
+ q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) { // first time
@@ -472,7 +475,7 @@
if (pred.thread == null) // check for race
continue retry;
}
- else if (!WAITERS.compareAndSet(this, q, s))
+ else if (!U.compareAndSwapObject(this, WAITERS, q, s))
continue retry;
}
break;
@@ -480,53 +483,21 @@
}
}
- /**
- * Returns a string representation of this FutureTask.
- *
- * @implSpec
- * The default implementation returns a string identifying this
- * FutureTask, as well as its completion state. The state, in
- * brackets, contains one of the strings {@code "Completed Normally"},
- * {@code "Completed Exceptionally"}, {@code "Cancelled"}, or {@code
- * "Not completed"}.
- *
- * @return a string representation of this FutureTask
- */
- public String toString() {
- final String status;
- switch (state) {
- case NORMAL:
- status = "[Completed normally]";
- break;
- case EXCEPTIONAL:
- status = "[Completed exceptionally: " + outcome + "]";
- break;
- case CANCELLED:
- case INTERRUPTING:
- case INTERRUPTED:
- status = "[Cancelled]";
- break;
- default:
- final Callable<?> callable = this.callable;
- status = (callable == null)
- ? "[Not completed]"
- : "[Not completed, task = " + callable + "]";
- }
- return super.toString() + status;
- }
-
- // VarHandle mechanics
- private static final VarHandle STATE;
- private static final VarHandle RUNNER;
- private static final VarHandle WAITERS;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long RUNNER;
+ private static final long WAITERS;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATE = l.findVarHandle(FutureTask.class, "state", int.class);
- RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
- WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
+ STATE = U.objectFieldOffset
+ (FutureTask.class.getDeclaredField("state"));
+ RUNNER = U.objectFieldOffset
+ (FutureTask.class.getDeclaredField("runner"));
+ WAITERS = U.objectFieldOffset
+ (FutureTask.class.getDeclaredField("waiters"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
// Reduce the risk of rare disastrous classloading in first call to
diff --git a/ojluni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java b/ojluni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
index f55998e..9829c9c 100644
--- a/ojluni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
+++ b/ojluni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
@@ -39,13 +39,15 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
@@ -64,12 +66,9 @@
* contains}, {@link #iterator iterator.remove()}, and the bulk
* operations, all of which run in linear time.
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
- *
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
* @since 1.6
* @author Doug Lea
@@ -195,7 +194,18 @@
*/
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
- addAll(c);
+ final ReentrantLock lock = this.lock;
+ lock.lock(); // Never contended, but necessary for visibility
+ try {
+ for (E e : c) {
+ if (e == null)
+ throw new NullPointerException();
+ if (!linkLast(new Node<E>(e)))
+ throw new IllegalStateException("Deque full");
+ }
+ } finally {
+ lock.unlock();
+ }
}
@@ -288,7 +298,6 @@
*/
void unlink(Node<E> x) {
// assert lock.isHeldByCurrentThread();
- // assert x.item != null;
Node<E> p = x.prev;
Node<E> n = x.next;
if (p == null) {
@@ -651,7 +660,7 @@
/**
* Retrieves and removes the head of the queue represented by this deque.
- * This method differs from {@link #poll() poll()} only in that it throws an
+ * This method differs from {@link #poll poll} only in that it throws an
* exception if this deque is empty.
*
* <p>This method is equivalent to {@link #removeFirst() removeFirst}.
@@ -677,7 +686,7 @@
/**
* Retrieves, but does not remove, the head of the queue represented by
- * this deque. This method differs from {@link #peek() peek()} only in that
+ * this deque. This method differs from {@link #peek peek} only in that
* it throws an exception if this deque is empty.
*
* <p>This method is equivalent to {@link #getFirst() getFirst}.
@@ -731,7 +740,8 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -824,65 +834,46 @@
}
}
- /**
- * Appends all of the elements in the specified collection to the end of
- * this deque, in the order that they are returned by the specified
- * collection's iterator. Attempts to {@code addAll} of a deque to
- * itself result in {@code IllegalArgumentException}.
+ /*
+ * TODO: Add support for more efficient bulk operations.
*
- * @param c the elements to be inserted into this deque
- * @return {@code true} if this deque changed as a result of the call
- * @throws NullPointerException if the specified collection or any
- * of its elements are null
- * @throws IllegalArgumentException if the collection is this deque
- * @throws IllegalStateException if this deque is full
- * @see #add(Object)
+ * We don't want to acquire the lock for every iteration, but we
+ * also want other threads a chance to interact with the
+ * collection, especially when count is close to capacity.
*/
- public boolean addAll(Collection<? extends E> c) {
- if (c == this)
- // As historically specified in AbstractQueue#addAll
- throw new IllegalArgumentException();
- // Copy c into a private chain of Nodes
- Node<E> beg = null, end = null;
- int n = 0;
- for (E e : c) {
- Objects.requireNonNull(e);
- n++;
- Node<E> newNode = new Node<E>(e);
- if (beg == null)
- beg = end = newNode;
- else {
- end.next = newNode;
- newNode.prev = end;
- end = newNode;
- }
- }
- if (beg == null)
- return false;
-
- // Atomically append the chain at the end
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count + n <= capacity) {
- beg.prev = last;
- if (first == null)
- first = beg;
- else
- last.next = beg;
- last = end;
- count += n;
- notEmpty.signalAll();
- return true;
- }
- } finally {
- lock.unlock();
- }
- // Fall back to historic non-atomic implementation, failing
- // with IllegalStateException when the capacity is exceeded.
- return super.addAll(c);
- }
+// /**
+// * Adds all of the elements in the specified collection to this
+// * queue. Attempts to addAll of a queue to itself result in
+// * {@code IllegalArgumentException}. Further, the behavior of
+// * this operation is undefined if the specified collection is
+// * modified while the operation is in progress.
+// *
+// * @param c collection containing elements to be added to this queue
+// * @return {@code true} if this queue changed as a result of the call
+// * @throws ClassCastException {@inheritDoc}
+// * @throws NullPointerException {@inheritDoc}
+// * @throws IllegalArgumentException {@inheritDoc}
+// * @throws IllegalStateException if this deque is full
+// * @see #add(Object)
+// */
+// public boolean addAll(Collection<? extends E> c) {
+// if (c == null)
+// throw new NullPointerException();
+// if (c == this)
+// throw new IllegalArgumentException();
+// final ReentrantLock lock = this.lock;
+// lock.lock();
+// try {
+// boolean modified = false;
+// for (E e : c)
+// if (linkLast(e))
+// modified = true;
+// return modified;
+// } finally {
+// lock.unlock();
+// }
+// }
/**
* Returns an array containing all of the elements in this deque, in
@@ -995,18 +986,6 @@
}
/**
- * Used for any element traversal that is not entirely under lock.
- * Such traversals must handle both:
- * - dequeued nodes (p.next == p)
- * - (possibly multiple) interior removed nodes (p.item == null)
- */
- Node<E> succ(Node<E> p) {
- if (p == (p = p.next))
- p = first;
- return p;
- }
-
- /**
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
@@ -1045,8 +1024,8 @@
/**
* nextItem holds on to item fields because once we claim that
* an element exists in hasNext(), we must return item read
- * under lock even if it was in the process of being removed
- * when hasNext() was called.
+ * under lock (in advance()) even if it was in the process of
+ * being removed when hasNext() was called.
*/
E nextItem;
@@ -1059,19 +1038,48 @@
abstract Node<E> firstNode();
abstract Node<E> nextNode(Node<E> n);
- private Node<E> succ(Node<E> p) {
- if (p == (p = nextNode(p)))
- p = firstNode();
- return p;
- }
-
AbstractItr() {
// set to initial position
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
- if ((next = firstNode()) != null)
- nextItem = next.item;
+ next = firstNode();
+ nextItem = (next == null) ? null : next.item;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the successor node of the given non-null, but
+ * possibly previously deleted, node.
+ */
+ private Node<E> succ(Node<E> n) {
+ // Chains of deleted nodes ending in null or self-links
+ // are possible if multiple interior nodes are removed.
+ for (;;) {
+ Node<E> s = nextNode(n);
+ if (s == null)
+ return null;
+ else if (s.item != null)
+ return s;
+ else if (s == n)
+ return firstNode();
+ else
+ n = s;
+ }
+ }
+
+ /**
+ * Advances next.
+ */
+ void advance() {
+ final ReentrantLock lock = LinkedBlockingDeque.this.lock;
+ lock.lock();
+ try {
+ // assert next != null;
+ next = succ(next);
+ nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
@@ -1082,65 +1090,14 @@
}
public E next() {
- Node<E> p;
- if ((p = next) == null)
+ if (next == null)
throw new NoSuchElementException();
- lastRet = p;
+ lastRet = next;
E x = nextItem;
- final ReentrantLock lock = LinkedBlockingDeque.this.lock;
- lock.lock();
- try {
- E e = null;
- for (p = nextNode(p); p != null && (e = p.item) == null; )
- p = succ(p);
- next = p;
- nextItem = e;
- } finally {
- lock.unlock();
- }
+ advance();
return x;
}
- public void forEachRemaining(Consumer<? super E> action) {
- // A variant of forEachFrom
- Objects.requireNonNull(action);
- Node<E> p;
- if ((p = next) == null) return;
- lastRet = p;
- next = null;
- final ReentrantLock lock = LinkedBlockingDeque.this.lock;
- final int batchSize = 64;
- Object[] es = null;
- int n, len = 1;
- do {
- lock.lock();
- try {
- if (es == null) {
- p = nextNode(p);
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == batchSize)
- break;
- es = new Object[len];
- es[0] = nextItem;
- nextItem = null;
- n = 1;
- } else
- n = 0;
- for (; p != null && n < len; p = succ(p))
- if ((es[n] = p.item) != null) {
- lastRet = p;
- n++;
- }
- } finally {
- lock.unlock();
- }
- for (int i = 0; i < n; i++) {
- @SuppressWarnings("unchecked") E e = (E) es[i];
- action.accept(e);
- }
- } while (n > 0 && p != null);
- }
-
public void remove() {
Node<E> n = lastRet;
if (n == null)
@@ -1159,49 +1116,51 @@
/** Forward iterator */
private class Itr extends AbstractItr {
- Itr() {} // prevent access constructor creation
Node<E> firstNode() { return first; }
Node<E> nextNode(Node<E> n) { return n.next; }
}
/** Descending iterator */
private class DescendingItr extends AbstractItr {
- DescendingItr() {} // prevent access constructor creation
Node<E> firstNode() { return last; }
Node<E> nextNode(Node<E> n) { return n.prev; }
}
- /**
- * A customized variant of Spliterators.IteratorSpliterator.
- * Keep this class in sync with (very similar) LBQSpliterator.
- */
- private final class LBDSpliterator implements Spliterator<E> {
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBDSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingDeque<E> queue;
Node<E> current; // current node; null until initialized
int batch; // batch size for splits
boolean exhausted; // true when no more nodes
- long est = size(); // size estimate
-
- LBDSpliterator() {}
+ long est; // size estimate
+ LBDSpliterator(LinkedBlockingDeque<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
public long estimateSize() { return est; }
public Spliterator<E> trySplit() {
Node<E> h;
+ final LinkedBlockingDeque<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
if (!exhausted &&
- ((h = current) != null || (h = first) != null)
- && h.next != null) {
- int n = batch = Math.min(batch + 1, MAX_BATCH);
+ ((h = current) != null || (h = q.first) != null) &&
+ h.next != null) {
Object[] a = new Object[n];
- final ReentrantLock lock = LinkedBlockingDeque.this.lock;
+ final ReentrantLock lock = q.lock;
int i = 0;
Node<E> p = current;
lock.lock();
try {
- if (p != null || (p = first) != null)
- for (; p != null && i < n; p = succ(p))
+ if (p != null || (p = q.first) != null) {
+ do {
if ((a[i] = p.item) != null)
- i++;
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
} finally {
lock.unlock();
}
@@ -1211,33 +1170,66 @@
}
else if ((est -= i) < 0L)
est = 0L;
- if (i > 0)
+ if (i > 0) {
+ batch = i;
return Spliterators.spliterator
(a, 0, i, (Spliterator.ORDERED |
Spliterator.NONNULL |
Spliterator.CONCURRENT));
+ }
}
return null;
}
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ lock.lock();
+ try {
+ if (p == null)
+ p = q.first;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
if (!exhausted) {
E e = null;
- final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
- Node<E> p;
- if ((p = current) != null || (p = first) != null)
- do {
- e = p.item;
- p = succ(p);
- } while (e == null && p != null);
- if ((current = p) == null)
- exhausted = true;
+ if (current == null)
+ current = q.first;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
} finally {
lock.unlock();
}
+ if (current == null)
+ exhausted = true;
if (e != null) {
action.accept(e);
return true;
@@ -1246,20 +1238,9 @@
return false;
}
- public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- if (!exhausted) {
- exhausted = true;
- Node<E> p = current;
- current = null;
- forEachFrom(action, p);
- }
- }
-
public int characteristics() {
- return (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT);
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
}
}
@@ -1280,127 +1261,7 @@
* @since 1.8
*/
public Spliterator<E> spliterator() {
- return new LBDSpliterator();
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- forEachFrom(action, null);
- }
-
- /**
- * Runs action on each element found during a traversal starting at p.
- * If p is null, traversal starts at head.
- */
- void forEachFrom(Consumer<? super E> action, Node<E> p) {
- // Extract batches of elements while holding the lock; then
- // run the action on the elements while not
- final ReentrantLock lock = this.lock;
- final int batchSize = 64; // max number of elements per batch
- Object[] es = null; // container for batch of elements
- int n, len = 0;
- do {
- lock.lock();
- try {
- if (es == null) {
- if (p == null) p = first;
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == batchSize)
- break;
- es = new Object[len];
- }
- for (n = 0; p != null && n < len; p = succ(p))
- if ((es[n] = p.item) != null)
- n++;
- } finally {
- lock.unlock();
- }
- for (int i = 0; i < n; i++) {
- @SuppressWarnings("unchecked") E e = (E) es[i];
- action.accept(e);
- }
- } while (n > 0 && p != null);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
-
- /** Implementation of bulk remove methods. */
- @SuppressWarnings("unchecked")
- private boolean bulkRemove(Predicate<? super E> filter) {
- boolean removed = false;
- final ReentrantLock lock = this.lock;
- Node<E> p = null;
- Node<E>[] nodes = null;
- int n, len = 0;
- do {
- // 1. Extract batch of up to 64 elements while holding the lock.
- lock.lock();
- try {
- if (nodes == null) { // first batch; initialize
- p = first;
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == 64)
- break;
- nodes = (Node<E>[]) new Node<?>[len];
- }
- for (n = 0; p != null && n < len; p = succ(p))
- nodes[n++] = p;
- } finally {
- lock.unlock();
- }
-
- // 2. Run the filter on the elements while lock is free.
- long deathRow = 0L; // "bitset" of size 64
- for (int i = 0; i < n; i++) {
- final E e;
- if ((e = nodes[i].item) != null && filter.test(e))
- deathRow |= 1L << i;
- }
-
- // 3. Remove any filtered elements while holding the lock.
- if (deathRow != 0) {
- lock.lock();
- try {
- for (int i = 0; i < n; i++) {
- final Node<E> q;
- if ((deathRow & (1L << i)) != 0L
- && (q = nodes[i]).item != null) {
- unlink(q);
- removed = true;
- }
- nodes[i] = null; // help GC
- }
- } finally {
- lock.unlock();
- }
- }
- } while (n > 0 && p != null);
- return removed;
+ return new LBDSpliterator<E>(this);
}
/**
@@ -1443,21 +1304,12 @@
last = null;
// Read in all elements and place in queue
for (;;) {
- @SuppressWarnings("unchecked") E item = (E)s.readObject();
+ @SuppressWarnings("unchecked")
+ E item = (E)s.readObject();
if (item == null)
break;
add(item);
}
}
- void checkInvariants() {
- // assert lock.isHeldByCurrentThread();
- // Nodes may get self-linked or lose their item, but only
- // after being unlinked and becoming unreachable from first.
- for (Node<E> p = first; p != null; p = p.next) {
- // assert p.next != p;
- // assert p.item != null;
- }
- }
-
}
diff --git a/ojluni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/ojluni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
index 4ba6c1e..cf2d447 100644
--- a/ojluni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
@@ -39,14 +39,16 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
@@ -67,12 +69,9 @@
* dynamically created upon each insertion unless this would bring the
* queue above capacity.
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
- *
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
* @since 1.5
* @author Doug Lea
@@ -235,6 +234,14 @@
putLock.unlock();
}
+// /**
+// * Tells whether both locks are held by current thread.
+// */
+// boolean isFullyLocked() {
+// return (putLock.isHeldByCurrentThread() &&
+// takeLock.isHeldByCurrentThread());
+// }
+
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
@@ -323,8 +330,10 @@
*/
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
- final int c;
- final Node<E> node = new Node<E>(e);
+ // Note: convention in all put/take/etc is to preset local var
+ // holding count negative to indicate failure unless set.
+ int c = -1;
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
@@ -365,7 +374,7 @@
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
- final int c;
+ int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
@@ -403,28 +412,28 @@
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
- final int c;
- final Node<E> node = new Node<E>(e);
+ int c = -1;
+ Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
- if (count.get() == capacity)
- return false;
- enqueue(node);
- c = count.getAndIncrement();
- if (c + 1 < capacity)
- notFull.signal();
+ if (count.get() < capacity) {
+ enqueue(node);
+ c = count.getAndIncrement();
+ if (c + 1 < capacity)
+ notFull.signal();
+ }
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
- return true;
+ return c >= 0;
}
public E take() throws InterruptedException {
- final E x;
- final int c;
+ E x;
+ int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
@@ -445,8 +454,8 @@
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
- final E x;
- final int c;
+ E x = null;
+ int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
@@ -473,17 +482,17 @@
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
- final E x;
- final int c;
+ E x = null;
+ int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
- if (count.get() == 0)
- return null;
- x = dequeue();
- c = count.getAndDecrement();
- if (c > 1)
- notEmpty.signal();
+ if (count.get() > 0) {
+ x = dequeue();
+ c = count.getAndDecrement();
+ if (c > 1)
+ notEmpty.signal();
+ }
} finally {
takeLock.unlock();
}
@@ -493,7 +502,6 @@
}
public E peek() {
- final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
@@ -506,17 +514,16 @@
}
/**
- * Unlinks interior Node p with predecessor pred.
+ * Unlinks interior Node p with predecessor trail.
*/
- void unlink(Node<E> p, Node<E> pred) {
- // assert putLock.isHeldByCurrentThread();
- // assert takeLock.isHeldByCurrentThread();
+ void unlink(Node<E> p, Node<E> trail) {
+ // assert isFullyLocked();
// p.next is not changed, to allow iterators that are
// traversing p to maintain their weak-consistency guarantee.
p.item = null;
- pred.next = p.next;
+ trail.next = p.next;
if (last == p)
- last = pred;
+ last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
@@ -536,11 +543,11 @@
if (o == null) return false;
fullyLock();
try {
- for (Node<E> pred = head, p = pred.next;
+ for (Node<E> trail = head, p = trail.next;
p != null;
- pred = p, p = p.next) {
+ trail = p, p = p.next) {
if (o.equals(p.item)) {
- unlink(p, pred);
+ unlink(p, trail);
return true;
}
}
@@ -694,7 +701,8 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -733,18 +741,6 @@
}
/**
- * Used for any element traversal that is not entirely under lock.
- * Such traversals must handle both:
- * - dequeued nodes (p.next == p)
- * - (possibly multiple) interior removed nodes (p.item == null)
- */
- Node<E> succ(Node<E> p) {
- if (p == (p = p.next))
- p = head.next;
- return p;
- }
-
- /**
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
@@ -757,103 +753,71 @@
return new Itr();
}
- /**
- * Weakly-consistent iterator.
- *
- * Lazily updated ancestor field provides expected O(1) remove(),
- * but still O(n) in the worst case, whenever the saved ancestor
- * is concurrently deleted.
- */
private class Itr implements Iterator<E> {
- private Node<E> next; // Node holding nextItem
- private E nextItem; // next item to hand out
+ /*
+ * Basic weakly-consistent iterator. At all times hold the next
+ * item to hand out so that if hasNext() reports true, we will
+ * still have it to return even if lost race with a take etc.
+ */
+
+ private Node<E> current;
private Node<E> lastRet;
- private Node<E> ancestor; // Helps unlink lastRet on remove()
+ private E currentElement;
Itr() {
fullyLock();
try {
- if ((next = head.next) != null)
- nextItem = next.item;
+ current = head.next;
+ if (current != null)
+ currentElement = current.item;
} finally {
fullyUnlock();
}
}
public boolean hasNext() {
- return next != null;
+ return current != null;
}
public E next() {
- Node<E> p;
- if ((p = next) == null)
- throw new NoSuchElementException();
- lastRet = p;
- E x = nextItem;
fullyLock();
try {
- E e = null;
- for (p = p.next; p != null && (e = p.item) == null; )
- p = succ(p);
- next = p;
- nextItem = e;
+ if (current == null)
+ throw new NoSuchElementException();
+ lastRet = current;
+ E item = null;
+ // Unlike other traversal methods, iterators must handle both:
+ // - dequeued nodes (p.next == p)
+ // - (possibly multiple) interior removed nodes (p.item == null)
+ for (Node<E> p = current, q;; p = q) {
+ if ((q = p.next) == p)
+ q = head.next;
+ if (q == null || (item = q.item) != null) {
+ current = q;
+ E x = currentElement;
+ currentElement = item;
+ return x;
+ }
+ }
} finally {
fullyUnlock();
}
- return x;
- }
-
- public void forEachRemaining(Consumer<? super E> action) {
- // A variant of forEachFrom
- Objects.requireNonNull(action);
- Node<E> p;
- if ((p = next) == null) return;
- lastRet = p;
- next = null;
- final int batchSize = 64;
- Object[] es = null;
- int n, len = 1;
- do {
- fullyLock();
- try {
- if (es == null) {
- p = p.next;
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == batchSize)
- break;
- es = new Object[len];
- es[0] = nextItem;
- nextItem = null;
- n = 1;
- } else
- n = 0;
- for (; p != null && n < len; p = succ(p))
- if ((es[n] = p.item) != null) {
- lastRet = p;
- n++;
- }
- } finally {
- fullyUnlock();
- }
- for (int i = 0; i < n; i++) {
- @SuppressWarnings("unchecked") E e = (E) es[i];
- action.accept(e);
- }
- } while (n > 0 && p != null);
}
public void remove() {
- Node<E> p = lastRet;
- if (p == null)
+ if (lastRet == null)
throw new IllegalStateException();
- lastRet = null;
fullyLock();
try {
- if (p.item != null) {
- if (ancestor == null)
- ancestor = head;
- ancestor = findPred(p, ancestor);
- unlink(p, ancestor);
+ Node<E> node = lastRet;
+ lastRet = null;
+ for (Node<E> trail = head, p = trail.next;
+ p != null;
+ trail = p, p = p.next) {
+ if (p == node) {
+ unlink(p, trail);
+ break;
+ }
}
} finally {
fullyUnlock();
@@ -861,38 +825,42 @@
}
}
- /**
- * A customized variant of Spliterators.IteratorSpliterator.
- * Keep this class in sync with (very similar) LBDSpliterator.
- */
- private final class LBQSpliterator implements Spliterator<E> {
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBQSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingQueue<E> queue;
Node<E> current; // current node; null until initialized
int batch; // batch size for splits
boolean exhausted; // true when no more nodes
- long est = size(); // size estimate
-
- LBQSpliterator() {}
+ long est; // size estimate
+ LBQSpliterator(LinkedBlockingQueue<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
public long estimateSize() { return est; }
public Spliterator<E> trySplit() {
Node<E> h;
+ final LinkedBlockingQueue<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
if (!exhausted &&
- ((h = current) != null || (h = head.next) != null)
- && h.next != null) {
- int n = batch = Math.min(batch + 1, MAX_BATCH);
+ ((h = current) != null || (h = q.head.next) != null) &&
+ h.next != null) {
Object[] a = new Object[n];
int i = 0;
Node<E> p = current;
- fullyLock();
+ q.fullyLock();
try {
- if (p != null || (p = head.next) != null)
- for (; p != null && i < n; p = succ(p))
+ if (p != null || (p = q.head.next) != null) {
+ do {
if ((a[i] = p.item) != null)
- i++;
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
} finally {
- fullyUnlock();
+ q.fullyUnlock();
}
if ((current = p) == null) {
est = 0L;
@@ -900,32 +868,64 @@
}
else if ((est -= i) < 0L)
est = 0L;
- if (i > 0)
+ if (i > 0) {
+ batch = i;
return Spliterators.spliterator
(a, 0, i, (Spliterator.ORDERED |
Spliterator.NONNULL |
Spliterator.CONCURRENT));
+ }
}
return null;
}
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ q.fullyLock();
+ try {
+ if (p == null)
+ p = q.head.next;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
if (!exhausted) {
E e = null;
- fullyLock();
+ q.fullyLock();
try {
- Node<E> p;
- if ((p = current) != null || (p = head.next) != null)
- do {
- e = p.item;
- p = succ(p);
- } while (e == null && p != null);
- if ((current = p) == null)
- exhausted = true;
+ if (current == null)
+ current = q.head.next;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
} finally {
- fullyUnlock();
+ q.fullyUnlock();
}
+ if (current == null)
+ exhausted = true;
if (e != null) {
action.accept(e);
return true;
@@ -934,20 +934,9 @@
return false;
}
- public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- if (!exhausted) {
- exhausted = true;
- Node<E> p = current;
- current = null;
- forEachFrom(action, p);
- }
- }
-
public int characteristics() {
- return (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT);
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
}
}
@@ -968,140 +957,7 @@
* @since 1.8
*/
public Spliterator<E> spliterator() {
- return new LBQSpliterator();
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- forEachFrom(action, null);
- }
-
- /**
- * Runs action on each element found during a traversal starting at p.
- * If p is null, traversal starts at head.
- */
- void forEachFrom(Consumer<? super E> action, Node<E> p) {
- // Extract batches of elements while holding the lock; then
- // run the action on the elements while not
- final int batchSize = 64; // max number of elements per batch
- Object[] es = null; // container for batch of elements
- int n, len = 0;
- do {
- fullyLock();
- try {
- if (es == null) {
- if (p == null) p = head.next;
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == batchSize)
- break;
- es = new Object[len];
- }
- for (n = 0; p != null && n < len; p = succ(p))
- if ((es[n] = p.item) != null)
- n++;
- } finally {
- fullyUnlock();
- }
- for (int i = 0; i < n; i++) {
- @SuppressWarnings("unchecked") E e = (E) es[i];
- action.accept(e);
- }
- } while (n > 0 && p != null);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
-
- /**
- * Returns the predecessor of live node p, given a node that was
- * once a live ancestor of p (or head); allows unlinking of p.
- */
- Node<E> findPred(Node<E> p, Node<E> ancestor) {
- // assert p.item != null;
- if (ancestor.item == null)
- ancestor = head;
- // Fails with NPE if precondition not satisfied
- for (Node<E> q; (q = ancestor.next) != p; )
- ancestor = q;
- return ancestor;
- }
-
- /** Implementation of bulk remove methods. */
- @SuppressWarnings("unchecked")
- private boolean bulkRemove(Predicate<? super E> filter) {
- boolean removed = false;
- Node<E> p = null, ancestor = head;
- Node<E>[] nodes = null;
- int n, len = 0;
- do {
- // 1. Extract batch of up to 64 elements while holding the lock.
- fullyLock();
- try {
- if (nodes == null) { // first batch; initialize
- p = head.next;
- for (Node<E> q = p; q != null; q = succ(q))
- if (q.item != null && ++len == 64)
- break;
- nodes = (Node<E>[]) new Node<?>[len];
- }
- for (n = 0; p != null && n < len; p = succ(p))
- nodes[n++] = p;
- } finally {
- fullyUnlock();
- }
-
- // 2. Run the filter on the elements while lock is free.
- long deathRow = 0L; // "bitset" of size 64
- for (int i = 0; i < n; i++) {
- final E e;
- if ((e = nodes[i].item) != null && filter.test(e))
- deathRow |= 1L << i;
- }
-
- // 3. Remove any filtered elements while holding the lock.
- if (deathRow != 0) {
- fullyLock();
- try {
- for (int i = 0; i < n; i++) {
- final Node<E> q;
- if ((deathRow & (1L << i)) != 0L
- && (q = nodes[i]).item != null) {
- ancestor = findPred(q, ancestor);
- unlink(q, ancestor);
- removed = true;
- }
- nodes[i] = null; // help GC
- }
- } finally {
- fullyUnlock();
- }
- }
- } while (n > 0 && p != null);
- return removed;
+ return new LBQSpliterator<E>(this);
}
/**
diff --git a/ojluni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/ojluni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
index 06d04e22..e282b42 100644
--- a/ojluni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
@@ -35,20 +35,20 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Queue;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
-import java.util.function.Predicate;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An unbounded {@link TransferQueue} based on linked nodes.
@@ -63,15 +63,16 @@
* asynchronous nature of these queues, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
+ * to be performed atomically. For example, an iterator operating
+ * concurrently with an {@code addAll} operation might view only some
+ * of the added elements.
*
- * <p>Bulk operations that add, remove, or examine multiple elements,
- * such as {@link #addAll}, {@link #removeIf} or {@link #forEach},
- * are <em>not</em> guaranteed to be performed atomically.
- * For example, a {@code forEach} traversal concurrent with an {@code
- * addAll} operation might observe only some of the added elements.
- *
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
* <p>Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
@@ -80,10 +81,6 @@
* actions subsequent to the access or removal of that element from
* the {@code LinkedTransferQueue} in another thread.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.7
* @author Doug Lea
* @param <E> the type of elements held in this queue
@@ -96,8 +93,8 @@
* *** Overview of Dual Queues with Slack ***
*
* Dual Queues, introduced by Scherer and Scott
- * (http://www.cs.rochester.edu/~scott/papers/2004_DISC_dual_DS.pdf)
- * are (linked) queues in which nodes may represent either data or
+ * (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf) are
+ * (linked) queues in which nodes may represent either data or
* requests. When a thread tries to enqueue a data node, but
* encounters a request node, it instead "matches" and removes it;
* and vice versa for enqueuing requests. Blocking Dual Queues
@@ -159,8 +156,9 @@
* correctly perform enqueue and dequeue operations by traversing
* from a pointer to the initial node; CASing the item of the
* first unmatched node on match and CASing the next field of the
- * trailing node on appends. While this would be a terrible idea
- * in itself, it does have the benefit of not requiring ANY atomic
+ * trailing node on appends. (Plus some special-casing when
+ * initially empty). While this would be a terrible idea in
+ * itself, it does have the benefit of not requiring ANY atomic
* updates on head/tail fields.
*
* We introduce here an approach that lies between the extremes of
@@ -196,15 +194,15 @@
* with a given probability per traversal step.
*
* In any strategy along these lines, because CASes updating
- * fields may fail, the actual slack may exceed targeted slack.
- * However, they may be retried at any time to maintain targets.
- * Even when using very small slack values, this approach works
- * well for dual queues because it allows all operations up to the
- * point of matching or appending an item (hence potentially
- * allowing progress by another thread) to be read-only, thus not
- * introducing any further contention. As described below, we
- * implement this by performing slack maintenance retries only
- * after these points.
+ * fields may fail, the actual slack may exceed targeted
+ * slack. However, they may be retried at any time to maintain
+ * targets. Even when using very small slack values, this
+ * approach works well for dual queues because it allows all
+ * operations up to the point of matching or appending an item
+ * (hence potentially allowing progress by another thread) to be
+ * read-only, thus not introducing any further contention. As
+ * described below, we implement this by performing slack
+ * maintenance retries only after these points.
*
* As an accompaniment to such techniques, traversal overhead can
* be further reduced without increasing contention of head
@@ -223,7 +221,7 @@
* (Similar issues arise in non-GC environments.) To cope with
* this in our implementation, upon CASing to advance the head
* pointer, we set the "next" link of the previous head to point
- * only to itself; thus limiting the length of chains of dead nodes.
+ * only to itself; thus limiting the length of connected dead lists.
* (We also take similar care to wipe out possibly garbage
* retaining values held in other Node fields.) However, doing so
* adds some further complexity to traversal: If any "next"
@@ -266,6 +264,15 @@
* interior nodes) except in the case of cancellation/removal (see
* below).
*
+ * We allow both the head and tail fields to be null before any
+ * nodes are enqueued; initializing upon first append. This
+ * simplifies some other logic, as well as providing more
+ * efficient explicit control paths instead of letting JVMs insert
+ * implicit NullPointerExceptions when they are null. While not
+ * currently fully implemented, we also leave open the possibility
+ * of re-nulling these fields when empty (which is complicated to
+ * arrange, for little benefit.)
+ *
* All enqueue/dequeue operations are handled by the single method
* "xfer" with parameters indicating whether to act as some form
* of offer, put, poll, take, or transfer (each possibly with
@@ -273,40 +280,44 @@
* method outweighs the code bulk and maintenance problems of
* using separate methods for each case.
*
- * Operation consists of up to two phases. The first is implemented
- * in method xfer, the second in method awaitMatch.
+ * Operation consists of up to three phases. The first is
+ * implemented within method xfer, the second in tryAppend, and
+ * the third in method awaitMatch.
*
- * 1. Traverse until matching or appending (method xfer)
+ * 1. Try to match an existing node
*
- * Conceptually, we simply traverse all nodes starting from head.
- * If we encounter an unmatched node of opposite mode, we match
- * it and return, also updating head (by at least 2 hops) to
- * one past the matched node (or the node itself if it's the
- * pinned trailing node). Traversals also check for the
- * possibility of falling off-list, in which case they restart.
+ * Starting at head, skip already-matched nodes until finding
+ * an unmatched node of opposite mode, if one exists, in which
+ * case matching it and returning, also if necessary updating
+ * head to one past the matched node (or the node itself if the
+ * list has no other unmatched nodes). If the CAS misses, then
+ * a loop retries advancing head by two steps until either
+ * success or the slack is at most two. By requiring that each
+ * attempt advances head by two (if applicable), we ensure that
+ * the slack does not grow without bound. Traversals also check
+ * if the initial head is now off-list, in which case they
+ * start at the new head.
*
- * If the trailing node of the list is reached, a match is not
- * possible. If this call was untimed poll or tryTransfer
- * (argument "how" is NOW), return empty-handed immediately.
- * Else a new node is CAS-appended. On successful append, if
- * this call was ASYNC (e.g. offer), an element was
- * successfully added to the end of the queue and we return.
+ * If no candidates are found and the call was untimed
+ * poll/offer, (argument "how" is NOW) return.
*
- * Of course, this naive traversal is O(n) when no match is
- * possible. We optimize the traversal by maintaining a tail
- * pointer, which is expected to be "near" the end of the list.
- * It is only safe to fast-forward to tail (in the presence of
- * arbitrary concurrent changes) if it is pointing to a node of
- * the same mode, even if it is dead (in this case no preceding
- * node could still be matchable by this traversal). If we
- * need to restart due to falling off-list, we can again
- * fast-forward to tail, but only if it has changed since the
- * last traversal (else we might loop forever). If tail cannot
- * be used, traversal starts at head (but in this case we
- * expect to be able to match near head). As with head, we
- * CAS-advance the tail pointer by at least two hops.
+ * 2. Try to append a new node (method tryAppend)
*
- * 2. Await match or cancellation (method awaitMatch)
+ * Starting at current tail pointer, find the actual last node
+ * and try to append a new node (or if head was null, establish
+ * the first node). Nodes can be appended only if their
+ * predecessors are either already matched or are of the same
+ * mode. If we detect otherwise, then a new node with opposite
+ * mode must have been appended during traversal, so we must
+ * restart at phase 1. The traversal and update steps are
+ * otherwise similar to phase 1: Retrying upon CAS misses and
+ * checking for staleness. In particular, if a self-link is
+ * encountered, then we can safely jump to a node on the list
+ * by continuing the traversal at current head.
+ *
+ * On successful append, if the call was ASYNC, return.
+ *
+ * 3. Await match or cancellation (method awaitMatch)
*
* Wait for another thread to match node; instead cancelling if
* the current thread was interrupted or the wait timed out. On
@@ -360,12 +371,12 @@
* from, the head of list.
*
* Without taking these into account, it would be possible for an
- * unbounded number of supposedly removed nodes to remain reachable.
- * Situations leading to such buildup are uncommon but can occur
- * in practice; for example when a series of short timed calls to
- * poll repeatedly time out at the trailing node but otherwise
- * never fall off the list because of an untimed call to take() at
- * the front of the queue.
+ * unbounded number of supposedly removed nodes to remain
+ * reachable. Situations leading to such buildup are uncommon but
+ * can occur in practice; for example when a series of short timed
+ * calls to poll repeatedly time out but never otherwise fall off
+ * the list because of an untimed call to take at the front of the
+ * queue.
*
* When these cases arise, rather than always retraversing the
* entire list to find an actual predecessor to unlink (which
@@ -378,9 +389,10 @@
* We perform sweeps by the thread hitting threshold (rather than
* background threads or by spreading work to other threads)
* because in the main contexts in which removal occurs, the
- * caller is timed-out or cancelled, which are not time-critical
- * enough to warrant the overhead that alternatives would impose
- * on other threads.
+ * caller is already timed-out, cancelled, or performing a
+ * potentially O(n) operation (e.g. remove(x)), none of which are
+ * time-critical enough to warrant the overhead that alternatives
+ * would impose on other threads.
*
* Because the sweepVotes estimate is conservative, and because
* nodes become unlinked "naturally" as they fall off the head of
@@ -392,13 +404,6 @@
* quiescent queues. The value defined below was chosen
* empirically to balance these under various timeout scenarios.
*
- * Because traversal operations on the linked list of nodes are a
- * natural opportunity to sweep dead nodes, we generally do so,
- * including all the operations that might remove elements as they
- * traverse, such as removeIf and Iterator.remove. This largely
- * eliminates long chains of dead interior nodes, except from
- * cancelled or timed out blocking operations.
- *
* Note that we cannot self-link unlinked interior nodes during
* sweeps. However, the associated garbage chains terminate when
* some successor ultimately falls off the head of the list and is
@@ -439,72 +444,55 @@
/**
* Queue nodes. Uses Object, not E, for items to allow forgetting
- * them after use. Writes that are intrinsically ordered wrt
- * other accesses or CASes use simple relaxed forms.
+ * them after use. Relies heavily on Unsafe mechanics to minimize
+ * unnecessary ordering constraints: Writes that are intrinsically
+ * ordered wrt other accesses or CASes use simple relaxed forms.
*/
static final class Node {
final boolean isData; // false if this is a request node
volatile Object item; // initially non-null if isData; CASed to match
volatile Node next;
- volatile Thread waiter; // null when not waiting for a match
+ volatile Thread waiter; // null until waiting
- /**
- * Constructs a data node holding item if item is non-null,
- * else a request node. Uses relaxed write because item can
- * only be seen after piggy-backing publication via CAS.
- */
- Node(Object item) {
- ITEM.set(this, item);
- isData = (item != null);
- }
-
- /** Constructs a (matched data) dummy node. */
- Node() {
- isData = true;
- }
-
+ // CAS methods for fields
final boolean casNext(Node cmp, Node val) {
- // assert val != null;
- return NEXT.compareAndSet(this, cmp, val);
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
}
final boolean casItem(Object cmp, Object val) {
- // assert isData == (cmp != null);
- // assert isData == (val == null);
- // assert !(cmp instanceof Node);
- return ITEM.compareAndSet(this, cmp, val);
+ // assert cmp == null || cmp.getClass() != Node.class;
+ return U.compareAndSwapObject(this, ITEM, cmp, val);
+ }
+
+ /**
+ * Constructs a new node. Uses relaxed write because item can
+ * only be seen after publication via casNext.
+ */
+ Node(Object item, boolean isData) {
+ U.putObject(this, ITEM, item); // relaxed write
+ this.isData = isData;
}
/**
* Links node to itself to avoid garbage retention. Called
* only after CASing head field, so uses relaxed write.
*/
- final void selfLink() {
- // assert isMatched();
- NEXT.setRelease(this, this);
- }
-
- final void appendRelaxed(Node next) {
- // assert next != null;
- // assert this.next == null;
- NEXT.set(this, next);
+ final void forgetNext() {
+ U.putObject(this, NEXT, this);
}
/**
- * Sets item (of a request node) to self and waiter to null,
- * to avoid garbage retention after matching or cancelling.
- * Uses relaxed writes because order is already constrained in
- * the only calling contexts: item is forgotten only after
- * volatile/atomic mechanics that extract items, and visitors
- * of request nodes only ever check whether item is null.
- * Similarly, clearing waiter follows either CAS or return
- * from park (if ever parked; else we don't care).
+ * Sets item to self and waiter to null, to avoid garbage
+ * retention after matching or cancelling. Uses relaxed writes
+ * because order is already constrained in the only calling
+ * contexts: item is forgotten only after volatile/atomic
+ * mechanics that extract items. Similarly, clearing waiter
+ * follows either CAS or return from park (if ever parked;
+ * else we don't care).
*/
final void forgetContents() {
- // assert isMatched();
- if (!isData)
- ITEM.set(this, this);
- WAITER.set(this, null);
+ U.putObject(this, ITEM, this);
+ U.putObject(this, WAITER, null);
}
/**
@@ -512,16 +500,15 @@
* case of artificial matches due to cancellation.
*/
final boolean isMatched() {
- return isData == (item == null);
+ Object x = item;
+ return (x == this) || ((x == null) == isData);
}
- /** Tries to CAS-match this node; if successful, wakes waiter. */
- final boolean tryMatch(Object cmp, Object val) {
- if (casItem(cmp, val)) {
- LockSupport.unpark(waiter);
- return true;
- }
- return false;
+ /**
+ * Returns true if this is an unmatched request node.
+ */
+ final boolean isUnmatchedRequest() {
+ return !isData && item == null;
}
/**
@@ -531,118 +518,69 @@
*/
final boolean cannotPrecede(boolean haveData) {
boolean d = isData;
- return d != haveData && d != (item == null);
+ Object x;
+ return d != haveData && (x = item) != this && (x != null) == d;
+ }
+
+ /**
+ * Tries to artificially match a data node -- used by remove.
+ */
+ final boolean tryMatchData() {
+ // assert isData;
+ Object x = item;
+ if (x != null && x != this && casItem(x, null)) {
+ LockSupport.unpark(waiter);
+ return true;
+ }
+ return false;
}
private static final long serialVersionUID = -3375979862319811754L;
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ITEM;
+ private static final long NEXT;
+ private static final long WAITER;
+ static {
+ try {
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ WAITER = U.objectFieldOffset
+ (Node.class.getDeclaredField("waiter"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
}
- /**
- * A node from which the first live (non-matched) node (if any)
- * can be reached in O(1) time.
- * Invariants:
- * - all live nodes are reachable from head via .next
- * - head != null
- * - (tmp = head).next != tmp || tmp != head
- * Non-invariants:
- * - head may or may not be live
- * - it is permitted for tail to lag behind head, that is, for tail
- * to not be reachable from head!
- */
+ /** head of the queue; null until first enqueue */
transient volatile Node head;
- /**
- * A node from which the last node on list (that is, the unique
- * node with node.next == null) can be reached in O(1) time.
- * Invariants:
- * - the last node is always reachable from tail via .next
- * - tail != null
- * Non-invariants:
- * - tail may or may not be live
- * - it is permitted for tail to lag behind head, that is, for tail
- * to not be reachable from head!
- * - tail.next may or may not be self-linked.
- */
+ /** tail of the queue; null until first append */
private transient volatile Node tail;
- /** The number of apparent failures to unsplice cancelled nodes */
+ /** The number of apparent failures to unsplice removed nodes */
private transient volatile int sweepVotes;
+ // CAS methods for fields
private boolean casTail(Node cmp, Node val) {
- // assert cmp != null;
- // assert val != null;
- return TAIL.compareAndSet(this, cmp, val);
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
private boolean casHead(Node cmp, Node val) {
- return HEAD.compareAndSet(this, cmp, val);
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
- /** Atomic version of ++sweepVotes. */
- private int incSweepVotes() {
- return (int) SWEEPVOTES.getAndAdd(this, 1) + 1;
+ private boolean casSweepVotes(int cmp, int val) {
+ return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
}
- /**
- * Tries to CAS pred.next (or head, if pred is null) from c to p.
- * Caller must ensure that we're not unlinking the trailing node.
+ /*
+ * Possible values for "how" argument in xfer method.
*/
- private boolean tryCasSuccessor(Node pred, Node c, Node p) {
- // assert p != null;
- // assert c.isData != (c.item != null);
- // assert c != p;
- if (pred != null)
- return pred.casNext(c, p);
- if (casHead(c, p)) {
- c.selfLink();
- return true;
- }
- return false;
- }
-
- /**
- * Collapses dead (matched) nodes between pred and q.
- * @param pred the last known live node, or null if none
- * @param c the first dead node
- * @param p the last dead node
- * @param q p.next: the next live node, or null if at end
- * @return pred if pred still alive and CAS succeeded; else p
- */
- private Node skipDeadNodes(Node pred, Node c, Node p, Node q) {
- // assert pred != c;
- // assert p != q;
- // assert c.isMatched();
- // assert p.isMatched();
- if (q == null) {
- // Never unlink trailing node.
- if (c == p) return pred;
- q = p;
- }
- return (tryCasSuccessor(pred, c, q)
- && (pred == null || !pred.isMatched()))
- ? pred : p;
- }
-
- /**
- * Collapses dead (matched) nodes from h (which was once head) to p.
- * Caller ensures all nodes from h up to and including p are dead.
- */
- private void skipDeadNodesNearHead(Node h, Node p) {
- // assert h != null;
- // assert h != p;
- // assert p.isMatched();
- for (;;) {
- final Node q;
- if ((q = p.next) == null) break;
- else if (!q.isMatched()) { p = q; break; }
- else if (p == (p = q)) return;
- }
- if (casHead(h, p))
- h.selfLink();
- }
-
- /* Possible values for "how" argument in xfer method. */
-
private static final int NOW = 0; // for untimed poll, tryTransfer
private static final int ASYNC = 1; // for offer, put, add
private static final int SYNC = 2; // for transfer, take
@@ -658,32 +596,84 @@
* @return an item if matched, else e
* @throws NullPointerException if haveData mode but e is null
*/
- @SuppressWarnings("unchecked")
private E xfer(E e, boolean haveData, int how, long nanos) {
if (haveData && (e == null))
throw new NullPointerException();
+ Node s = null; // the node to append, if needed
- restart: for (Node s = null, t = null, h = null;;) {
- for (Node p = (t != (t = tail) && t.isData == haveData) ? t
- : (h = head);; ) {
- final Node q; final Object item;
- if (p.isData != haveData
- && haveData == ((item = p.item) == null)) {
- if (h == null) h = head;
- if (p.tryMatch(item, e)) {
- if (h != p) skipDeadNodesNearHead(h, p);
- return (E) item;
+ retry:
+ for (;;) { // restart on append race
+
+ for (Node h = head, p = h; p != null;) { // find & match first node
+ boolean isData = p.isData;
+ Object item = p.item;
+ if (item != p && (item != null) == isData) { // unmatched
+ if (isData == haveData) // can't match
+ break;
+ if (p.casItem(item, e)) { // match
+ for (Node q = p; q != h;) {
+ Node n = q.next; // update by 2 unless singleton
+ if (head == h && casHead(h, n == null ? q : n)) {
+ h.forgetNext();
+ break;
+ } // advance and retry
+ if ((h = head) == null ||
+ (q = h.next) == null || !q.isMatched())
+ break; // unless slack < 2
+ }
+ LockSupport.unpark(p.waiter);
+ @SuppressWarnings("unchecked") E itemE = (E) item;
+ return itemE;
}
}
- if ((q = p.next) == null) {
- if (how == NOW) return e;
- if (s == null) s = new Node(e);
- if (!p.casNext(null, s)) continue;
- if (p != t) casTail(t, s);
- if (how == ASYNC) return e;
- return awaitMatch(s, p, e, (how == TIMED), nanos);
+ Node n = p.next;
+ p = (p != n) ? n : (h = head); // Use head if p offlist
+ }
+
+ if (how != NOW) { // No matches available
+ if (s == null)
+ s = new Node(e, haveData);
+ Node pred = tryAppend(s, haveData);
+ if (pred == null)
+ continue retry; // lost race vs opposite mode
+ if (how != ASYNC)
+ return awaitMatch(s, pred, e, (how == TIMED), nanos);
+ }
+ return e; // not waiting
+ }
+ }
+
+ /**
+ * Tries to append node s as tail.
+ *
+ * @param s the node to append
+ * @param haveData true if appending in data mode
+ * @return null on failure due to losing race with append in
+ * different mode, else s's predecessor, or s itself if no
+ * predecessor
+ */
+ private Node tryAppend(Node s, boolean haveData) {
+ for (Node t = tail, p = t;;) { // move p to last node and append
+ Node n, u; // temps for reads of next & tail
+ if (p == null && (p = head) == null) {
+ if (casHead(null, s))
+ return s; // initialize
+ }
+ else if (p.cannotPrecede(haveData))
+ return null; // lost race vs opposite mode
+ else if ((n = p.next) != null) // not last; keep traversing
+ p = p != t && t != (u = tail) ? (t = u) : // stale tail
+ (p != n) ? n : null; // restart if off list
+ else if (!p.casNext(null, s))
+ p = p.next; // re-read on CAS failure
+ else {
+ if (p != t) { // update if slack now >= 2
+ while ((tail != t || !casTail(t, s)) &&
+ (t = tail) != null &&
+ (s = t.next) != null && // advance and retry
+ (s = s.next) != null && s != t);
}
- if (p == (p = q)) continue restart;
+ return p;
}
}
}
@@ -692,9 +682,9 @@
* Spins/yields/blocks until node s is matched or caller gives up.
*
* @param s the waiting node
- * @param pred the predecessor of s, or null if unknown (the null
- * case does not occur in any current calls but may in possible
- * future extensions)
+ * @param pred the predecessor of s, or s itself if it has no
+ * predecessor, or null if unknown (the null case does not occur
+ * in any current calls but may in possible future extensions)
* @param e the comparison value for checking match
* @param timed if true, wait only until timeout elapses
* @param nanos timeout in nanosecs, used only if timed is true
@@ -707,20 +697,17 @@
ThreadLocalRandom randomYields = null; // bound if needed
for (;;) {
- final Object item;
- if ((item = s.item) != e) { // matched
+ Object item = s.item;
+ if (item != e) { // matched
// assert item != s;
s.forgetContents(); // avoid garbage
@SuppressWarnings("unchecked") E itemE = (E) item;
return itemE;
}
else if (w.isInterrupted() || (timed && nanos <= 0L)) {
- // try to cancel and unlink
- if (s.casItem(e, s.isData ? null : s)) {
- unsplice(pred, s);
+ unsplice(pred, s); // try to unlink and cancel
+ if (s.casItem(e, s)) // return normally if lost CAS
return e;
- }
- // return normally if lost CAS
}
else if (spins < 0) { // establish spins at/near front
if ((spins = spinsFor(pred, s.isData)) > 0)
@@ -764,32 +751,34 @@
/* -------------- Traversal methods -------------- */
/**
+ * Returns the successor of p, or the head node if p.next has been
+ * linked to self, which will only be true if traversing with a
+ * stale pointer that is now off the list.
+ */
+ final Node succ(Node p) {
+ Node next = p.next;
+ return (p == next) ? head : next;
+ }
+
+ /**
* Returns the first unmatched data node, or null if none.
- * Callers must recheck if the returned node is unmatched
- * before using.
+ * Callers must recheck if the returned node's item field is null
+ * or self-linked before using.
*/
final Node firstDataNode() {
- Node first = null;
restartFromHead: for (;;) {
- Node h = head, p = h;
- while (p != null) {
- if (p.item != null) {
- if (p.isData) {
- first = p;
- break;
- }
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p)
+ return p;
}
- else if (!p.isData)
+ else if (item == null)
break;
- final Node q;
- if ((q = p.next) == null)
- break;
- if (p == (p = q))
+ if (p == (p = p.next))
continue restartFromHead;
}
- if (p != h && casHead(h, p))
- h.selfLink();
- return first;
+ return null;
}
}
@@ -822,7 +811,7 @@
for (Node p = head; p != null;) {
Object item = p.item;
if (p.isData) {
- if (item != null) {
+ if (item != null && item != p) {
if (a == null)
a = new String[4];
else if (size == a.length)
@@ -851,7 +840,7 @@
for (Node p = head; p != null;) {
Object item = p.item;
if (p.isData) {
- if (item != null) {
+ if (item != null && item != p) {
if (x == null)
x = new Object[4];
else if (size == x.length)
@@ -930,50 +919,76 @@
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
- Objects.requireNonNull(a);
+ if (a == null) throw new NullPointerException();
return (T[]) toArrayInternal(a);
}
- /**
- * Weakly-consistent iterator.
- *
- * Lazily updated ancestor is expected to be amortized O(1) remove(),
- * but O(n) in the worst case, when lastRet is concurrently deleted.
- */
final class Itr implements Iterator<E> {
private Node nextNode; // next node to return item for
private E nextItem; // the corresponding item
private Node lastRet; // last returned node, to support remove
- private Node ancestor; // Helps unlink lastRet on remove()
+ private Node lastPred; // predecessor to unlink lastRet
/**
- * Moves to next node after pred, or first node if pred null.
+ * Moves to next node after prev, or first node if prev null.
*/
- @SuppressWarnings("unchecked")
- private void advance(Node pred) {
- for (Node p = (pred == null) ? head : pred.next, c = p;
- p != null; ) {
- final Object item;
- if ((item = p.item) != null && p.isData) {
- nextNode = p;
- nextItem = (E) item;
- if (c != p)
- tryCasSuccessor(pred, c, p);
- return;
- }
- else if (!p.isData && item == null)
- break;
- if (c != p && !tryCasSuccessor(pred, c, c = p)) {
- pred = p;
- c = p = p.next;
- }
- else if (p == (p = p.next)) {
- pred = null;
- c = p = head;
- }
+ private void advance(Node prev) {
+ /*
+ * To track and avoid buildup of deleted nodes in the face
+ * of calls to both Queue.remove and Itr.remove, we must
+ * include variants of unsplice and sweep upon each
+ * advance: Upon Itr.remove, we may need to catch up links
+ * from lastPred, and upon other removes, we might need to
+ * skip ahead from stale nodes and unsplice deleted ones
+ * found while advancing.
+ */
+
+ Node r, b; // reset lastPred upon possible deletion of lastRet
+ if ((r = lastRet) != null && !r.isMatched())
+ lastPred = r; // next lastPred is old lastRet
+ else if ((b = lastPred) == null || b.isMatched())
+ lastPred = null; // at start of list
+ else {
+ Node s, n; // help with removal of lastPred.next
+ while ((s = b.next) != null &&
+ s != b && s.isMatched() &&
+ (n = s.next) != null && n != s)
+ b.casNext(s, n);
}
- nextItem = null;
+
+ this.lastRet = prev;
+
+ for (Node p = prev, s, n;;) {
+ s = (p == null) ? head : p.next;
+ if (s == null)
+ break;
+ else if (s == p) {
+ p = null;
+ continue;
+ }
+ Object item = s.item;
+ if (s.isData) {
+ if (item != null && item != s) {
+ @SuppressWarnings("unchecked") E itemE = (E) item;
+ nextItem = itemE;
+ nextNode = s;
+ return;
+ }
+ }
+ else if (item == null)
+ break;
+ // assert s.isMatched();
+ if (p == null)
+ p = s;
+ else if ((n = s.next) == null)
+ break;
+ else if (s == n)
+ p = null;
+ else
+ p.casNext(s, n);
+ }
nextNode = null;
+ nextItem = null;
}
Itr() {
@@ -985,67 +1000,25 @@
}
public final E next() {
- final Node p;
- if ((p = nextNode) == null) throw new NoSuchElementException();
+ Node p = nextNode;
+ if (p == null) throw new NoSuchElementException();
E e = nextItem;
- advance(lastRet = p);
+ advance(p);
return e;
}
- public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- Node q = null;
- for (Node p; (p = nextNode) != null; advance(q = p))
- action.accept(nextItem);
- if (q != null)
- lastRet = q;
- }
-
public final void remove() {
final Node lastRet = this.lastRet;
if (lastRet == null)
throw new IllegalStateException();
this.lastRet = null;
- if (lastRet.item == null) // already deleted?
- return;
- // Advance ancestor, collapsing intervening dead nodes
- Node pred = ancestor;
- for (Node p = (pred == null) ? head : pred.next, c = p, q;
- p != null; ) {
- if (p == lastRet) {
- final Object item;
- if ((item = p.item) != null)
- p.tryMatch(item, null);
- if ((q = p.next) == null) q = p;
- if (c != q) tryCasSuccessor(pred, c, q);
- ancestor = pred;
- return;
- }
- final Object item; final boolean pAlive;
- if (pAlive = ((item = p.item) != null && p.isData)) {
- // exceptionally, nothing to do
- }
- else if (!p.isData && item == null)
- break;
- if ((c != p && !tryCasSuccessor(pred, c, c = p)) || pAlive) {
- pred = p;
- c = p = p.next;
- }
- else if (p == (p = p.next)) {
- pred = null;
- c = p = head;
- }
- }
- // traversal failed to find lastRet; must have been deleted;
- // leave ancestor at original location to avoid overshoot;
- // better luck next time!
-
- // assert lastRet.isMatched();
+ if (lastRet.tryMatchData())
+ unsplice(lastPred, lastRet);
}
}
/** A customized variant of Spliterators.IteratorSpliterator */
- final class LTQSpliterator implements Spliterator<E> {
+ final class LTQSpliterator<E> implements Spliterator<E> {
static final int MAX_BATCH = 1 << 25; // max batch array size;
Node current; // current node; null until initialized
int batch; // batch size for splits
@@ -1053,90 +1026,79 @@
LTQSpliterator() {}
public Spliterator<E> trySplit() {
- Node p, q;
- if ((p = current()) == null || (q = p.next) == null)
- return null;
- int i = 0, n = batch = Math.min(batch + 1, MAX_BATCH);
- Object[] a = null;
- do {
- final Object item = p.item;
- if (p.isData) {
- if (item != null) {
- if (a == null)
- a = new Object[n];
- a[i++] = item;
- }
- } else if (item == null) {
- p = null;
- break;
+ Node p;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null) &&
+ p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ Object e = p.item;
+ if (e != p && (a[i] = e) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = firstDataNode();
+ } while (p != null && i < n && p.isData);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
}
- if (p == (p = q))
- p = firstDataNode();
- } while (p != null && (q = p.next) != null && i < n);
- setCurrent(p);
- return (i == 0) ? null :
- Spliterators.spliterator(a, 0, i, (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT));
+ }
+ return null;
}
+ @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final Node p;
- if ((p = current()) != null) {
- current = null;
+ Node p;
+ if (action == null) throw new NullPointerException();
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null)) {
exhausted = true;
- forEachFrom(action, p);
+ do {
+ Object e = p.item;
+ if (e != null && e != p)
+ action.accept((E)e);
+ if (p == (p = p.next))
+ p = firstDataNode();
+ } while (p != null && p.isData);
}
}
@SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
Node p;
- if ((p = current()) != null) {
- E e = null;
+ if (action == null) throw new NullPointerException();
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null)) {
+ Object e;
do {
- final Object item = p.item;
- final boolean isData = p.isData;
+ if ((e = p.item) == p)
+ e = null;
if (p == (p = p.next))
- p = head;
- if (isData) {
- if (item != null) {
- e = (E) item;
- break;
- }
- }
- else if (item == null)
- p = null;
- } while (p != null);
- setCurrent(p);
+ p = firstDataNode();
+ } while (e == null && p != null && p.isData);
+ if ((current = p) == null)
+ exhausted = true;
if (e != null) {
- action.accept(e);
+ action.accept((E)e);
return true;
}
}
return false;
}
- private void setCurrent(Node p) {
- if ((current = p) == null)
- exhausted = true;
- }
-
- private Node current() {
- Node p;
- if ((p = current) == null && !exhausted)
- setCurrent(p = firstDataNode());
- return p;
- }
-
public long estimateSize() { return Long.MAX_VALUE; }
public int characteristics() {
- return (Spliterator.ORDERED |
- Spliterator.NONNULL |
- Spliterator.CONCURRENT);
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
}
}
@@ -1157,7 +1119,7 @@
* @since 1.8
*/
public Spliterator<E> spliterator() {
- return new LTQSpliterator();
+ return new LTQSpliterator<E>();
}
/* -------------- Removal methods -------------- */
@@ -1167,15 +1129,10 @@
* the given predecessor.
*
* @param pred a node that was at one time known to be the
- * predecessor of s
+ * predecessor of s, or null or s itself if s is/was at head
* @param s the node to be unspliced
*/
final void unsplice(Node pred, Node s) {
- // assert pred != null;
- // assert pred != s;
- // assert s != null;
- // assert s.isMatched();
- // assert (SWEEP_THRESHOLD & (SWEEP_THRESHOLD - 1)) == 0;
s.waiter = null; // disable signals
/*
* See above for rationale. Briefly: if pred still points to
@@ -1184,13 +1141,13 @@
* nor s are head or offlist, add to sweepVotes, and if enough
* votes have accumulated, sweep.
*/
- if (pred != null && pred.next == s) {
+ if (pred != null && pred != s && pred.next == s) {
Node n = s.next;
if (n == null ||
(n != s && pred.casNext(s, n) && pred.isMatched())) {
for (;;) { // check if at, or could be, head
Node h = head;
- if (h == pred || h == s)
+ if (h == pred || h == s || h == null)
return; // at head or list empty
if (!h.isMatched())
break;
@@ -1198,12 +1155,21 @@
if (hn == null)
return; // now empty
if (hn != h && casHead(h, hn))
- h.selfLink(); // advance head
+ h.forgetNext(); // advance head
}
- // sweep every SWEEP_THRESHOLD votes
- if (pred.next != pred && s.next != s // recheck if offlist
- && (incSweepVotes() & (SWEEP_THRESHOLD - 1)) == 0)
- sweep();
+ if (pred.next != pred && s.next != s) { // recheck if offlist
+ for (;;) { // sweep now if enough votes
+ int v = sweepVotes;
+ if (v < SWEEP_THRESHOLD) {
+ if (casSweepVotes(v, v + 1))
+ break;
+ }
+ else if (casSweepVotes(v, 0)) {
+ sweep();
+ break;
+ }
+ }
+ }
}
}
}
@@ -1228,10 +1194,35 @@
}
/**
+ * Main implementation of remove(Object)
+ */
+ private boolean findAndRemove(Object e) {
+ if (e != null) {
+ for (Node pred = null, p = head; p != null; ) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && e.equals(item) &&
+ p.tryMatchData()) {
+ unsplice(pred, p);
+ return true;
+ }
+ }
+ else if (item == null)
+ break;
+ pred = p;
+ if ((p = p.next) == pred) { // stale
+ pred = null;
+ p = head;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Creates an initially empty {@code LinkedTransferQueue}.
*/
public LinkedTransferQueue() {
- head = tail = new Node();
}
/**
@@ -1244,18 +1235,8 @@
* of its elements are null
*/
public LinkedTransferQueue(Collection<? extends E> c) {
- Node h = null, t = null;
- for (E e : c) {
- Node newNode = new Node(Objects.requireNonNull(e));
- if (h == null)
- h = t = newNode;
- else
- t.appendRelaxed(t = newNode);
- }
- if (h == null)
- h = t = new Node();
- head = h;
- tail = t;
+ this();
+ addAll(c);
}
/**
@@ -1274,7 +1255,8 @@
* return {@code false}.
*
* @return {@code true} (as specified by
- * {@link BlockingQueue#offer(Object,long,TimeUnit) BlockingQueue.offer})
+ * {@link java.util.concurrent.BlockingQueue#offer(Object,long,TimeUnit)
+ * BlockingQueue.offer})
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e, long timeout, TimeUnit unit) {
@@ -1386,12 +1368,15 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- for (E e; (e = poll()) != null; n++)
+ for (E e; (e = poll()) != null;) {
c.add(e);
+ ++n;
+ }
return n;
}
@@ -1400,12 +1385,15 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- for (E e; n < maxElements && (e = poll()) != null; n++)
+ for (E e; n < maxElements && (e = poll()) != null;) {
c.add(e);
+ ++n;
+ }
return n;
}
@@ -1427,7 +1415,7 @@
for (Node p = head; p != null;) {
Object item = p.item;
if (p.isData) {
- if (item != null) {
+ if (item != null && item != p) {
@SuppressWarnings("unchecked") E e = (E) item;
return e;
}
@@ -1455,7 +1443,7 @@
for (Node p = head; p != null;) {
Object item = p.item;
if (p.isData) {
- if (item != null)
+ if (item != null && item != p)
break;
}
else if (item == null)
@@ -1499,31 +1487,7 @@
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
- if (o == null) return false;
- restartFromHead: for (;;) {
- for (Node p = head, pred = null; p != null; ) {
- Node q = p.next;
- final Object item;
- if ((item = p.item) != null) {
- if (p.isData) {
- if (o.equals(item) && p.tryMatch(item, null)) {
- skipDeadNodes(pred, p, p, q);
- return true;
- }
- pred = p; p = q; continue;
- }
- }
- else if (!p.isData)
- break;
- for (Node c = p;; q = p.next) {
- if (q == null || !q.isMatched()) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) continue restartFromHead;
- }
- }
- return false;
- }
+ return findAndRemove(o);
}
/**
@@ -1535,29 +1499,18 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- if (o == null) return false;
- restartFromHead: for (;;) {
- for (Node p = head, pred = null; p != null; ) {
- Node q = p.next;
- final Object item;
- if ((item = p.item) != null) {
- if (p.isData) {
- if (o.equals(item))
- return true;
- pred = p; p = q; continue;
- }
+ if (o != null) {
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && o.equals(item))
+ return true;
}
- else if (!p.isData)
+ else if (item == null)
break;
- for (Node c = p;; q = p.next) {
- if (q == null || !q.isMatched()) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) continue restartFromHead;
- }
}
- return false;
}
+ return false;
}
/**
@@ -1565,7 +1518,8 @@
* {@code LinkedTransferQueue} is not capacity constrained.
*
* @return {@code Integer.MAX_VALUE} (as specified by
- * {@link BlockingQueue#remainingCapacity()})
+ * {@link java.util.concurrent.BlockingQueue#remainingCapacity()
+ * BlockingQueue.remainingCapacity})
*/
public int remainingCapacity() {
return Integer.MAX_VALUE;
@@ -1597,149 +1551,33 @@
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
-
- // Read in elements until trailing null sentinel found
- Node h = null, t = null;
- for (Object item; (item = s.readObject()) != null; ) {
- Node newNode = new Node(item);
- if (h == null)
- h = t = newNode;
- else
- t.appendRelaxed(t = newNode);
- }
- if (h == null)
- h = t = new Node();
- head = h;
- tail = t;
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
-
- public void clear() {
- bulkRemove(e -> true);
- }
-
- /**
- * Tolerate this many consecutive dead nodes before CAS-collapsing.
- * Amortized cost of clear() is (1 + 1/MAX_HOPS) CASes per element.
- */
- private static final int MAX_HOPS = 8;
-
- /** Implementation of bulk remove methods. */
- @SuppressWarnings("unchecked")
- private boolean bulkRemove(Predicate<? super E> filter) {
- boolean removed = false;
- restartFromHead: for (;;) {
- int hops = MAX_HOPS;
- // c will be CASed to collapse intervening dead nodes between
- // pred (or head if null) and p.
- for (Node p = head, c = p, pred = null, q; p != null; p = q) {
- q = p.next;
- final Object item; boolean pAlive;
- if (pAlive = ((item = p.item) != null && p.isData)) {
- if (filter.test((E) item)) {
- if (p.tryMatch(item, null))
- removed = true;
- pAlive = false;
- }
- }
- else if (!p.isData && item == null)
- break;
- if (pAlive || q == null || --hops == 0) {
- // p might already be self-linked here, but if so:
- // - CASing head will surely fail
- // - CASing pred's next will be useless but harmless.
- if ((c != p && !tryCasSuccessor(pred, c, c = p))
- || pAlive) {
- // if CAS failed or alive, abandon old pred
- hops = MAX_HOPS;
- pred = p;
- c = q;
- }
- } else if (p == q)
- continue restartFromHead;
- }
- return removed;
- }
- }
-
- /**
- * Runs action on each element found during a traversal starting at p.
- * If p is null, the action is not run.
- */
- @SuppressWarnings("unchecked")
- void forEachFrom(Consumer<? super E> action, Node p) {
- for (Node pred = null; p != null; ) {
- Node q = p.next;
- final Object item;
- if ((item = p.item) != null) {
- if (p.isData) {
- action.accept((E) item);
- pred = p; p = q; continue;
- }
- }
- else if (!p.isData)
+ s.defaultReadObject();
+ for (;;) {
+ @SuppressWarnings("unchecked")
+ E item = (E) s.readObject();
+ if (item == null)
break;
- for (Node c = p;; q = p.next) {
- if (q == null || !q.isMatched()) {
- pred = skipDeadNodes(pred, c, p, q); p = q; break;
- }
- if (p == (p = q)) { pred = null; p = head; break; }
- }
+ else
+ offer(item);
}
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- forEachFrom(action, head);
- }
+ // Unsafe mechanics
- // VarHandle mechanics
- private static final VarHandle HEAD;
- private static final VarHandle TAIL;
- private static final VarHandle SWEEPVOTES;
- static final VarHandle ITEM;
- static final VarHandle NEXT;
- static final VarHandle WAITER;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long SWEEPVOTES;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- HEAD = l.findVarHandle(LinkedTransferQueue.class, "head",
- Node.class);
- TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail",
- Node.class);
- SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes",
- int.class);
- ITEM = l.findVarHandle(Node.class, "item", Object.class);
- NEXT = l.findVarHandle(Node.class, "next", Node.class);
- WAITER = l.findVarHandle(Node.class, "waiter", Thread.class);
+ HEAD = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("tail"));
+ SWEEPVOTES = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
// Reduce the risk of rare disastrous classloading in first call to
diff --git a/ojluni/src/main/java/java/util/concurrent/Phaser.java b/ojluni/src/main/java/java/util/concurrent/Phaser.java
index d878e45..9ef9936 100644
--- a/ojluni/src/main/java/java/util/concurrent/Phaser.java
+++ b/ojluni/src/main/java/java/util/concurrent/Phaser.java
@@ -35,15 +35,14 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
/**
* A reusable synchronization barrier, similar in functionality to
- * {@link CyclicBarrier} and {@link CountDownLatch} but supporting
- * more flexible usage.
+ * {@link java.util.concurrent.CyclicBarrier CyclicBarrier} and
+ * {@link java.util.concurrent.CountDownLatch CountDownLatch}
+ * but supporting more flexible usage.
*
* <p><b>Registration.</b> Unlike the case for other barriers, the
* number of parties <em>registered</em> to synchronize on a phaser
@@ -153,46 +152,49 @@
* <p>A {@code Phaser} may be used instead of a {@code CountDownLatch}
* to control a one-shot action serving a variable number of parties.
* The typical idiom is for the method setting this up to first
- * register, then start all the actions, then deregister, as in:
+ * register, then start the actions, then deregister, as in:
*
* <pre> {@code
* void runTasks(List<Runnable> tasks) {
- * Phaser startingGate = new Phaser(1); // "1" to register self
+ * final Phaser phaser = new Phaser(1); // "1" to register self
* // create and start threads
- * for (Runnable task : tasks) {
- * startingGate.register();
- * new Thread(() -> {
- * startingGate.arriveAndAwaitAdvance();
- * task.run();
- * }).start();
+ * for (final Runnable task : tasks) {
+ * phaser.register();
+ * new Thread() {
+ * public void run() {
+ * phaser.arriveAndAwaitAdvance(); // await all creation
+ * task.run();
+ * }
+ * }.start();
* }
*
- * // deregister self to allow threads to proceed
- * startingGate.arriveAndDeregister();
+ * // allow threads to start and deregister self
+ * phaser.arriveAndDeregister();
* }}</pre>
*
* <p>One way to cause a set of threads to repeatedly perform actions
* for a given number of iterations is to override {@code onAdvance}:
*
* <pre> {@code
- * void startTasks(List<Runnable> tasks, int iterations) {
- * Phaser phaser = new Phaser() {
+ * void startTasks(List<Runnable> tasks, final int iterations) {
+ * final Phaser phaser = new Phaser() {
* protected boolean onAdvance(int phase, int registeredParties) {
- * return phase >= iterations - 1 || registeredParties == 0;
+ * return phase >= iterations || registeredParties == 0;
* }
* };
* phaser.register();
- * for (Runnable task : tasks) {
+ * for (final Runnable task : tasks) {
* phaser.register();
- * new Thread(() -> {
- * do {
- * task.run();
- * phaser.arriveAndAwaitAdvance();
- * } while (!phaser.isTerminated());
- * }).start();
+ * new Thread() {
+ * public void run() {
+ * do {
+ * task.run();
+ * phaser.arriveAndAwaitAdvance();
+ * } while (!phaser.isTerminated());
+ * }
+ * }.start();
* }
- * // allow threads to proceed; don't wait for them
- * phaser.arriveAndDeregister();
+ * phaser.arriveAndDeregister(); // deregister self, don't wait
* }}</pre>
*
* If the main task must later await termination, it
@@ -219,6 +221,7 @@
* phaser.arriveAndDeregister();
* }}</pre>
*
+ *
* <p>To create a set of {@code n} tasks using a tree of phasers, you
* could use code of the following form, assuming a Task class with a
* constructor accepting a {@code Phaser} that it registers with upon
@@ -345,6 +348,10 @@
private final AtomicReference<QNode> evenQ;
private final AtomicReference<QNode> oddQ;
+ private AtomicReference<QNode> queueFor(int phase) {
+ return ((phase & 1) == 0) ? evenQ : oddQ;
+ }
+
/**
* Returns message string for bounds exceptions on arrival.
*/
@@ -381,7 +388,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (STATE.compareAndSet(this, s, s-=adjust)) {
+ if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
if (unarrived == 1) {
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
@@ -394,12 +401,12 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- STATE.compareAndSet(this, s, n);
+ U.compareAndSwapLong(this, STATE, s, n);
releaseWaiters(phase);
}
else if (nextUnarrived == 0) { // propagate deregistration
phase = parent.doArrive(ONE_DEREGISTER);
- STATE.compareAndSet(this, s, s | EMPTY);
+ U.compareAndSwapLong(this, STATE, s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);
@@ -434,13 +441,13 @@
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
- else if (STATE.compareAndSet(this, s, s + adjust))
+ else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
long next = ((long)phase << PHASE_SHIFT) | adjust;
- if (STATE.compareAndSet(this, s, next))
+ if (U.compareAndSwapLong(this, STATE, s, next))
break;
}
else {
@@ -452,8 +459,8 @@
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
- while (!STATE.weakCompareAndSet
- (this, s,
+ while (!U.compareAndSwapLong
+ (this, STATE, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
@@ -484,8 +491,8 @@
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
(int)(s >>> PHASE_SHIFT) &&
- !STATE.weakCompareAndSet
- (this, s,
+ !U.compareAndSwapLong
+ (this, STATE, s,
s = (((long)phase << PHASE_SHIFT) |
((phase < 0) ? (s & COUNTS_MASK) :
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
@@ -674,7 +681,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) {
+ if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
if (unarrived > 1)
return root.internalAwaitAdvance(phase, null);
if (root != this)
@@ -689,7 +696,7 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- if (!STATE.compareAndSet(this, s, n))
+ if (!U.compareAndSwapLong(this, STATE, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
releaseWaiters(phase);
return nextPhase;
@@ -805,7 +812,7 @@
final Phaser root = this.root;
long s;
while ((s = root.state) >= 0) {
- if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) {
+ if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
// signal all threads
releaseWaiters(0); // Waiters on evenQ
releaseWaiters(1); // Waiters on oddQ
@@ -1040,8 +1047,6 @@
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
- else
- Thread.onSpinWait();
}
else if (node.isReleasable()) // done or aborted
break;
@@ -1130,14 +1135,16 @@
}
}
- // VarHandle mechanics
- private static final VarHandle STATE;
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- STATE = l.findVarHandle(Phaser.class, "state", long.class);
+ STATE = U.objectFieldOffset
+ (Phaser.class.getDeclaredField("state"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
// Reduce the risk of rare disastrous classloading in first call to
diff --git a/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index 03a17e5..644de86 100644
--- a/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -35,15 +35,12 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.SortedSet;
@@ -51,8 +48,10 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
-import java.util.function.Predicate;
-import jdk.internal.misc.SharedSecrets;
+
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
/**
* An unbounded {@linkplain BlockingQueue blocking queue} that uses
@@ -65,15 +64,15 @@
* non-comparable objects (doing so results in
* {@code ClassCastException}).
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
- * The Iterator provided in method {@link #iterator()} and the
- * Spliterator provided in method {@link #spliterator()} are <em>not</em>
- * guaranteed to traverse the elements of the PriorityBlockingQueue in
- * any particular order. If you need ordered traversal, consider using
- * {@code Arrays.sort(pq.toArray())}. Also, method {@code drainTo} can
- * be used to <em>remove</em> some or all elements in priority order and
- * place them in another collection.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the PriorityBlockingQueue in any particular order. If you need
+ * ordered traversal, consider using
+ * {@code Arrays.sort(pq.toArray())}. Also, method {@code drainTo}
+ * can be used to <em>remove</em> some or all elements in priority
+ * order and place them in another collection.
*
* <p>Operations on this class make no guarantees about the ordering
* of elements with equal priority. If you need to enforce an
@@ -102,10 +101,6 @@
* }
* }}</pre>
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.5
* @author Doug Lea
* @param <E> the type of elements held in this queue
@@ -168,12 +163,12 @@
/**
* Lock used for all public operations.
*/
- private final ReentrantLock lock = new ReentrantLock();
+ private final ReentrantLock lock;
/**
* Condition for blocking when empty.
*/
- private final Condition notEmpty = lock.newCondition();
+ private final Condition notEmpty;
/**
* Spinlock for allocation, acquired via CAS.
@@ -225,8 +220,10 @@
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
+ this.lock = new ReentrantLock();
+ this.notEmpty = lock.newCondition();
this.comparator = comparator;
- this.queue = new Object[Math.max(1, initialCapacity)];
+ this.queue = new Object[initialCapacity];
}
/**
@@ -246,6 +243,8 @@
* of its elements are null
*/
public PriorityBlockingQueue(Collection<? extends E> c) {
+ this.lock = new ReentrantLock();
+ this.notEmpty = lock.newCondition();
boolean heapify = true; // true if not known to be in heap order
boolean screen = true; // true if must screen for nulls
if (c instanceof SortedSet<?>) {
@@ -261,27 +260,22 @@
if (pq.getClass() == PriorityBlockingQueue.class) // exact match
heapify = false;
}
- Object[] es = c.toArray();
- int n = es.length;
+ Object[] a = c.toArray();
+ int n = a.length;
// If c.toArray incorrectly doesn't return Object[], copy it.
- if (es.getClass() != Object[].class)
- es = Arrays.copyOf(es, n, Object[].class);
+ if (a.getClass() != Object[].class)
+ a = Arrays.copyOf(a, n, Object[].class);
if (screen && (n == 1 || this.comparator != null)) {
- for (Object e : es)
- if (e == null)
+ for (int i = 0; i < n; ++i)
+ if (a[i] == null)
throw new NullPointerException();
}
- this.queue = ensureNonEmpty(es);
+ this.queue = a;
this.size = n;
if (heapify)
heapify();
}
- /** Ensures that queue[0] exists, helping peek() and poll(). */
- private static Object[] ensureNonEmpty(Object[] es) {
- return (es.length > 0) ? es : new Object[1];
- }
-
/**
* Tries to grow array to accommodate at least one more element
* (but normally expand by about 50%), giving up (allowing retry)
@@ -295,7 +289,7 @@
lock.unlock(); // must release and then re-acquire main lock
Object[] newArray = null;
if (allocationSpinLock == 0 &&
- ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
+ U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
@@ -325,23 +319,22 @@
* Mechanics for poll(). Call only while holding lock.
*/
private E dequeue() {
- // assert lock.isHeldByCurrentThread();
- final Object[] es;
- final E result;
-
- if ((result = (E) ((es = queue)[0])) != null) {
- final int n;
- final E x = (E) es[(n = --size)];
- es[n] = null;
- if (n > 0) {
- final Comparator<? super E> cmp;
- if ((cmp = comparator) == null)
- siftDownComparable(0, x, es, n);
- else
- siftDownUsingComparator(0, x, es, n, cmp);
- }
+ int n = size - 1;
+ if (n < 0)
+ return null;
+ else {
+ Object[] array = queue;
+ E result = (E) array[0];
+ E x = (E) array[n];
+ array[n] = null;
+ Comparator<? super E> cmp = comparator;
+ if (cmp == null)
+ siftDownComparable(0, x, array, n);
+ else
+ siftDownUsingComparator(0, x, array, n, cmp);
+ size = n;
+ return result;
}
- return result;
}
/**
@@ -349,38 +342,40 @@
* promoting x up the tree until it is greater than or equal to
* its parent, or is the root.
*
- * To simplify and speed up coercions and comparisons, the
+ * To simplify and speed up coercions and comparisons. the
* Comparable and Comparator versions are separated into different
* methods that are otherwise identical. (Similarly for siftDown.)
+ * These methods are static, with heap state as arguments, to
+ * simplify use in light of possible comparator exceptions.
*
* @param k the position to fill
* @param x the item to insert
- * @param es the heap array
+ * @param array the heap array
*/
- private static <T> void siftUpComparable(int k, T x, Object[] es) {
+ private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
- Object e = es[parent];
+ Object e = array[parent];
if (key.compareTo((T) e) >= 0)
break;
- es[k] = e;
+ array[k] = e;
k = parent;
}
- es[k] = key;
+ array[k] = key;
}
- private static <T> void siftUpUsingComparator(
- int k, T x, Object[] es, Comparator<? super T> cmp) {
+ private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
+ Comparator<? super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;
- Object e = es[parent];
+ Object e = array[parent];
if (cmp.compare(x, (T) e) >= 0)
break;
- es[k] = e;
+ array[k] = e;
k = parent;
}
- es[k] = x;
+ array[k] = x;
}
/**
@@ -390,61 +385,67 @@
*
* @param k the position to fill
* @param x the item to insert
- * @param es the heap array
+ * @param array the heap array
* @param n heap size
*/
- private static <T> void siftDownComparable(int k, T x, Object[] es, int n) {
- // assert n > 0;
- Comparable<? super T> key = (Comparable<? super T>)x;
- int half = n >>> 1; // loop while a non-leaf
- while (k < half) {
- int child = (k << 1) + 1; // assume left child is least
- Object c = es[child];
- int right = child + 1;
- if (right < n &&
- ((Comparable<? super T>) c).compareTo((T) es[right]) > 0)
- c = es[child = right];
- if (key.compareTo((T) c) <= 0)
- break;
- es[k] = c;
- k = child;
+ private static <T> void siftDownComparable(int k, T x, Object[] array,
+ int n) {
+ if (n > 0) {
+ Comparable<? super T> key = (Comparable<? super T>)x;
+ int half = n >>> 1; // loop while a non-leaf
+ while (k < half) {
+ int child = (k << 1) + 1; // assume left child is least
+ Object c = array[child];
+ int right = child + 1;
+ if (right < n &&
+ ((Comparable<? super T>) c).compareTo((T) array[right]) > 0)
+ c = array[child = right];
+ if (key.compareTo((T) c) <= 0)
+ break;
+ array[k] = c;
+ k = child;
+ }
+ array[k] = key;
}
- es[k] = key;
}
- private static <T> void siftDownUsingComparator(
- int k, T x, Object[] es, int n, Comparator<? super T> cmp) {
- // assert n > 0;
- int half = n >>> 1;
- while (k < half) {
- int child = (k << 1) + 1;
- Object c = es[child];
- int right = child + 1;
- if (right < n && cmp.compare((T) c, (T) es[right]) > 0)
- c = es[child = right];
- if (cmp.compare(x, (T) c) <= 0)
- break;
- es[k] = c;
- k = child;
+ private static <T> void siftDownUsingComparator(int k, T x, Object[] array,
+ int n,
+ Comparator<? super T> cmp) {
+ if (n > 0) {
+ int half = n >>> 1;
+ while (k < half) {
+ int child = (k << 1) + 1;
+ Object c = array[child];
+ int right = child + 1;
+ if (right < n && cmp.compare((T) c, (T) array[right]) > 0)
+ c = array[child = right];
+ if (cmp.compare(x, (T) c) <= 0)
+ break;
+ array[k] = c;
+ k = child;
+ }
+ array[k] = x;
}
- es[k] = x;
}
/**
* Establishes the heap invariant (described above) in the entire tree,
* assuming nothing about the order of the elements prior to the call.
- * This classic algorithm due to Floyd (1964) is known to be O(size).
*/
private void heapify() {
- final Object[] es = queue;
- int n = size, i = (n >>> 1) - 1;
- final Comparator<? super E> cmp;
- if ((cmp = comparator) == null)
- for (; i >= 0; i--)
- siftDownComparable(i, (E) es[i], es, n);
- else
- for (; i >= 0; i--)
- siftDownUsingComparator(i, (E) es[i], es, n, cmp);
+ Object[] array = queue;
+ int n = size;
+ int half = (n >>> 1) - 1;
+ Comparator<? super E> cmp = comparator;
+ if (cmp == null) {
+ for (int i = half; i >= 0; i--)
+ siftDownComparable(i, (E) array[i], array, n);
+ }
+ else {
+ for (int i = half; i >= 0; i--)
+ siftDownUsingComparator(i, (E) array[i], array, n, cmp);
+ }
}
/**
@@ -478,15 +479,15 @@
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
- Object[] es;
- while ((n = size) >= (cap = (es = queue).length))
- tryGrow(es, cap);
+ Object[] array;
+ while ((n = size) >= (cap = (array = queue).length))
+ tryGrow(array, cap);
try {
- final Comparator<? super E> cmp;
- if ((cmp = comparator) == null)
- siftUpComparable(n, e, es);
+ Comparator<? super E> cmp = comparator;
+ if (cmp == null)
+ siftUpComparable(n, e, array);
else
- siftUpUsingComparator(n, e, es, cmp);
+ siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();
} finally {
@@ -569,7 +570,7 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return (E) queue[0];
+ return (size == 0) ? null : (E) queue[0];
} finally {
lock.unlock();
}
@@ -609,9 +610,10 @@
private int indexOf(Object o) {
if (o != null) {
- final Object[] es = queue;
- for (int i = 0, n = size; i < n; i++)
- if (o.equals(es[i]))
+ Object[] array = queue;
+ int n = size;
+ for (int i = 0; i < n; i++)
+ if (o.equals(array[i]))
return i;
}
return -1;
@@ -621,23 +623,23 @@
* Removes the ith element from queue.
*/
private void removeAt(int i) {
- final Object[] es = queue;
- final int n = size - 1;
+ Object[] array = queue;
+ int n = size - 1;
if (n == i) // removed last element
- es[i] = null;
+ array[i] = null;
else {
- E moved = (E) es[n];
- es[n] = null;
- final Comparator<? super E> cmp;
- if ((cmp = comparator) == null)
- siftDownComparable(i, moved, es, n);
+ E moved = (E) array[n];
+ array[n] = null;
+ Comparator<? super E> cmp = comparator;
+ if (cmp == null)
+ siftDownComparable(i, moved, array, n);
else
- siftDownUsingComparator(i, moved, es, n, cmp);
- if (es[i] == moved) {
+ siftDownUsingComparator(i, moved, array, n, cmp);
+ if (array[i] == moved) {
if (cmp == null)
- siftUpComparable(i, moved, es);
+ siftUpComparable(i, moved, array);
else
- siftUpUsingComparator(i, moved, es, cmp);
+ siftUpUsingComparator(i, moved, array, cmp);
}
}
size = n;
@@ -670,16 +672,14 @@
/**
* Identity-based version for use in Itr.remove.
- *
- * @param o element to be removed from this queue, if present
*/
- void removeEq(Object o) {
+ void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- final Object[] es = queue;
+ Object[] array = queue;
for (int i = 0, n = size; i < n; i++) {
- if (o == es[i]) {
+ if (o == array[i]) {
removeAt(i);
break;
}
@@ -728,7 +728,8 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -755,10 +756,11 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- final Object[] es = queue;
- for (int i = 0, n = size; i < n; i++)
- es[i] = null;
+ Object[] array = queue;
+ int n = size;
size = 0;
+ for (int i = 0; i < n; i++)
+ array[i] = null;
} finally {
lock.unlock();
}
@@ -859,9 +861,10 @@
final class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
int cursor; // index of next element to return
- int lastRet = -1; // index of last element, or -1 if no such
+ int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
+ lastRet = -1;
this.array = array;
}
@@ -872,28 +875,16 @@
public E next() {
if (cursor >= array.length)
throw new NoSuchElementException();
- return (E)array[lastRet = cursor++];
+ lastRet = cursor;
+ return (E)array[cursor++];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
- removeEq(array[lastRet]);
+ removeEQ(array[lastRet]);
lastRet = -1;
}
-
- public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final Object[] es = array;
- int i;
- if ((i = cursor) < es.length) {
- lastRet = -1;
- cursor = es.length;
- for (; i < es.length; i++)
- action.accept((E) es[i]);
- lastRet = es.length - 1;
- }
- }
}
/**
@@ -931,9 +922,7 @@
throws java.io.IOException, ClassNotFoundException {
try {
s.defaultReadObject();
- int sz = q.size();
- SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, sz);
- this.queue = new Object[Math.max(1, sz)];
+ this.queue = new Object[q.size()];
comparator = q.comparator();
addAll(q);
} finally {
@@ -941,65 +930,68 @@
}
}
- /**
- * Immutable snapshot spliterator that binds to elements "late".
- */
- final class PBQSpliterator implements Spliterator<E> {
- Object[] array; // null until late-bound-initialized
+ // Similar to Collections.ArraySnapshotSpliterator but avoids
+ // commitment to toArray until needed
+ static final class PBQSpliterator<E> implements Spliterator<E> {
+ final PriorityBlockingQueue<E> queue;
+ Object[] array;
int index;
int fence;
- PBQSpliterator() {}
-
- PBQSpliterator(Object[] array, int index, int fence) {
+ PBQSpliterator(PriorityBlockingQueue<E> queue, Object[] array,
+ int index, int fence) {
+ this.queue = queue;
this.array = array;
this.index = index;
this.fence = fence;
}
- private int getFence() {
- if (array == null)
- fence = (array = toArray()).length;
- return fence;
+ final int getFence() {
+ int hi;
+ if ((hi = fence) < 0)
+ hi = fence = (array = queue.toArray()).length;
+ return hi;
}
- public PBQSpliterator trySplit() {
+ public PBQSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
- new PBQSpliterator(array, lo, index = mid);
+ new PBQSpliterator<E>(queue, array, lo, index = mid);
}
+ @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final int hi = getFence(), lo = index;
- final Object[] es = array;
- index = hi; // ensure exhaustion
- for (int i = lo; i < hi; i++)
- action.accept((E) es[i]);
+ Object[] a; int i, hi; // hoist accesses and checks from loop
+ if (action == null)
+ throw new NullPointerException();
+ if ((a = array) == null)
+ fence = (a = queue.toArray()).length;
+ if ((hi = fence) <= a.length &&
+ (i = index) >= 0 && i < (index = hi)) {
+ do { action.accept((E)a[i]); } while (++i < hi);
+ }
}
public boolean tryAdvance(Consumer<? super E> action) {
- Objects.requireNonNull(action);
+ if (action == null)
+ throw new NullPointerException();
if (getFence() > index && index >= 0) {
- action.accept((E) array[index++]);
+ @SuppressWarnings("unchecked") E e = (E) array[index++];
+ action.accept(e);
return true;
}
return false;
}
- public long estimateSize() { return getFence() - index; }
+ public long estimateSize() { return (long)(getFence() - index); }
public int characteristics() {
- return (Spliterator.NONNULL |
- Spliterator.SIZED |
- Spliterator.SUBSIZED);
+ return Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
/**
* Returns a {@link Spliterator} over the elements in this queue.
- * The spliterator does not traverse elements in any particular order
- * (the {@link Spliterator#ORDERED ORDERED} characteristic is not reported).
*
* <p>The returned spliterator is
* <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
@@ -1014,106 +1006,18 @@
* @since 1.8
*/
public Spliterator<E> spliterator() {
- return new PBQSpliterator();
+ return new PBQSpliterator<E>(this, null, 0, -1);
}
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- return bulkRemove(filter);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean removeAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> c.contains(e));
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public boolean retainAll(Collection<?> c) {
- Objects.requireNonNull(c);
- return bulkRemove(e -> !c.contains(e));
- }
-
- // A tiny bit set implementation
-
- private static long[] nBits(int n) {
- return new long[((n - 1) >> 6) + 1];
- }
- private static void setBit(long[] bits, int i) {
- bits[i >> 6] |= 1L << i;
- }
- private static boolean isClear(long[] bits, int i) {
- return (bits[i >> 6] & (1L << i)) == 0;
- }
-
- /** Implementation of bulk remove methods. */
- private boolean bulkRemove(Predicate<? super E> filter) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- final Object[] es = queue;
- final int end = size;
- int i;
- // Optimize for initial run of survivors
- for (i = 0; i < end && !filter.test((E) es[i]); i++)
- ;
- if (i >= end)
- return false;
- // Tolerate predicates that reentrantly access the
- // collection for read, so traverse once to find elements
- // to delete, a second pass to physically expunge.
- final int beg = i;
- final long[] deathRow = nBits(end - beg);
- deathRow[0] = 1L; // set bit 0
- for (i = beg + 1; i < end; i++)
- if (filter.test((E) es[i]))
- setBit(deathRow, i - beg);
- int w = beg;
- for (i = beg; i < end; i++)
- if (isClear(deathRow, i - beg))
- es[w++] = es[i];
- for (i = size = w; i < end; i++)
- es[i] = null;
- heapify();
- return true;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- */
- public void forEach(Consumer<? super E> action) {
- Objects.requireNonNull(action);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- final Object[] es = queue;
- for (int i = 0, n = size; i < n; i++)
- action.accept((E) es[i]);
- } finally {
- lock.unlock();
- }
- }
-
- // VarHandle mechanics
- private static final VarHandle ALLOCATIONSPINLOCK;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ALLOCATIONSPINLOCK;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class,
- "allocationSpinLock",
- int.class);
+ ALLOCATIONSPINLOCK = U.objectFieldOffset
+ (PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index be75ff4..6b9fcc3 100644
--- a/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -77,11 +77,14 @@
* Executors.newScheduledThreadPool(1);
*
* public void beepForAnHour() {
- * Runnable beeper = () -> System.out.println("beep");
- * ScheduledFuture<?> beeperHandle =
+ * final Runnable beeper = new Runnable() {
+ * public void run() { System.out.println("beep"); }
+ * };
+ * final ScheduledFuture<?< beeperHandle =
* scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
- * Runnable canceller = () -> beeperHandle.cancel(false);
- * scheduler.schedule(canceller, 1, HOURS);
+ * scheduler.schedule(new Runnable() {
+ * public void run() { beeperHandle.cancel(true); }
+ * }, 60 * 60, SECONDS);
* }
* }}</pre>
*
@@ -91,7 +94,8 @@
public interface ScheduledExecutorService extends ExecutorService {
/**
- * Submits a one-shot task that becomes enabled after the given delay.
+ * Creates and executes a one-shot action that becomes enabled
+ * after the given delay.
*
* @param command the task to execute
* @param delay the time from now to delay execution
@@ -101,14 +105,14 @@
* {@code null} upon completion
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
- * @throws NullPointerException if command or unit is null
+ * @throws NullPointerException if command is null
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
- * Submits a value-returning one-shot task that becomes enabled
- * after the given delay.
+ * Creates and executes a ScheduledFuture that becomes enabled after the
+ * given delay.
*
* @param callable the function to execute
* @param delay the time from now to delay execution
@@ -117,15 +121,15 @@
* @return a ScheduledFuture that can be used to extract result or cancel
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
- * @throws NullPointerException if callable or unit is null
+ * @throws NullPointerException if callable is null
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
- * Submits a periodic action that becomes enabled first after the
- * given initial delay, and subsequently with the given period;
- * that is, executions will commence after
+ * Creates and executes a periodic action that becomes enabled first
+ * after the given initial delay, and subsequently with the given
+ * period; that is, executions will commence after
* {@code initialDelay}, then {@code initialDelay + period}, then
* {@code initialDelay + 2 * period}, and so on.
*
@@ -136,8 +140,8 @@
* via the returned future.
* <li>The executor terminates, also resulting in task cancellation.
* <li>An execution of the task throws an exception. In this case
- * calling {@link Future#get() get} on the returned future will throw
- * {@link ExecutionException}, holding the exception as its cause.
+ * calling {@link Future#get() get} on the returned future will
+ * throw {@link ExecutionException}.
* </ul>
* Subsequent executions are suppressed. Subsequent calls to
* {@link Future#isDone isDone()} on the returned future will
@@ -158,7 +162,7 @@
* abnormal termination of a task execution.
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
- * @throws NullPointerException if command or unit is null
+ * @throws NullPointerException if command is null
* @throws IllegalArgumentException if period less than or equal to zero
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
@@ -167,10 +171,10 @@
TimeUnit unit);
/**
- * Submits a periodic action that becomes enabled first after the
- * given initial delay, and subsequently with the given delay
- * between the termination of one execution and the commencement of
- * the next.
+ * Creates and executes a periodic action that becomes enabled first
+ * after the given initial delay, and subsequently with the
+ * given delay between the termination of one execution and the
+ * commencement of the next.
*
* <p>The sequence of task executions continues indefinitely until
* one of the following exceptional completions occur:
@@ -179,8 +183,8 @@
* via the returned future.
* <li>The executor terminates, also resulting in task cancellation.
* <li>An execution of the task throws an exception. In this case
- * calling {@link Future#get() get} on the returned future will throw
- * {@link ExecutionException}, holding the exception as its cause.
+ * calling {@link Future#get() get} on the returned future will
+ * throw {@link ExecutionException}.
* </ul>
* Subsequent executions are suppressed. Subsequent calls to
* {@link Future#isDone isDone()} on the returned future will
@@ -198,7 +202,7 @@
* abnormal termination of a task execution.
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
- * @throws NullPointerException if command or unit is null
+ * @throws NullPointerException if command is null
* @throws IllegalArgumentException if delay less than or equal to zero
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
diff --git a/ojluni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/ojluni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index 6aba89a..4249872 100644
--- a/ojluni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/ojluni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -44,7 +44,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -90,11 +89,6 @@
* use {@code allowCoreThreadTimeOut} because this may leave the pool
* without threads to handle tasks once they become eligible to run.
*
- * <p>As with {@code ThreadPoolExecutor}, if not otherwise specified,
- * this class uses {@link Executors#defaultThreadFactory} as the
- * default thread factory, and {@link ThreadPoolExecutor.AbortPolicy}
- * as the default rejected execution handler.
- *
* <p><b>Extension notes:</b> This class overrides the
* {@link ThreadPoolExecutor#execute(Runnable) execute} and
* {@link AbstractExecutorService#submit(Runnable) submit}
@@ -169,7 +163,7 @@
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
/**
- * False if should cancel non-periodic not-yet-expired tasks on shutdown.
+ * False if should cancel non-periodic tasks on shutdown.
*/
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
@@ -300,9 +294,10 @@
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
- if (!canRunInCurrentRunState(this))
+ boolean periodic = isPeriodic();
+ if (!canRunInCurrentRunState(periodic))
cancel(false);
- else if (!isPeriodic())
+ else if (!periodic)
super.run();
else if (super.runAndReset()) {
setNextRunTime();
@@ -312,18 +307,15 @@
}
/**
- * Returns true if can run a task given current run state and
- * run-after-shutdown parameters.
+ * Returns true if can run a task given current run state
+ * and run-after-shutdown parameters.
+ *
+ * @param periodic true if this task periodic, false if delayed
*/
- boolean canRunInCurrentRunState(RunnableScheduledFuture<?> task) {
- if (!isShutdown())
- return true;
- if (isStopped())
- return false;
- return task.isPeriodic()
- ? continueExistingPeriodicTasksAfterShutdown
- : (executeExistingDelayedTasksAfterShutdown
- || task.getDelay(NANOSECONDS) <= 0);
+ boolean canRunInCurrentRunState(boolean periodic) {
+ return isRunningOrShutdown(periodic ?
+ continueExistingPeriodicTasksAfterShutdown :
+ executeExistingDelayedTasksAfterShutdown);
}
/**
@@ -342,7 +334,9 @@
reject(task);
else {
super.getQueue().add(task);
- if (!canRunInCurrentRunState(task) && remove(task))
+ if (isShutdown() &&
+ !canRunInCurrentRunState(task.isPeriodic()) &&
+ remove(task))
task.cancel(false);
else
ensurePrestart();
@@ -356,14 +350,13 @@
* @param task the task
*/
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
- if (canRunInCurrentRunState(task)) {
+ if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
- if (canRunInCurrentRunState(task) || !remove(task)) {
+ if (!canRunInCurrentRunState(true) && remove(task))
+ task.cancel(false);
+ else
ensurePrestart();
- return;
- }
}
- task.cancel(false);
}
/**
@@ -376,18 +369,23 @@
getExecuteExistingDelayedTasksAfterShutdownPolicy();
boolean keepPeriodic =
getContinueExistingPeriodicTasksAfterShutdownPolicy();
- // Traverse snapshot to avoid iterator exceptions
- // TODO: implement and use efficient removeIf
- // super.getQueue().removeIf(...);
- for (Object e : q.toArray()) {
- if (e instanceof RunnableScheduledFuture) {
- RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>)e;
- if ((t.isPeriodic()
- ? !keepPeriodic
- : (!keepDelayed && t.getDelay(NANOSECONDS) > 0))
- || t.isCancelled()) { // also remove if already cancelled
- if (q.remove(t))
- t.cancel(false);
+ if (!keepDelayed && !keepPeriodic) {
+ for (Object e : q.toArray())
+ if (e instanceof RunnableScheduledFuture<?>)
+ ((RunnableScheduledFuture<?>) e).cancel(false);
+ q.clear();
+ }
+ else {
+ // Traverse snapshot to avoid iterator exceptions
+ for (Object e : q.toArray()) {
+ if (e instanceof RunnableScheduledFuture) {
+ RunnableScheduledFuture<?> t =
+ (RunnableScheduledFuture<?>)e;
+ if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
+ t.isCancelled()) { // also remove if already cancelled
+ if (q.remove(t))
+ t.cancel(false);
+ }
}
}
}
@@ -583,34 +581,6 @@
}
/**
- * Submits a periodic action that becomes enabled first after the
- * given initial delay, and subsequently with the given period;
- * that is, executions will commence after
- * {@code initialDelay}, then {@code initialDelay + period}, then
- * {@code initialDelay + 2 * period}, and so on.
- *
- * <p>The sequence of task executions continues indefinitely until
- * one of the following exceptional completions occur:
- * <ul>
- * <li>The task is {@linkplain Future#cancel explicitly cancelled}
- * via the returned future.
- * <li>Method {@link #shutdown} is called and the {@linkplain
- * #getContinueExistingPeriodicTasksAfterShutdownPolicy policy on
- * whether to continue after shutdown} is not set true, or method
- * {@link #shutdownNow} is called; also resulting in task
- * cancellation.
- * <li>An execution of the task throws an exception. In this case
- * calling {@link Future#get() get} on the returned future will throw
- * {@link ExecutionException}, holding the exception as its cause.
- * </ul>
- * Subsequent executions are suppressed. Subsequent calls to
- * {@link Future#isDone isDone()} on the returned future will
- * return {@code true}.
- *
- * <p>If any execution of this task takes longer than its period, then
- * subsequent executions may start late, but will not concurrently
- * execute.
- *
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
@@ -636,29 +606,6 @@
}
/**
- * Submits a periodic action that becomes enabled first after the
- * given initial delay, and subsequently with the given delay
- * between the termination of one execution and the commencement of
- * the next.
- *
- * <p>The sequence of task executions continues indefinitely until
- * one of the following exceptional completions occur:
- * <ul>
- * <li>The task is {@linkplain Future#cancel explicitly cancelled}
- * via the returned future.
- * <li>Method {@link #shutdown} is called and the {@linkplain
- * #getContinueExistingPeriodicTasksAfterShutdownPolicy policy on
- * whether to continue after shutdown} is not set true, or method
- * {@link #shutdownNow} is called; also resulting in task
- * cancellation.
- * <li>An execution of the task throws an exception. In this case
- * calling {@link Future#get() get} on the returned future will throw
- * {@link ExecutionException}, holding the exception as its cause.
- * </ul>
- * Subsequent executions are suppressed. Subsequent calls to
- * {@link Future#isDone isDone()} on the returned future will
- * return {@code true}.
- *
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
@@ -736,8 +683,9 @@
/**
* Sets the policy on whether to continue executing existing
* periodic tasks even when this executor has been {@code shutdown}.
- * In this case, executions will continue until {@code shutdownNow}
- * or the policy is set to {@code false} when already shutdown.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow} or after setting the policy to
+ * {@code false} when already shutdown.
* This value is by default {@code false}.
*
* @param value if {@code true}, continue after shutdown, else don't
@@ -752,8 +700,9 @@
/**
* Gets the policy on whether to continue executing existing
* periodic tasks even when this executor has been {@code shutdown}.
- * In this case, executions will continue until {@code shutdownNow}
- * or the policy is set to {@code false} when already shutdown.
+ * In this case, these tasks will only terminate upon
+ * {@code shutdownNow} or after setting the policy to
+ * {@code false} when already shutdown.
* This value is by default {@code false}.
*
* @return {@code true} if will continue after shutdown
@@ -956,7 +905,7 @@
/**
* Sets f's heapIndex if it is a ScheduledFutureTask.
*/
- private static void setIndex(RunnableScheduledFuture<?> f, int idx) {
+ private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask)f).heapIndex = idx;
}
@@ -1254,12 +1203,41 @@
}
}
+ /**
+ * Returns first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private RunnableScheduledFuture<?> peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ RunnableScheduledFuture<?> first = queue[0];
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
+ }
+
public int drainTo(Collection<? super Runnable> c) {
- return drainTo(c, Integer.MAX_VALUE);
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ RunnableScheduledFuture<?> first;
+ int n = 0;
+ while ((first = peekExpired()) != null) {
+ c.add(first); // In this order, in case add() throws.
+ finishPoll(first);
+ ++n;
+ }
+ return n;
+ } finally {
+ lock.unlock();
+ }
}
public int drainTo(Collection<? super Runnable> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -1267,11 +1245,9 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
+ RunnableScheduledFuture<?> first;
int n = 0;
- for (RunnableScheduledFuture<?> first;
- n < maxElements
- && (first = queue[0]) != null
- && first.getDelay(NANOSECONDS) <= 0;) {
+ while (n < maxElements && (first = peekExpired()) != null) {
c.add(first); // In this order, in case add() throws.
finishPoll(first);
++n;
@@ -1309,13 +1285,7 @@
}
public Iterator<Runnable> iterator() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return new Itr(Arrays.copyOf(queue, size));
- } finally {
- lock.unlock();
- }
+ return new Itr(Arrays.copyOf(queue, size));
}
/**
@@ -1337,7 +1307,8 @@
public Runnable next() {
if (cursor >= array.length)
throw new NoSuchElementException();
- return array[lastRet = cursor++];
+ lastRet = cursor;
+ return array[cursor++];
}
public void remove() {
diff --git a/ojluni/src/main/java/java/util/concurrent/Semaphore.java b/ojluni/src/main/java/java/util/concurrent/Semaphore.java
index 86ee638..1298a6e 100644
--- a/ojluni/src/main/java/java/util/concurrent/Semaphore.java
+++ b/ojluni/src/main/java/java/util/concurrent/Semaphore.java
@@ -72,8 +72,8 @@
* protected synchronized Object getNextAvailableItem() {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (!used[i]) {
- * used[i] = true;
- * return items[i];
+ * used[i] = true;
+ * return items[i];
* }
* }
* return null; // not reached
@@ -82,11 +82,11 @@
* protected synchronized boolean markAsUnused(Object item) {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (item == items[i]) {
- * if (used[i]) {
- * used[i] = false;
- * return true;
- * } else
- * return false;
+ * if (used[i]) {
+ * used[i] = false;
+ * return true;
+ * } else
+ * return false;
* }
* }
* return false;
@@ -359,7 +359,7 @@
* This "barging" behavior can be useful in certain
* circumstances, even though it breaks fairness. If you want to honor
* the fairness setting, then use
- * {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS)}
+ * {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
* @return {@code true} if a permit was acquired and {@code false}
@@ -523,7 +523,7 @@
* "barging" behavior can be useful in certain
* circumstances, even though it breaks fairness. If you want to
* honor the fairness setting, then use {@link #tryAcquire(int,
- * long, TimeUnit) tryAcquire(permits, 0, TimeUnit.SECONDS)}
+ * long, TimeUnit) tryAcquire(permits, 0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
* @param permits the number of permits to acquire
@@ -631,12 +631,9 @@
}
/**
- * Acquires and returns all permits that are immediately
- * available, or if negative permits are available, releases them.
- * Upon return, zero permits are available.
+ * Acquires and returns all permits that are immediately available.
*
- * @return the number of permits acquired or, if negative, the
- * number released
+ * @return the number of permits acquired
*/
public int drainPermits() {
return sync.drainPermits();
diff --git a/ojluni/src/main/java/java/util/concurrent/SubmissionPublisher.java b/ojluni/src/main/java/java/util/concurrent/SubmissionPublisher.java
deleted file mode 100644
index 5624161..0000000
--- a/ojluni/src/main/java/java/util/concurrent/SubmissionPublisher.java
+++ /dev/null
@@ -1,1480 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * This file is available under and governed by the GNU General Public
- * License version 2 only, as published by the Free Software Foundation.
- * However, the following notice accompanied the original version of this
- * file:
- *
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-package java.util.concurrent;
-
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.locks.LockSupport;
-import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
-import java.util.function.Consumer;
-import static java.util.concurrent.Flow.Publisher;
-import static java.util.concurrent.Flow.Subscriber;
-import static java.util.concurrent.Flow.Subscription;
-
-/**
- * A {@link Flow.Publisher} that asynchronously issues submitted
- * (non-null) items to current subscribers until it is closed. Each
- * current subscriber receives newly submitted items in the same order
- * unless drops or exceptions are encountered. Using a
- * SubmissionPublisher allows item generators to act as compliant <a
- * href="http://www.reactive-streams.org/"> reactive-streams</a>
- * Publishers relying on drop handling and/or blocking for flow
- * control.
- *
- * <p>A SubmissionPublisher uses the {@link Executor} supplied in its
- * constructor for delivery to subscribers. The best choice of
- * Executor depends on expected usage. If the generator(s) of
- * submitted items run in separate threads, and the number of
- * subscribers can be estimated, consider using a {@link
- * Executors#newFixedThreadPool}. Otherwise consider using the
- * default, normally the {@link ForkJoinPool#commonPool}.
- *
- * <p>Buffering allows producers and consumers to transiently operate
- * at different rates. Each subscriber uses an independent buffer.
- * Buffers are created upon first use and expanded as needed up to the
- * given maximum. (The enforced capacity may be rounded up to the
- * nearest power of two and/or bounded by the largest value supported
- * by this implementation.) Invocations of {@link
- * Flow.Subscription#request(long) request} do not directly result in
- * buffer expansion, but risk saturation if unfilled requests exceed
- * the maximum capacity. The default value of {@link
- * Flow#defaultBufferSize()} may provide a useful starting point for
- * choosing a capacity based on expected rates, resources, and usages.
- *
- * <p>A single SubmissionPublisher may be shared among multiple
- * sources. Actions in a source thread prior to publishing an item or
- * issuing a signal <a href="package-summary.html#MemoryVisibility">
- * <i>happen-before</i></a> actions subsequent to the corresponding
- * access by each subscriber. But reported estimates of lag and demand
- * are designed for use in monitoring, not for synchronization
- * control, and may reflect stale or inaccurate views of progress.
- *
- * <p>Publication methods support different policies about what to do
- * when buffers are saturated. Method {@link #submit(Object) submit}
- * blocks until resources are available. This is simplest, but least
- * responsive. The {@code offer} methods may drop items (either
- * immediately or with bounded timeout), but provide an opportunity to
- * interpose a handler and then retry.
- *
- * <p>If any Subscriber method throws an exception, its subscription
- * is cancelled. If a handler is supplied as a constructor argument,
- * it is invoked before cancellation upon an exception in method
- * {@link Flow.Subscriber#onNext onNext}, but exceptions in methods
- * {@link Flow.Subscriber#onSubscribe onSubscribe},
- * {@link Flow.Subscriber#onError(Throwable) onError} and
- * {@link Flow.Subscriber#onComplete() onComplete} are not recorded or
- * handled before cancellation. If the supplied Executor throws
- * {@link RejectedExecutionException} (or any other RuntimeException
- * or Error) when attempting to execute a task, or a drop handler
- * throws an exception when processing a dropped item, then the
- * exception is rethrown. In these cases, not all subscribers will
- * have been issued the published item. It is usually good practice to
- * {@link #closeExceptionally closeExceptionally} in these cases.
- *
- * <p>Method {@link #consume(Consumer)} simplifies support for a
- * common case in which the only action of a subscriber is to request
- * and process all items using a supplied function.
- *
- * <p>This class may also serve as a convenient base for subclasses
- * that generate items, and use the methods in this class to publish
- * them. For example here is a class that periodically publishes the
- * items generated from a supplier. (In practice you might add methods
- * to independently start and stop generation, to share Executors
- * among publishers, and so on, or use a SubmissionPublisher as a
- * component rather than a superclass.)
- *
- * <pre> {@code
- * class PeriodicPublisher<T> extends SubmissionPublisher<T> {
- * final ScheduledFuture<?> periodicTask;
- * final ScheduledExecutorService scheduler;
- * PeriodicPublisher(Executor executor, int maxBufferCapacity,
- * Supplier<? extends T> supplier,
- * long period, TimeUnit unit) {
- * super(executor, maxBufferCapacity);
- * scheduler = new ScheduledThreadPoolExecutor(1);
- * periodicTask = scheduler.scheduleAtFixedRate(
- * () -> submit(supplier.get()), 0, period, unit);
- * }
- * public void close() {
- * periodicTask.cancel(false);
- * scheduler.shutdown();
- * super.close();
- * }
- * }}</pre>
- *
- * <p>Here is an example of a {@link Flow.Processor} implementation.
- * It uses single-step requests to its publisher for simplicity of
- * illustration. A more adaptive version could monitor flow using the
- * lag estimate returned from {@code submit}, along with other utility
- * methods.
- *
- * <pre> {@code
- * class TransformProcessor<S,T> extends SubmissionPublisher<T>
- * implements Flow.Processor<S,T> {
- * final Function<? super S, ? extends T> function;
- * Flow.Subscription subscription;
- * TransformProcessor(Executor executor, int maxBufferCapacity,
- * Function<? super S, ? extends T> function) {
- * super(executor, maxBufferCapacity);
- * this.function = function;
- * }
- * public void onSubscribe(Flow.Subscription subscription) {
- * (this.subscription = subscription).request(1);
- * }
- * public void onNext(S item) {
- * subscription.request(1);
- * submit(function.apply(item));
- * }
- * public void onError(Throwable ex) { closeExceptionally(ex); }
- * public void onComplete() { close(); }
- * }}</pre>
- *
- * @param <T> the published item type
- * @author Doug Lea
- * @since 9
- */
-public class SubmissionPublisher<T> implements Publisher<T>,
- AutoCloseable {
- /*
- * Most mechanics are handled by BufferedSubscription. This class
- * mainly tracks subscribers and ensures sequentiality, by using
- * built-in synchronization locks across public methods. Using
- * built-in locks works well in the most typical case in which
- * only one thread submits items. We extend this idea in
- * submission methods by detecting single-ownership to reduce
- * producer-consumer synchronization strength.
- */
-
- /** The largest possible power of two array size. */
- static final int BUFFER_CAPACITY_LIMIT = 1 << 30;
-
- /**
- * Initial buffer capacity used when maxBufferCapacity is
- * greater. Must be a power of two.
- */
- static final int INITIAL_CAPACITY = 32;
-
- /** Round capacity to power of 2, at most limit. */
- static final int roundCapacity(int cap) {
- int n = cap - 1;
- n |= n >>> 1;
- n |= n >>> 2;
- n |= n >>> 4;
- n |= n >>> 8;
- n |= n >>> 16;
- return (n <= 0) ? 1 : // at least 1
- (n >= BUFFER_CAPACITY_LIMIT) ? BUFFER_CAPACITY_LIMIT : n + 1;
- }
-
- // default Executor setup; nearly the same as CompletableFuture
-
- /**
- * Default executor -- ForkJoinPool.commonPool() unless it cannot
- * support parallelism.
- */
- private static final Executor ASYNC_POOL =
- (ForkJoinPool.getCommonPoolParallelism() > 1) ?
- ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
-
- /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */
- private static final class ThreadPerTaskExecutor implements Executor {
- ThreadPerTaskExecutor() {} // prevent access constructor creation
- public void execute(Runnable r) { new Thread(r).start(); }
- }
-
- /**
- * Clients (BufferedSubscriptions) are maintained in a linked list
- * (via their "next" fields). This works well for publish loops.
- * It requires O(n) traversal to check for duplicate subscribers,
- * but we expect that subscribing is much less common than
- * publishing. Unsubscribing occurs only during traversal loops,
- * when BufferedSubscription methods return negative values
- * signifying that they have been closed. To reduce
- * head-of-line blocking, submit and offer methods first call
- * BufferedSubscription.offer on each subscriber, and place
- * saturated ones in retries list (using nextRetry field), and
- * retry, possibly blocking or dropping.
- */
- BufferedSubscription<T> clients;
-
- /** Run status, updated only within locks */
- volatile boolean closed;
- /** Set true on first call to subscribe, to initialize possible owner */
- boolean subscribed;
- /** The first caller thread to subscribe, or null if thread ever changed */
- Thread owner;
- /** If non-null, the exception in closeExceptionally */
- volatile Throwable closedException;
-
- // Parameters for constructing BufferedSubscriptions
- final Executor executor;
- final BiConsumer<? super Subscriber<? super T>, ? super Throwable> onNextHandler;
- final int maxBufferCapacity;
-
- /**
- * Creates a new SubmissionPublisher using the given Executor for
- * async delivery to subscribers, with the given maximum buffer size
- * for each subscriber, and, if non-null, the given handler invoked
- * when any Subscriber throws an exception in method {@link
- * Flow.Subscriber#onNext(Object) onNext}.
- *
- * @param executor the executor to use for async delivery,
- * supporting creation of at least one independent thread
- * @param maxBufferCapacity the maximum capacity for each
- * subscriber's buffer (the enforced capacity may be rounded up to
- * the nearest power of two and/or bounded by the largest value
- * supported by this implementation; method {@link #getMaxBufferCapacity}
- * returns the actual value)
- * @param handler if non-null, procedure to invoke upon exception
- * thrown in method {@code onNext}
- * @throws NullPointerException if executor is null
- * @throws IllegalArgumentException if maxBufferCapacity not
- * positive
- */
- public SubmissionPublisher(Executor executor, int maxBufferCapacity,
- BiConsumer<? super Subscriber<? super T>, ? super Throwable> handler) {
- if (executor == null)
- throw new NullPointerException();
- if (maxBufferCapacity <= 0)
- throw new IllegalArgumentException("capacity must be positive");
- this.executor = executor;
- this.onNextHandler = handler;
- this.maxBufferCapacity = roundCapacity(maxBufferCapacity);
- }
-
- /**
- * Creates a new SubmissionPublisher using the given Executor for
- * async delivery to subscribers, with the given maximum buffer size
- * for each subscriber, and no handler for Subscriber exceptions in
- * method {@link Flow.Subscriber#onNext(Object) onNext}.
- *
- * @param executor the executor to use for async delivery,
- * supporting creation of at least one independent thread
- * @param maxBufferCapacity the maximum capacity for each
- * subscriber's buffer (the enforced capacity may be rounded up to
- * the nearest power of two and/or bounded by the largest value
- * supported by this implementation; method {@link #getMaxBufferCapacity}
- * returns the actual value)
- * @throws NullPointerException if executor is null
- * @throws IllegalArgumentException if maxBufferCapacity not
- * positive
- */
- public SubmissionPublisher(Executor executor, int maxBufferCapacity) {
- this(executor, maxBufferCapacity, null);
- }
-
- /**
- * Creates a new SubmissionPublisher using the {@link
- * ForkJoinPool#commonPool()} for async delivery to subscribers
- * (unless it does not support a parallelism level of at least two,
- * in which case, a new Thread is created to run each task), with
- * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no
- * handler for Subscriber exceptions in method {@link
- * Flow.Subscriber#onNext(Object) onNext}.
- */
- public SubmissionPublisher() {
- this(ASYNC_POOL, Flow.defaultBufferSize(), null);
- }
-
- /**
- * Adds the given Subscriber unless already subscribed. If already
- * subscribed, the Subscriber's {@link
- * Flow.Subscriber#onError(Throwable) onError} method is invoked on
- * the existing subscription with an {@link IllegalStateException}.
- * Otherwise, upon success, the Subscriber's {@link
- * Flow.Subscriber#onSubscribe onSubscribe} method is invoked
- * asynchronously with a new {@link Flow.Subscription}. If {@link
- * Flow.Subscriber#onSubscribe onSubscribe} throws an exception, the
- * subscription is cancelled. Otherwise, if this SubmissionPublisher
- * was closed exceptionally, then the subscriber's {@link
- * Flow.Subscriber#onError onError} method is invoked with the
- * corresponding exception, or if closed without exception, the
- * subscriber's {@link Flow.Subscriber#onComplete() onComplete}
- * method is invoked. Subscribers may enable receiving items by
- * invoking the {@link Flow.Subscription#request(long) request}
- * method of the new Subscription, and may unsubscribe by invoking
- * its {@link Flow.Subscription#cancel() cancel} method.
- *
- * @param subscriber the subscriber
- * @throws NullPointerException if subscriber is null
- */
- public void subscribe(Subscriber<? super T> subscriber) {
- if (subscriber == null) throw new NullPointerException();
- int max = maxBufferCapacity; // allocate initial array
- Object[] array = new Object[max < INITIAL_CAPACITY ?
- max : INITIAL_CAPACITY];
- BufferedSubscription<T> subscription =
- new BufferedSubscription<T>(subscriber, executor, onNextHandler,
- array, max);
- synchronized (this) {
- if (!subscribed) {
- subscribed = true;
- owner = Thread.currentThread();
- }
- for (BufferedSubscription<T> b = clients, pred = null;;) {
- if (b == null) {
- Throwable ex;
- subscription.onSubscribe();
- if ((ex = closedException) != null)
- subscription.onError(ex);
- else if (closed)
- subscription.onComplete();
- else if (pred == null)
- clients = subscription;
- else
- pred.next = subscription;
- break;
- }
- BufferedSubscription<T> next = b.next;
- if (b.isClosed()) { // remove
- b.next = null; // detach
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else if (subscriber.equals(b.subscriber)) {
- b.onError(new IllegalStateException("Duplicate subscribe"));
- break;
- }
- else
- pred = b;
- b = next;
- }
- }
- }
-
- /**
- * Common implementation for all three forms of submit and offer.
- * Acts as submit if nanos == Long.MAX_VALUE, else offer.
- */
- private int doOffer(T item, long nanos,
- BiPredicate<Subscriber<? super T>, ? super T> onDrop) {
- if (item == null) throw new NullPointerException();
- int lag = 0;
- boolean complete, unowned;
- synchronized (this) {
- Thread t = Thread.currentThread(), o;
- BufferedSubscription<T> b = clients;
- if ((unowned = ((o = owner) != t)) && o != null)
- owner = null; // disable bias
- if (b == null)
- complete = closed;
- else {
- complete = false;
- boolean cleanMe = false;
- BufferedSubscription<T> retries = null, rtail = null, next;
- do {
- next = b.next;
- int stat = b.offer(item, unowned);
- if (stat == 0) { // saturated; add to retry list
- b.nextRetry = null; // avoid garbage on exceptions
- if (rtail == null)
- retries = b;
- else
- rtail.nextRetry = b;
- rtail = b;
- }
- else if (stat < 0) // closed
- cleanMe = true; // remove later
- else if (stat > lag)
- lag = stat;
- } while ((b = next) != null);
-
- if (retries != null || cleanMe)
- lag = retryOffer(item, nanos, onDrop, retries, lag, cleanMe);
- }
- }
- if (complete)
- throw new IllegalStateException("Closed");
- else
- return lag;
- }
-
- /**
- * Helps, (timed) waits for, and/or drops buffers on list; returns
- * lag or negative drops (for use in offer).
- */
- private int retryOffer(T item, long nanos,
- BiPredicate<Subscriber<? super T>, ? super T> onDrop,
- BufferedSubscription<T> retries, int lag,
- boolean cleanMe) {
- for (BufferedSubscription<T> r = retries; r != null;) {
- BufferedSubscription<T> nextRetry = r.nextRetry;
- r.nextRetry = null;
- if (nanos > 0L)
- r.awaitSpace(nanos);
- int stat = r.retryOffer(item);
- if (stat == 0 && onDrop != null && onDrop.test(r.subscriber, item))
- stat = r.retryOffer(item);
- if (stat == 0)
- lag = (lag >= 0) ? -1 : lag - 1;
- else if (stat < 0)
- cleanMe = true;
- else if (lag >= 0 && stat > lag)
- lag = stat;
- r = nextRetry;
- }
- if (cleanMe)
- cleanAndCount();
- return lag;
- }
-
- /**
- * Returns current list count after removing closed subscribers.
- * Call only while holding lock. Used mainly by retryOffer for
- * cleanup.
- */
- private int cleanAndCount() {
- int count = 0;
- BufferedSubscription<T> pred = null, next;
- for (BufferedSubscription<T> b = clients; b != null; b = next) {
- next = b.next;
- if (b.isClosed()) {
- b.next = null;
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else {
- pred = b;
- ++count;
- }
- }
- return count;
- }
-
- /**
- * Publishes the given item to each current subscriber by
- * asynchronously invoking its {@link Flow.Subscriber#onNext(Object)
- * onNext} method, blocking uninterruptibly while resources for any
- * subscriber are unavailable. This method returns an estimate of
- * the maximum lag (number of items submitted but not yet consumed)
- * among all current subscribers. This value is at least one
- * (accounting for this submitted item) if there are any
- * subscribers, else zero.
- *
- * <p>If the Executor for this publisher throws a
- * RejectedExecutionException (or any other RuntimeException or
- * Error) when attempting to asynchronously notify subscribers,
- * then this exception is rethrown, in which case not all
- * subscribers will have been issued this item.
- *
- * @param item the (non-null) item to publish
- * @return the estimated maximum lag among subscribers
- * @throws IllegalStateException if closed
- * @throws NullPointerException if item is null
- * @throws RejectedExecutionException if thrown by Executor
- */
- public int submit(T item) {
- return doOffer(item, Long.MAX_VALUE, null);
- }
-
- /**
- * Publishes the given item, if possible, to each current subscriber
- * by asynchronously invoking its {@link
- * Flow.Subscriber#onNext(Object) onNext} method. The item may be
- * dropped by one or more subscribers if resource limits are
- * exceeded, in which case the given handler (if non-null) is
- * invoked, and if it returns true, retried once. Other calls to
- * methods in this class by other threads are blocked while the
- * handler is invoked. Unless recovery is assured, options are
- * usually limited to logging the error and/or issuing an {@link
- * Flow.Subscriber#onError(Throwable) onError} signal to the
- * subscriber.
- *
- * <p>This method returns a status indicator: If negative, it
- * represents the (negative) number of drops (failed attempts to
- * issue the item to a subscriber). Otherwise it is an estimate of
- * the maximum lag (number of items submitted but not yet
- * consumed) among all current subscribers. This value is at least
- * one (accounting for this submitted item) if there are any
- * subscribers, else zero.
- *
- * <p>If the Executor for this publisher throws a
- * RejectedExecutionException (or any other RuntimeException or
- * Error) when attempting to asynchronously notify subscribers, or
- * the drop handler throws an exception when processing a dropped
- * item, then this exception is rethrown.
- *
- * @param item the (non-null) item to publish
- * @param onDrop if non-null, the handler invoked upon a drop to a
- * subscriber, with arguments of the subscriber and item; if it
- * returns true, an offer is re-attempted (once)
- * @return if negative, the (negative) number of drops; otherwise
- * an estimate of maximum lag
- * @throws IllegalStateException if closed
- * @throws NullPointerException if item is null
- * @throws RejectedExecutionException if thrown by Executor
- */
- public int offer(T item,
- BiPredicate<Subscriber<? super T>, ? super T> onDrop) {
- return doOffer(item, 0L, onDrop);
- }
-
- /**
- * Publishes the given item, if possible, to each current subscriber
- * by asynchronously invoking its {@link
- * Flow.Subscriber#onNext(Object) onNext} method, blocking while
- * resources for any subscription are unavailable, up to the
- * specified timeout or until the caller thread is interrupted, at
- * which point the given handler (if non-null) is invoked, and if it
- * returns true, retried once. (The drop handler may distinguish
- * timeouts from interrupts by checking whether the current thread
- * is interrupted.) Other calls to methods in this class by other
- * threads are blocked while the handler is invoked. Unless
- * recovery is assured, options are usually limited to logging the
- * error and/or issuing an {@link Flow.Subscriber#onError(Throwable)
- * onError} signal to the subscriber.
- *
- * <p>This method returns a status indicator: If negative, it
- * represents the (negative) number of drops (failed attempts to
- * issue the item to a subscriber). Otherwise it is an estimate of
- * the maximum lag (number of items submitted but not yet
- * consumed) among all current subscribers. This value is at least
- * one (accounting for this submitted item) if there are any
- * subscribers, else zero.
- *
- * <p>If the Executor for this publisher throws a
- * RejectedExecutionException (or any other RuntimeException or
- * Error) when attempting to asynchronously notify subscribers, or
- * the drop handler throws an exception when processing a dropped
- * item, then this exception is rethrown.
- *
- * @param item the (non-null) item to publish
- * @param timeout how long to wait for resources for any subscriber
- * before giving up, in units of {@code unit}
- * @param unit a {@code TimeUnit} determining how to interpret the
- * {@code timeout} parameter
- * @param onDrop if non-null, the handler invoked upon a drop to a
- * subscriber, with arguments of the subscriber and item; if it
- * returns true, an offer is re-attempted (once)
- * @return if negative, the (negative) number of drops; otherwise
- * an estimate of maximum lag
- * @throws IllegalStateException if closed
- * @throws NullPointerException if item is null
- * @throws RejectedExecutionException if thrown by Executor
- */
- public int offer(T item, long timeout, TimeUnit unit,
- BiPredicate<Subscriber<? super T>, ? super T> onDrop) {
- long nanos = unit.toNanos(timeout);
- // distinguishes from untimed (only wrt interrupt policy)
- if (nanos == Long.MAX_VALUE) --nanos;
- return doOffer(item, nanos, onDrop);
- }
-
- /**
- * Unless already closed, issues {@link
- * Flow.Subscriber#onComplete() onComplete} signals to current
- * subscribers, and disallows subsequent attempts to publish.
- * Upon return, this method does <em>NOT</em> guarantee that all
- * subscribers have yet completed.
- */
- public void close() {
- if (!closed) {
- BufferedSubscription<T> b;
- synchronized (this) {
- // no need to re-check closed here
- b = clients;
- clients = null;
- owner = null;
- closed = true;
- }
- while (b != null) {
- BufferedSubscription<T> next = b.next;
- b.next = null;
- b.onComplete();
- b = next;
- }
- }
- }
-
- /**
- * Unless already closed, issues {@link
- * Flow.Subscriber#onError(Throwable) onError} signals to current
- * subscribers with the given error, and disallows subsequent
- * attempts to publish. Future subscribers also receive the given
- * error. Upon return, this method does <em>NOT</em> guarantee
- * that all subscribers have yet completed.
- *
- * @param error the {@code onError} argument sent to subscribers
- * @throws NullPointerException if error is null
- */
- public void closeExceptionally(Throwable error) {
- if (error == null)
- throw new NullPointerException();
- if (!closed) {
- BufferedSubscription<T> b;
- synchronized (this) {
- b = clients;
- if (!closed) { // don't clobber racing close
- closedException = error;
- clients = null;
- owner = null;
- closed = true;
- }
- }
- while (b != null) {
- BufferedSubscription<T> next = b.next;
- b.next = null;
- b.onError(error);
- b = next;
- }
- }
- }
-
- /**
- * Returns true if this publisher is not accepting submissions.
- *
- * @return true if closed
- */
- public boolean isClosed() {
- return closed;
- }
-
- /**
- * Returns the exception associated with {@link
- * #closeExceptionally(Throwable) closeExceptionally}, or null if
- * not closed or if closed normally.
- *
- * @return the exception, or null if none
- */
- public Throwable getClosedException() {
- return closedException;
- }
-
- /**
- * Returns true if this publisher has any subscribers.
- *
- * @return true if this publisher has any subscribers
- */
- public boolean hasSubscribers() {
- boolean nonEmpty = false;
- synchronized (this) {
- for (BufferedSubscription<T> b = clients; b != null;) {
- BufferedSubscription<T> next = b.next;
- if (b.isClosed()) {
- b.next = null;
- b = clients = next;
- }
- else {
- nonEmpty = true;
- break;
- }
- }
- }
- return nonEmpty;
- }
-
- /**
- * Returns the number of current subscribers.
- *
- * @return the number of current subscribers
- */
- public int getNumberOfSubscribers() {
- synchronized (this) {
- return cleanAndCount();
- }
- }
-
- /**
- * Returns the Executor used for asynchronous delivery.
- *
- * @return the Executor used for asynchronous delivery
- */
- public Executor getExecutor() {
- return executor;
- }
-
- /**
- * Returns the maximum per-subscriber buffer capacity.
- *
- * @return the maximum per-subscriber buffer capacity
- */
- public int getMaxBufferCapacity() {
- return maxBufferCapacity;
- }
-
- /**
- * Returns a list of current subscribers for monitoring and
- * tracking purposes, not for invoking {@link Flow.Subscriber}
- * methods on the subscribers.
- *
- * @return list of current subscribers
- */
- public List<Subscriber<? super T>> getSubscribers() {
- ArrayList<Subscriber<? super T>> subs = new ArrayList<>();
- synchronized (this) {
- BufferedSubscription<T> pred = null, next;
- for (BufferedSubscription<T> b = clients; b != null; b = next) {
- next = b.next;
- if (b.isClosed()) {
- b.next = null;
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else {
- subs.add(b.subscriber);
- pred = b;
- }
- }
- }
- return subs;
- }
-
- /**
- * Returns true if the given Subscriber is currently subscribed.
- *
- * @param subscriber the subscriber
- * @return true if currently subscribed
- * @throws NullPointerException if subscriber is null
- */
- public boolean isSubscribed(Subscriber<? super T> subscriber) {
- if (subscriber == null) throw new NullPointerException();
- if (!closed) {
- synchronized (this) {
- BufferedSubscription<T> pred = null, next;
- for (BufferedSubscription<T> b = clients; b != null; b = next) {
- next = b.next;
- if (b.isClosed()) {
- b.next = null;
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else if (subscriber.equals(b.subscriber))
- return true;
- else
- pred = b;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns an estimate of the minimum number of items requested
- * (via {@link Flow.Subscription#request(long) request}) but not
- * yet produced, among all current subscribers.
- *
- * @return the estimate, or zero if no subscribers
- */
- public long estimateMinimumDemand() {
- long min = Long.MAX_VALUE;
- boolean nonEmpty = false;
- synchronized (this) {
- BufferedSubscription<T> pred = null, next;
- for (BufferedSubscription<T> b = clients; b != null; b = next) {
- int n; long d;
- next = b.next;
- if ((n = b.estimateLag()) < 0) {
- b.next = null;
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else {
- if ((d = b.demand - n) < min)
- min = d;
- nonEmpty = true;
- pred = b;
- }
- }
- }
- return nonEmpty ? min : 0;
- }
-
- /**
- * Returns an estimate of the maximum number of items produced but
- * not yet consumed among all current subscribers.
- *
- * @return the estimate
- */
- public int estimateMaximumLag() {
- int max = 0;
- synchronized (this) {
- BufferedSubscription<T> pred = null, next;
- for (BufferedSubscription<T> b = clients; b != null; b = next) {
- int n;
- next = b.next;
- if ((n = b.estimateLag()) < 0) {
- b.next = null;
- if (pred == null)
- clients = next;
- else
- pred.next = next;
- }
- else {
- if (n > max)
- max = n;
- pred = b;
- }
- }
- }
- return max;
- }
-
- /**
- * Processes all published items using the given Consumer function.
- * Returns a CompletableFuture that is completed normally when this
- * publisher signals {@link Flow.Subscriber#onComplete()
- * onComplete}, or completed exceptionally upon any error, or an
- * exception is thrown by the Consumer, or the returned
- * CompletableFuture is cancelled, in which case no further items
- * are processed.
- *
- * @param consumer the function applied to each onNext item
- * @return a CompletableFuture that is completed normally
- * when the publisher signals onComplete, and exceptionally
- * upon any error or cancellation
- * @throws NullPointerException if consumer is null
- */
- public CompletableFuture<Void> consume(Consumer<? super T> consumer) {
- if (consumer == null)
- throw new NullPointerException();
- CompletableFuture<Void> status = new CompletableFuture<>();
- subscribe(new ConsumerSubscriber<T>(status, consumer));
- return status;
- }
-
- /** Subscriber for method consume */
- static final class ConsumerSubscriber<T> implements Subscriber<T> {
- final CompletableFuture<Void> status;
- final Consumer<? super T> consumer;
- Subscription subscription;
- ConsumerSubscriber(CompletableFuture<Void> status,
- Consumer<? super T> consumer) {
- this.status = status; this.consumer = consumer;
- }
- public final void onSubscribe(Subscription subscription) {
- this.subscription = subscription;
- status.whenComplete((v, e) -> subscription.cancel());
- if (!status.isDone())
- subscription.request(Long.MAX_VALUE);
- }
- public final void onError(Throwable ex) {
- status.completeExceptionally(ex);
- }
- public final void onComplete() {
- status.complete(null);
- }
- public final void onNext(T item) {
- try {
- consumer.accept(item);
- } catch (Throwable ex) {
- subscription.cancel();
- status.completeExceptionally(ex);
- }
- }
- }
-
- /**
- * A task for consuming buffer items and signals, created and
- * executed whenever they become available. A task consumes as
- * many items/signals as possible before terminating, at which
- * point another task is created when needed. The dual Runnable
- * and ForkJoinTask declaration saves overhead when executed by
- * ForkJoinPools, without impacting other kinds of Executors.
- */
- @SuppressWarnings("serial")
- static final class ConsumerTask<T> extends ForkJoinTask<Void>
- implements Runnable, CompletableFuture.AsynchronousCompletionTask {
- final BufferedSubscription<T> consumer;
- ConsumerTask(BufferedSubscription<T> consumer) {
- this.consumer = consumer;
- }
- public final Void getRawResult() { return null; }
- public final void setRawResult(Void v) {}
- public final boolean exec() { consumer.consume(); return false; }
- public final void run() { consumer.consume(); }
- }
-
- /**
- * A resizable array-based ring buffer with integrated control to
- * start a consumer task whenever items are available. The buffer
- * algorithm is specialized for the case of at most one concurrent
- * producer and consumer, and power of two buffer sizes. It relies
- * primarily on atomic operations (CAS or getAndSet) at the next
- * array slot to put or take an element, at the "tail" and "head"
- * indices written only by the producer and consumer respectively.
- *
- * We ensure internally that there is at most one active consumer
- * task at any given time. The publisher guarantees a single
- * producer via its lock. Sync among producers and consumers
- * relies on volatile fields "ctl", "demand", and "waiting" (along
- * with element access). Other variables are accessed in plain
- * mode, relying on outer ordering and exclusion, and/or enclosing
- * them within other volatile accesses. Some atomic operations are
- * avoided by tracking single threaded ownership by producers (in
- * the style of biased locking).
- *
- * Execution control and protocol state are managed using field
- * "ctl". Methods to subscribe, close, request, and cancel set
- * ctl bits (mostly using atomic boolean method getAndBitwiseOr),
- * and ensure that a task is running. (The corresponding consumer
- * side actions are in method consume.) To avoid starting a new
- * task on each action, ctl also includes a keep-alive bit
- * (ACTIVE) that is refreshed if needed on producer actions.
- * (Maintaining agreement about keep-alives requires most atomic
- * updates to be full SC/Volatile strength, which is still much
- * cheaper than using one task per item.) Error signals
- * additionally null out items and/or fields to reduce termination
- * latency. The cancel() method is supported by treating as ERROR
- * but suppressing onError signal.
- *
- * Support for blocking also exploits the fact that there is only
- * one possible waiter. ManagedBlocker-compatible control fields
- * are placed in this class itself rather than in wait-nodes.
- * Blocking control relies on the "waiting" and "waiter"
- * fields. Producers set them before trying to block. Signalling
- * unparks and clears fields. If the producer and/or consumer are
- * using a ForkJoinPool, the producer attempts to help run
- * consumer tasks via ForkJoinPool.helpAsyncBlocker before
- * blocking.
- *
- * Usages of this class may encounter any of several forms of
- * memory contention. We try to ameliorate across them without
- * unduly impacting footprints in low-contention usages where it
- * isn't needed. Buffer arrays start out small and grow only as
- * needed. The class uses @Contended and heuristic field
- * declaration ordering to reduce false-sharing memory contention
- * across instances of BufferedSubscription (as in, multiple
- * subscribers per publisher). We additionally segregate some
- * fields that would otherwise nearly always encounter cache line
- * contention among producers and consumers. To reduce contention
- * across time (vs space), consumers only periodically update
- * other fields (see method takeItems), at the expense of possibly
- * staler reporting of lags and demand (bounded at 12.5% == 1/8
- * capacity) and possibly more atomic operations.
- *
- * Other forms of imbalance and slowdowns can occur during startup
- * when producer and consumer methods are compiled and/or memory
- * is allocated at different rates. This is ameliorated by
- * artificially subdividing some consumer methods, including
- * isolation of all subscriber callbacks. This code also includes
- * typical power-of-two array screening idioms to avoid compilers
- * generating traps, along with the usual SSA-based inline
- * assignment coding style. Also, all methods and fields have
- * default visibility to simplify usage by callers.
- */
- @SuppressWarnings("serial")
- // Android-removed: @Contended, this hint is not used by the Android runtime.
- // @jdk.internal.vm.annotation.Contended
- static final class BufferedSubscription<T>
- implements Subscription, ForkJoinPool.ManagedBlocker {
- long timeout; // Long.MAX_VALUE if untimed wait
- int head; // next position to take
- int tail; // next position to put
- final int maxCapacity; // max buffer size
- volatile int ctl; // atomic run state flags
- Object[] array; // buffer
- final Subscriber<? super T> subscriber;
- final BiConsumer<? super Subscriber<? super T>, ? super Throwable> onNextHandler;
- Executor executor; // null on error
- Thread waiter; // blocked producer thread
- Throwable pendingError; // holds until onError issued
- BufferedSubscription<T> next; // used only by publisher
- BufferedSubscription<T> nextRetry; // used only by publisher
-
- // Android-removed: @Contended, this hint is not used by the Android runtime.
- // @jdk.internal.vm.annotation.Contended("c") // segregate
- volatile long demand; // # unfilled requests
- // Android-removed: @Contended, this hint is not used by the Android runtime.
- // @jdk.internal.vm.annotation.Contended("c")
- volatile int waiting; // nonzero if producer blocked
-
- // ctl bit values
- static final int CLOSED = 0x01; // if set, other bits ignored
- static final int ACTIVE = 0x02; // keep-alive for consumer task
- static final int REQS = 0x04; // (possibly) nonzero demand
- static final int ERROR = 0x08; // issues onError when noticed
- static final int COMPLETE = 0x10; // issues onComplete when done
- static final int RUN = 0x20; // task is or will be running
- static final int OPEN = 0x40; // true after subscribe
-
- static final long INTERRUPTED = -1L; // timeout vs interrupt sentinel
-
- BufferedSubscription(Subscriber<? super T> subscriber,
- Executor executor,
- BiConsumer<? super Subscriber<? super T>,
- ? super Throwable> onNextHandler,
- Object[] array,
- int maxBufferCapacity) {
- this.subscriber = subscriber;
- this.executor = executor;
- this.onNextHandler = onNextHandler;
- this.array = array;
- this.maxCapacity = maxBufferCapacity;
- }
-
- // Wrappers for some VarHandle methods
-
- final boolean weakCasCtl(int cmp, int val) {
- return CTL.weakCompareAndSet(this, cmp, val);
- }
-
- final int getAndBitwiseOrCtl(int bits) {
- return (int)CTL.getAndBitwiseOr(this, bits);
- }
-
- final long subtractDemand(int k) {
- long n = (long)(-k);
- return n + (long)DEMAND.getAndAdd(this, n);
- }
-
- final boolean casDemand(long cmp, long val) {
- return DEMAND.compareAndSet(this, cmp, val);
- }
-
- // Utilities used by SubmissionPublisher
-
- /**
- * Returns true if closed (consumer task may still be running).
- */
- final boolean isClosed() {
- return (ctl & CLOSED) != 0;
- }
-
- /**
- * Returns estimated number of buffered items, or negative if
- * closed.
- */
- final int estimateLag() {
- int c = ctl, n = tail - head;
- return ((c & CLOSED) != 0) ? -1 : (n < 0) ? 0 : n;
- }
-
- // Methods for submitting items
-
- /**
- * Tries to add item and start consumer task if necessary.
- * @return negative if closed, 0 if saturated, else estimated lag
- */
- final int offer(T item, boolean unowned) {
- Object[] a;
- int stat = 0, cap = ((a = array) == null) ? 0 : a.length;
- int t = tail, i = t & (cap - 1), n = t + 1 - head;
- if (cap > 0) {
- boolean added;
- if (n >= cap && cap < maxCapacity) // resize
- added = growAndOffer(item, a, t);
- else if (n >= cap || unowned) // need volatile CAS
- added = QA.compareAndSet(a, i, null, item);
- else { // can use release mode
- QA.setRelease(a, i, item);
- added = true;
- }
- if (added) {
- tail = t + 1;
- stat = n;
- }
- }
- return startOnOffer(stat);
- }
-
- /**
- * Tries to expand buffer and add item, returning true on
- * success. Currently fails only if out of memory.
- */
- final boolean growAndOffer(T item, Object[] a, int t) {
- int cap = 0, newCap = 0;
- Object[] newArray = null;
- if (a != null && (cap = a.length) > 0 && (newCap = cap << 1) > 0) {
- try {
- newArray = new Object[newCap];
- } catch (OutOfMemoryError ex) {
- }
- }
- if (newArray == null)
- return false;
- else { // take and move items
- int newMask = newCap - 1;
- newArray[t-- & newMask] = item;
- for (int mask = cap - 1, k = mask; k >= 0; --k) {
- Object x = QA.getAndSet(a, t & mask, null);
- if (x == null)
- break; // already consumed
- else
- newArray[t-- & newMask] = x;
- }
- array = newArray;
- VarHandle.releaseFence(); // release array and slots
- return true;
- }
- }
-
- /**
- * Version of offer for retries (no resize or bias)
- */
- final int retryOffer(T item) {
- Object[] a;
- int stat = 0, t = tail, h = head, cap;
- if ((a = array) != null && (cap = a.length) > 0 &&
- QA.compareAndSet(a, (cap - 1) & t, null, item))
- stat = (tail = t + 1) - h;
- return startOnOffer(stat);
- }
-
- /**
- * Tries to start consumer task after offer.
- * @return negative if now closed, else argument
- */
- final int startOnOffer(int stat) {
- int c; // start or keep alive if requests exist and not active
- if (((c = ctl) & (REQS | ACTIVE)) == REQS &&
- ((c = getAndBitwiseOrCtl(RUN | ACTIVE)) & (RUN | CLOSED)) == 0)
- tryStart();
- else if ((c & CLOSED) != 0)
- stat = -1;
- return stat;
- }
-
- /**
- * Tries to start consumer task. Sets error state on failure.
- */
- final void tryStart() {
- try {
- Executor e;
- ConsumerTask<T> task = new ConsumerTask<T>(this);
- if ((e = executor) != null) // skip if disabled on error
- e.execute(task);
- } catch (RuntimeException | Error ex) {
- getAndBitwiseOrCtl(ERROR | CLOSED);
- throw ex;
- }
- }
-
- // Signals to consumer tasks
-
- /**
- * Sets the given control bits, starting task if not running or closed.
- * @param bits state bits, assumed to include RUN but not CLOSED
- */
- final void startOnSignal(int bits) {
- if ((ctl & bits) != bits &&
- (getAndBitwiseOrCtl(bits) & (RUN | CLOSED)) == 0)
- tryStart();
- }
-
- final void onSubscribe() {
- startOnSignal(RUN | ACTIVE);
- }
-
- final void onComplete() {
- startOnSignal(RUN | ACTIVE | COMPLETE);
- }
-
- final void onError(Throwable ex) {
- int c; Object[] a; // to null out buffer on async error
- if (ex != null)
- pendingError = ex; // races are OK
- if (((c = getAndBitwiseOrCtl(ERROR | RUN | ACTIVE)) & CLOSED) == 0) {
- if ((c & RUN) == 0)
- tryStart();
- else if ((a = array) != null)
- Arrays.fill(a, null);
- }
- }
-
- public final void cancel() {
- onError(null);
- }
-
- public final void request(long n) {
- if (n > 0L) {
- for (;;) {
- long p = demand, d = p + n; // saturate
- if (casDemand(p, d < p ? Long.MAX_VALUE : d))
- break;
- }
- startOnSignal(RUN | ACTIVE | REQS);
- }
- else
- onError(new IllegalArgumentException(
- "non-positive subscription request"));
- }
-
- // Consumer task actions
-
- /**
- * Consumer loop, called from ConsumerTask, or indirectly when
- * helping during submit.
- */
- final void consume() {
- Subscriber<? super T> s;
- if ((s = subscriber) != null) { // hoist checks
- subscribeOnOpen(s);
- long d = demand;
- for (int h = head, t = tail;;) {
- int c, taken; boolean empty;
- if (((c = ctl) & ERROR) != 0) {
- closeOnError(s, null);
- break;
- }
- else if ((taken = takeItems(s, d, h)) > 0) {
- head = h += taken;
- d = subtractDemand(taken);
- }
- else if ((d = demand) == 0L && (c & REQS) != 0)
- weakCasCtl(c, c & ~REQS); // exhausted demand
- else if (d != 0L && (c & REQS) == 0)
- weakCasCtl(c, c | REQS); // new demand
- else if (t == (t = tail)) { // stability check
- if ((empty = (t == h)) && (c & COMPLETE) != 0) {
- closeOnComplete(s); // end of stream
- break;
- }
- else if (empty || d == 0L) {
- int bit = ((c & ACTIVE) != 0) ? ACTIVE : RUN;
- if (weakCasCtl(c, c & ~bit) && bit == RUN)
- break; // un-keep-alive or exit
- }
- }
- }
- }
- }
-
- /**
- * Consumes some items until unavailable or bound or error.
- *
- * @param s subscriber
- * @param d current demand
- * @param h current head
- * @return number taken
- */
- final int takeItems(Subscriber<? super T> s, long d, int h) {
- Object[] a;
- int k = 0, cap;
- if ((a = array) != null && (cap = a.length) > 0) {
- int m = cap - 1, b = (m >>> 3) + 1; // min(1, cap/8)
- int n = (d < (long)b) ? (int)d : b;
- for (; k < n; ++h, ++k) {
- Object x = QA.getAndSet(a, h & m, null);
- if (waiting != 0)
- signalWaiter();
- if (x == null)
- break;
- else if (!consumeNext(s, x))
- break;
- }
- }
- return k;
- }
-
- final boolean consumeNext(Subscriber<? super T> s, Object x) {
- try {
- @SuppressWarnings("unchecked") T y = (T) x;
- if (s != null)
- s.onNext(y);
- return true;
- } catch (Throwable ex) {
- handleOnNext(s, ex);
- return false;
- }
- }
-
- /**
- * Processes exception in Subscriber.onNext.
- */
- final void handleOnNext(Subscriber<? super T> s, Throwable ex) {
- BiConsumer<? super Subscriber<? super T>, ? super Throwable> h;
- try {
- if ((h = onNextHandler) != null)
- h.accept(s, ex);
- } catch (Throwable ignore) {
- }
- closeOnError(s, ex);
- }
-
- /**
- * Issues subscriber.onSubscribe if this is first signal.
- */
- final void subscribeOnOpen(Subscriber<? super T> s) {
- if ((ctl & OPEN) == 0 && (getAndBitwiseOrCtl(OPEN) & OPEN) == 0)
- consumeSubscribe(s);
- }
-
- final void consumeSubscribe(Subscriber<? super T> s) {
- try {
- if (s != null) // ignore if disabled
- s.onSubscribe(this);
- } catch (Throwable ex) {
- closeOnError(s, ex);
- }
- }
-
- /**
- * Issues subscriber.onComplete unless already closed.
- */
- final void closeOnComplete(Subscriber<? super T> s) {
- if ((getAndBitwiseOrCtl(CLOSED) & CLOSED) == 0)
- consumeComplete(s);
- }
-
- final void consumeComplete(Subscriber<? super T> s) {
- try {
- if (s != null)
- s.onComplete();
- } catch (Throwable ignore) {
- }
- }
-
- /**
- * Issues subscriber.onError, and unblocks producer if needed.
- */
- final void closeOnError(Subscriber<? super T> s, Throwable ex) {
- if ((getAndBitwiseOrCtl(ERROR | CLOSED) & CLOSED) == 0) {
- if (ex == null)
- ex = pendingError;
- pendingError = null; // detach
- executor = null; // suppress racing start calls
- signalWaiter();
- consumeError(s, ex);
- }
- }
-
- final void consumeError(Subscriber<? super T> s, Throwable ex) {
- try {
- if (ex != null && s != null)
- s.onError(ex);
- } catch (Throwable ignore) {
- }
- }
-
- // Blocking support
-
- /**
- * Unblocks waiting producer.
- */
- final void signalWaiter() {
- Thread w;
- waiting = 0;
- if ((w = waiter) != null)
- LockSupport.unpark(w);
- }
-
- /**
- * Returns true if closed or space available.
- * For ManagedBlocker.
- */
- public final boolean isReleasable() {
- Object[] a; int cap;
- return ((ctl & CLOSED) != 0 ||
- ((a = array) != null && (cap = a.length) > 0 &&
- QA.getAcquire(a, (cap - 1) & tail) == null));
- }
-
- /**
- * Helps or blocks until timeout, closed, or space available.
- */
- final void awaitSpace(long nanos) {
- if (!isReleasable()) {
- ForkJoinPool.helpAsyncBlocker(executor, this);
- if (!isReleasable()) {
- timeout = nanos;
- try {
- ForkJoinPool.managedBlock(this);
- } catch (InterruptedException ie) {
- timeout = INTERRUPTED;
- }
- if (timeout == INTERRUPTED)
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Blocks until closed, space available or timeout.
- * For ManagedBlocker.
- */
- public final boolean block() {
- long nanos = timeout;
- boolean timed = (nanos < Long.MAX_VALUE);
- long deadline = timed ? System.nanoTime() + nanos : 0L;
- while (!isReleasable()) {
- if (Thread.interrupted()) {
- timeout = INTERRUPTED;
- if (timed)
- break;
- }
- else if (timed && (nanos = deadline - System.nanoTime()) <= 0L)
- break;
- else if (waiter == null)
- waiter = Thread.currentThread();
- else if (waiting == 0)
- waiting = 1;
- else if (timed)
- LockSupport.parkNanos(this, nanos);
- else
- LockSupport.park(this);
- }
- waiter = null;
- waiting = 0;
- return true;
- }
-
- // VarHandle mechanics
- static final VarHandle CTL;
- static final VarHandle DEMAND;
- static final VarHandle QA;
-
- static {
- try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- CTL = l.findVarHandle(BufferedSubscription.class, "ctl",
- int.class);
- DEMAND = l.findVarHandle(BufferedSubscription.class, "demand",
- long.class);
- QA = MethodHandles.arrayElementVarHandle(Object[].class);
- } catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
- }
-
- // Reduce the risk of rare disastrous classloading in first call to
- // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
- Class<?> ensureLoaded = LockSupport.class;
- }
- }
-}
diff --git a/ojluni/src/main/java/java/util/concurrent/SynchronousQueue.java b/ojluni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 8de28b8..9655205 100644
--- a/ojluni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -36,18 +36,19 @@
package java.util.concurrent;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
-import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* A {@linkplain BlockingQueue blocking queue} in which each insert
* operation must wait for a corresponding remove operation by another
@@ -76,12 +77,9 @@
* is not guaranteed. However, a queue constructed with fairness set
* to {@code true} grants threads access in FIFO order.
*
- * <p>This class and its iterator implement all of the <em>optional</em>
- * methods of the {@link Collection} and {@link Iterator} interfaces.
- *
- * <p>This class is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
* @since 1.5
* @author Doug Lea and Bill Scherer and Michael Scott
@@ -249,7 +247,7 @@
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
- SNEXT.compareAndSet(this, cmp, val);
+ U.compareAndSwapObject(this, NEXT, cmp, val);
}
/**
@@ -262,7 +260,7 @@
*/
boolean tryMatch(SNode s) {
if (match == null &&
- SMATCH.compareAndSet(this, null, s)) {
+ U.compareAndSwapObject(this, MATCH, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
@@ -277,23 +275,26 @@
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
- SMATCH.compareAndSet(this, null, this);
+ U.compareAndSwapObject(this, MATCH, null, this);
}
boolean isCancelled() {
return match == this;
}
- // VarHandle mechanics
- private static final VarHandle SMATCH;
- private static final VarHandle SNEXT;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long MATCH;
+ private static final long NEXT;
+
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
- SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
+ MATCH = U.objectFieldOffset
+ (SNode.class.getDeclaredField("match"));
+ NEXT = U.objectFieldOffset
+ (SNode.class.getDeclaredField("next"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
@@ -303,7 +304,7 @@
boolean casHead(SNode h, SNode nh) {
return h == head &&
- SHEAD.compareAndSet(this, h, nh);
+ U.compareAndSwapObject(this, HEAD, h, nh);
}
/**
@@ -450,10 +451,8 @@
continue;
}
}
- if (spins > 0) {
- Thread.onSpinWait();
+ if (spins > 0)
spins = shouldSpin(s) ? (spins - 1) : 0;
- }
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
@@ -509,14 +508,15 @@
}
}
- // VarHandle mechanics
- private static final VarHandle SHEAD;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class);
+ HEAD = U.objectFieldOffset
+ (TransferStack.class.getDeclaredField("head"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
@@ -546,19 +546,19 @@
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
- QNEXT.compareAndSet(this, cmp, val);
+ U.compareAndSwapObject(this, NEXT, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp &&
- QITEM.compareAndSet(this, cmp, val);
+ U.compareAndSwapObject(this, ITEM, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
- QITEM.compareAndSet(this, cmp, this);
+ U.compareAndSwapObject(this, ITEM, cmp, this);
}
boolean isCancelled() {
@@ -574,16 +574,19 @@
return next == this;
}
- // VarHandle mechanics
- private static final VarHandle QITEM;
- private static final VarHandle QNEXT;
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ITEM;
+ private static final long NEXT;
+
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- QITEM = l.findVarHandle(QNode.class, "item", Object.class);
- QNEXT = l.findVarHandle(QNode.class, "next", QNode.class);
+ ITEM = U.objectFieldOffset
+ (QNode.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (QNode.class.getDeclaredField("next"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
@@ -611,7 +614,7 @@
*/
void advanceHead(QNode h, QNode nh) {
if (h == head &&
- QHEAD.compareAndSet(this, h, nh))
+ U.compareAndSwapObject(this, HEAD, h, nh))
h.next = h; // forget old next
}
@@ -620,7 +623,7 @@
*/
void advanceTail(QNode t, QNode nt) {
if (tail == t)
- QTAIL.compareAndSet(this, t, nt);
+ U.compareAndSwapObject(this, TAIL, t, nt);
}
/**
@@ -628,7 +631,7 @@
*/
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
- QCLEANME.compareAndSet(this, cmp, val);
+ U.compareAndSwapObject(this, CLEANME, cmp, val);
}
/**
@@ -749,10 +752,8 @@
continue;
}
}
- if (spins > 0) {
+ if (spins > 0)
--spins;
- Thread.onSpinWait();
- }
else if (s.waiter == null)
s.waiter = w;
else if (!timed)
@@ -816,21 +817,20 @@
}
}
- // VarHandle mechanics
- private static final VarHandle QHEAD;
- private static final VarHandle QTAIL;
- private static final VarHandle QCLEANME;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long CLEANME;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- QHEAD = l.findVarHandle(TransferQueue.class, "head",
- QNode.class);
- QTAIL = l.findVarHandle(TransferQueue.class, "tail",
- QNode.class);
- QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe",
- QNode.class);
+ HEAD = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("tail"));
+ CLEANME = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("cleanMe"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
}
@@ -1066,7 +1066,7 @@
/**
* Returns an empty spliterator in which calls to
- * {@link Spliterator#trySplit() trySplit} always return {@code null}.
+ * {@link java.util.Spliterator#trySplit()} always return {@code null}.
*
* @return an empty spliterator
* @since 1.8
@@ -1112,12 +1112,15 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- for (E e; (e = poll()) != null; n++)
+ for (E e; (e = poll()) != null;) {
c.add(e);
+ ++n;
+ }
return n;
}
@@ -1128,12 +1131,15 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- Objects.requireNonNull(c);
+ if (c == null)
+ throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
int n = 0;
- for (E e; n < maxElements && (e = poll()) != null; n++)
+ for (E e; n < maxElements && (e = poll()) != null;) {
c.add(e);
+ ++n;
+ }
return n;
}
diff --git a/ojluni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/ojluni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
index c20d2b3..195f8ac 100644
--- a/ojluni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
+++ b/ojluni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
@@ -36,7 +36,6 @@
package java.util.concurrent;
import java.io.ObjectStreamField;
-import java.security.AccessControlContext;
import java.util.Random;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
@@ -48,8 +47,6 @@
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.StreamSupport;
-import jdk.internal.misc.Unsafe;
-import jdk.internal.misc.VM;
/**
* A random number generator isolated to the current thread. Like the
@@ -67,7 +64,7 @@
* {@code ThreadLocalRandom.current().nextX(...)} (where
* {@code X} is {@code Int}, {@code Long}, etc).
* When all usages are of this form, it is never possible to
- * accidentally share a {@code ThreadLocalRandom} across multiple threads.
+ * accidently share a {@code ThreadLocalRandom} across multiple threads.
*
* <p>This class also provides additional commonly used bounded random
* generation methods.
@@ -98,9 +95,7 @@
* ThreadLocalRandom sequence. The dual use is a marriage of
* convenience, but is a simple and efficient way of reducing
* application-level overhead and footprint of most concurrent
- * programs. Even more opportunistically, we also define here
- * other package-private utilities that access Thread class
- * fields.
+ * programs.
*
* Even though this class subclasses java.util.Random, it uses the
* same basic algorithm as java.util.SplittableRandom. (See its
@@ -198,17 +193,9 @@
return r;
}
- /**
- * Generates a pseudorandom number with the indicated number of
- * low-order bits. Because this class has no subclasses, this
- * method cannot be invoked or overridden.
- *
- * @param bits random bits
- * @return the next pseudorandom value from this random number
- * generator's sequence
- */
+ // We must define this, but never use it.
protected int next(int bits) {
- return nextInt() >>> (32 - bits);
+ return (int)(mix64(nextSeed()) >>> (64 - bits));
}
/**
@@ -468,7 +455,7 @@
s = v1 * v1 + v2 * v2;
} while (s >= 1 || s == 0);
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
- nextLocalGaussian.set(Double.valueOf(v2 * multiplier));
+ nextLocalGaussian.set(new Double(v2 * multiplier));
return v1 * multiplier;
}
@@ -700,7 +687,8 @@
* @return a stream of pseudorandom {@code double} values,
* each with the given origin (inclusive) and bound (exclusive)
* @throws IllegalArgumentException if {@code streamSize} is
- * less than zero, or {@code randomNumberOrigin}
+ * less than zero
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
* @since 1.8
*/
@@ -970,21 +958,6 @@
return r;
}
- // Support for other package-private ThreadLocal access
-
- /**
- * Erases ThreadLocals by nulling out Thread maps.
- */
- static final void eraseThreadLocals(Thread thread) {
- U.putObject(thread, THREADLOCALS, null);
- U.putObject(thread, INHERITABLETHREADLOCALS, null);
- }
-
- static final void setInheritedAccessControlContext(Thread thread,
- AccessControlContext acc) {
- U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
- }
-
// Serialization support
private static final long serialVersionUID = -5851777807851030925L;
@@ -1039,10 +1012,7 @@
*/
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
- /**
- * The least non-zero value returned by nextDouble(). This value
- * is scaled by a random value of 53 bits to produce a result.
- */
+ // Constants from SplittableRandom
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
@@ -1052,19 +1022,22 @@
static final String BAD_SIZE = "size must be non-negative";
// Unsafe mechanics
- private static final Unsafe U = Unsafe.getUnsafe();
- private static final long SEED = U.objectFieldOffset
- (Thread.class, "threadLocalRandomSeed");
- private static final long PROBE = U.objectFieldOffset
- (Thread.class, "threadLocalRandomProbe");
- private static final long SECONDARY = U.objectFieldOffset
- (Thread.class, "threadLocalRandomSecondarySeed");
- private static final long THREADLOCALS = U.objectFieldOffset
- (Thread.class, "threadLocals");
- private static final long INHERITABLETHREADLOCALS = U.objectFieldOffset
- (Thread.class, "inheritableThreadLocals");
- private static final long INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
- (Thread.class, "inheritedAccessControlContext");
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long SEED;
+ private static final long PROBE;
+ private static final long SECONDARY;
+ static {
+ try {
+ SEED = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSeed"));
+ PROBE = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomProbe"));
+ SECONDARY = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
/** Rarely-used holder for the second of a pair of Gaussians */
private static final ThreadLocal<Double> nextLocalGaussian =
@@ -1085,8 +1058,11 @@
// at end of <clinit> to survive static initialization circularity
static {
- String sec = VM.getSavedProperty("java.util.secureRandomSeed");
- if (Boolean.parseBoolean(sec)) {
+ if (java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.getBoolean("java.util.secureRandomSeed");
+ }})) {
byte[] seedBytes = java.security.SecureRandom.getSeed(8);
long s = (long)seedBytes[0] & 0xffL;
for (int i = 1; i < 8; ++i)
diff --git a/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
index f88242b..b0096a4 100644
--- a/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
+++ b/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
@@ -79,28 +79,31 @@
*
* <dt>Core and maximum pool sizes</dt>
*
- * <dd>A {@code ThreadPoolExecutor} will automatically adjust the
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * A {@code ThreadPoolExecutor} will automatically adjust the
* pool size (see {@link #getPoolSize})
* according to the bounds set by
* corePoolSize (see {@link #getCorePoolSize}) and
* maximumPoolSize (see {@link #getMaximumPoolSize}).
*
* When a new task is submitted in method {@link #execute(Runnable)},
- * if fewer than corePoolSize threads are running, a new thread is
+ * and fewer than corePoolSize threads are running, a new thread is
* created to handle the request, even if other worker threads are
- * idle. Else if fewer than maximumPoolSize threads are running, a
- * new thread will be created to handle the request only if the queue
- * is full. By setting corePoolSize and maximumPoolSize the same, you
- * create a fixed-size thread pool. By setting maximumPoolSize to an
- * essentially unbounded value such as {@code Integer.MAX_VALUE}, you
- * allow the pool to accommodate an arbitrary number of concurrent
- * tasks. Most typically, core and maximum pool sizes are set only
- * upon construction, but they may also be changed dynamically using
- * {@link #setCorePoolSize} and {@link #setMaximumPoolSize}. </dd>
+ * idle. If there are more than corePoolSize but less than
+ * maximumPoolSize threads running, a new thread will be created only
+ * if the queue is full. By setting corePoolSize and maximumPoolSize
+ * the same, you create a fixed-size thread pool. By setting
+ * maximumPoolSize to an essentially unbounded value such as {@code
+ * Integer.MAX_VALUE}, you allow the pool to accommodate an arbitrary
+ * number of concurrent tasks. Most typically, core and maximum pool
+ * sizes are set only upon construction, but they may also be changed
+ * dynamically using {@link #setCorePoolSize} and {@link
+ * #setMaximumPoolSize}. </dd>
*
* <dt>On-demand construction</dt>
*
- * <dd>By default, even core threads are initially created and
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * By default, even core threads are initially created and
* started only when new tasks arrive, but this can be overridden
* dynamically using method {@link #prestartCoreThread} or {@link
* #prestartAllCoreThreads}. You probably want to prestart threads if
@@ -108,7 +111,8 @@
*
* <dt>Creating new threads</dt>
*
- * <dd>New threads are created using a {@link ThreadFactory}. If not
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * New threads are created using a {@link ThreadFactory}. If not
* otherwise specified, a {@link Executors#defaultThreadFactory} is
* used, that creates threads to all be in the same {@link
* ThreadGroup} and with the same {@code NORM_PRIORITY} priority and
@@ -125,7 +129,8 @@
*
* <dt>Keep-alive times</dt>
*
- * <dd>If the pool currently has more than corePoolSize threads,
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * If the pool currently has more than corePoolSize threads,
* excess threads will be terminated if they have been idle for more
* than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}).
* This provides a means of reducing resource consumption when the
@@ -142,7 +147,8 @@
*
* <dt>Queuing</dt>
*
- * <dd>Any {@link BlockingQueue} may be used to transfer and hold
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * Any {@link BlockingQueue} may be used to transfer and hold
* submitted tasks. The use of this queue interacts with pool sizing:
*
* <ul>
@@ -207,7 +213,8 @@
*
* <dt>Rejected tasks</dt>
*
- * <dd>New tasks submitted in method {@link #execute(Runnable)} will be
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * New tasks submitted in method {@link #execute(Runnable)} will be
* <em>rejected</em> when the Executor has been shut down, and also when
* the Executor uses finite bounds for both maximum threads and work queue
* capacity, and is saturated. In either case, the {@code execute} method
@@ -218,8 +225,9 @@
*
* <ol>
*
- * <li>In the default {@link ThreadPoolExecutor.AbortPolicy}, the handler
- * throws a runtime {@link RejectedExecutionException} upon rejection.
+ * <li>In the default {@link ThreadPoolExecutor.AbortPolicy}, the
+ * handler throws a runtime {@link RejectedExecutionException} upon
+ * rejection.
*
* <li>In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
* that invokes {@code execute} itself runs the task. This provides a
@@ -243,7 +251,8 @@
*
* <dt>Hook methods</dt>
*
- * <dd>This class provides {@code protected} overridable
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * This class provides {@code protected} overridable
* {@link #beforeExecute(Thread, Runnable)} and
* {@link #afterExecute(Runnable, Throwable)} methods that are called
* before and after execution of each task. These can be used to
@@ -259,19 +268,22 @@
*
* <dt>Queue maintenance</dt>
*
- * <dd>Method {@link #getQueue()} allows access to the work queue
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * Method {@link #getQueue()} allows access to the work queue
* for purposes of monitoring and debugging. Use of this method for
* any other purpose is strongly discouraged. Two supplied methods,
* {@link #remove(Runnable)} and {@link #purge} are available to
* assist in storage reclamation when large numbers of queued tasks
* become cancelled.</dd>
*
- * <dt>Reclamation</dt>
+ * <dt>Finalization</dt>
*
- * <dd>A pool that is no longer referenced in a program <em>AND</em>
- * has no remaining threads may be reclaimed (garbage collected)
- * without being explicitly shutdown. You can configure a pool to
- * allow all unused threads to eventually die by setting appropriate
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * A pool that is no longer referenced in a program <em>AND</em>
+ * has no remaining threads will be {@code shutdown} automatically. If
+ * you would like to ensure that unreferenced pools are reclaimed even
+ * if users forget to call {@link #shutdown}, then you must arrange
+ * that unused threads eventually die, by setting appropriate
* keep-alive times, using a lower bound of zero core threads and/or
* setting {@link #allowCoreThreadTimeOut(boolean)}. </dd>
*
@@ -362,7 +374,7 @@
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
- * On invocation of shutdown()
+ * On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
@@ -386,7 +398,7 @@
@ReachabilitySensitive
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
- private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
+ private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
@@ -396,8 +408,8 @@
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
- private static int runStateOf(int c) { return c & ~COUNT_MASK; }
- private static int workerCountOf(int c) { return c & COUNT_MASK; }
+ private static int runStateOf(int c) { return c & ~CAPACITY; }
+ private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
/*
@@ -437,7 +449,7 @@
* decrements are performed within getTask.
*/
private void decrementWorkerCount() {
- ctl.addAndGet(-1);
+ do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
/**
@@ -543,17 +555,12 @@
* Core pool size is the minimum number of workers to keep alive
* (and not allow to time out etc) unless allowCoreThreadTimeOut
* is set, in which case the minimum is zero.
- *
- * Since the worker count is actually stored in COUNT_BITS bits,
- * the effective limit is {@code corePoolSize & COUNT_MASK}.
*/
private volatile int corePoolSize;
/**
- * Maximum pool size.
- *
- * Since the worker count is actually stored in COUNT_BITS bits,
- * the effective limit is {@code maximumPoolSize & COUNT_MASK}.
+ * Maximum pool size. Note that the actual maximum is internally
+ * bounded by CAPACITY.
*/
private volatile int maximumPoolSize;
@@ -619,9 +626,6 @@
/** Per-thread task counter */
volatile long completedTasks;
- // TODO: switch to AbstractQueuedLongSynchronizer and move
- // completedTasks into the lock word.
-
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
@@ -712,7 +716,7 @@
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
- (runStateLessThan(c, STOP) && ! workQueue.isEmpty()))
+ (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
@@ -751,12 +755,17 @@
* specially.
*/
private void checkShutdownAccess() {
- // assert mainLock.isHeldByCurrentThread();
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
- for (Worker w : workers)
- security.checkAccess(w.thread);
+ final ReentrantLock mainLock = this.mainLock;
+ mainLock.lock();
+ try {
+ for (Worker w : workers)
+ security.checkAccess(w.thread);
+ } finally {
+ mainLock.unlock();
+ }
}
}
@@ -765,9 +774,14 @@
* (in which case some threads may remain uninterrupted).
*/
private void interruptWorkers() {
- // assert mainLock.isHeldByCurrentThread();
- for (Worker w : workers)
- w.interruptIfStarted();
+ final ReentrantLock mainLock = this.mainLock;
+ mainLock.lock();
+ try {
+ for (Worker w : workers)
+ w.interruptIfStarted();
+ } finally {
+ mainLock.unlock();
+ }
}
/**
@@ -843,6 +857,17 @@
}
/**
+ * State check needed by ScheduledThreadPoolExecutor to
+ * enable running tasks during shutdown.
+ *
+ * @param shutdownOK true if should return true if SHUTDOWN
+ */
+ final boolean isRunningOrShutdown(boolean shutdownOK) {
+ int rs = runStateOf(ctl.get());
+ return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
+ }
+
+ /**
* Drains the task queue into a new list, normally using
* drainTo. But if the queue is a DelayQueue or any other kind of
* queue for which poll or drainTo may fail to remove some
@@ -893,22 +918,26 @@
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
- for (int c = ctl.get();;) {
+ for (;;) {
+ int c = ctl.get();
+ int rs = runStateOf(c);
+
// Check if queue empty only if necessary.
- if (runStateAtLeast(c, SHUTDOWN)
- && (runStateAtLeast(c, STOP)
- || firstTask != null
- || workQueue.isEmpty()))
+ if (rs >= SHUTDOWN &&
+ ! (rs == SHUTDOWN &&
+ firstTask == null &&
+ ! workQueue.isEmpty()))
return false;
for (;;) {
- if (workerCountOf(c)
- >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
+ int wc = workerCountOf(c);
+ if (wc >= CAPACITY ||
+ wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
- if (runStateAtLeast(c, SHUTDOWN))
+ if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
@@ -927,10 +956,10 @@
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
- int c = ctl.get();
+ int rs = runStateOf(ctl.get());
- if (isRunning(c) ||
- (runStateLessThan(c, STOP) && firstTask == null)) {
+ if (rs < SHUTDOWN ||
+ (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
@@ -1037,10 +1066,10 @@
for (;;) {
int c = ctl.get();
+ int rs = runStateOf(c);
// Check if queue empty only if necessary.
- if (runStateAtLeast(c, SHUTDOWN)
- && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
+ if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
@@ -1133,12 +1162,17 @@
wt.interrupt();
try {
beforeExecute(wt, task);
+ Throwable thrown = null;
try {
task.run();
- afterExecute(task, null);
- } catch (Throwable ex) {
- afterExecute(task, ex);
- throw ex;
+ } catch (RuntimeException x) {
+ thrown = x; throw x;
+ } catch (Error x) {
+ thrown = x; throw x;
+ } catch (Throwable x) {
+ thrown = x; throw new Error(x);
+ } finally {
+ afterExecute(task, thrown);
}
} finally {
task = null;
@@ -1156,11 +1190,9 @@
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
- * parameters, the default thread factory and the default rejected
- * execution handler.
- *
- * <p>It may be more convenient to use one of the {@link Executors}
- * factory methods instead of this general purpose constructor.
+ * parameters and default thread factory and rejected execution handler.
+ * It may be more convenient to use one of the {@link Executors} factory
+ * methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
@@ -1191,8 +1223,7 @@
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
- * parameters and {@linkplain ThreadPoolExecutor.AbortPolicy
- * default rejected execution handler}.
+ * parameters and default rejected execution handler.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
@@ -1227,8 +1258,7 @@
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
- * parameters and
- * {@linkplain Executors#defaultThreadFactory default thread factory}.
+ * parameters and default thread factory.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
@@ -1316,7 +1346,7 @@
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
- * the task is handled by the current {@link RejectedExecutionHandler}.
+ * the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
@@ -1421,12 +1451,7 @@
}
public boolean isShutdown() {
- return runStateAtLeast(ctl.get(), SHUTDOWN);
- }
-
- /** Used by ScheduledThreadPoolExecutor. */
- boolean isStopped() {
- return runStateAtLeast(ctl.get(), STOP);
+ return ! isRunning(ctl.get());
}
/**
@@ -1442,7 +1467,7 @@
*/
public boolean isTerminating() {
int c = ctl.get();
- return runStateAtLeast(c, SHUTDOWN) && runStateLessThan(c, TERMINATED);
+ return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
public boolean isTerminated() {
@@ -1455,7 +1480,7 @@
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- while (runStateLessThan(ctl.get(), TERMINATED)) {
+ while (!runStateAtLeast(ctl.get(), TERMINATED)) {
if (nanos <= 0L)
return false;
nanos = termination.awaitNanos(nanos);
@@ -1466,21 +1491,13 @@
}
}
- // Override without "throws Throwable" for compatibility with subclasses
- // whose finalize method invokes super.finalize() (as is recommended).
- // Before JDK 11, finalize() had a non-empty method body.
-
- // Android-added: The @deprecated javadoc tag
/**
- * @implNote Previous versions of this class had a finalize method
- * that shut down this executor, but in this version, finalize
- * does nothing.
- *
- * @deprecated Subclass is not recommended to override finalize(). If it
- * must, please always invoke super.finalize().
+ * Invokes {@code shutdown} when this executor is no longer
+ * referenced and it has no threads.
*/
- @Deprecated(since="9")
- protected void finalize() {}
+ protected void finalize() {
+ shutdown();
+ }
/**
* Sets the thread factory used to create new threads.
@@ -1929,7 +1946,7 @@
}
int c = ctl.get();
String runState =
- isRunning(c) ? "Running" :
+ runStateLessThan(c, SHUTDOWN) ? "Running" :
runStateAtLeast(c, TERMINATED) ? "Terminated" :
"Shutting down";
return super.toString() +
@@ -2048,10 +2065,7 @@
/**
* A handler for rejected tasks that throws a
- * {@link RejectedExecutionException}.
- *
- * This is the default handler for {@link ThreadPoolExecutor} and
- * {@link ScheduledThreadPoolExecutor}.
+ * {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
diff --git a/ojluni/src/main/java/java/util/concurrent/TimeUnit.java b/ojluni/src/main/java/java/util/concurrent/TimeUnit.java
index f02aa9f..44d7964 100644
--- a/ojluni/src/main/java/java/util/concurrent/TimeUnit.java
+++ b/ojluni/src/main/java/java/util/concurrent/TimeUnit.java
@@ -35,8 +35,6 @@
package java.util.concurrent;
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
import java.util.Objects;
/**
@@ -76,94 +74,137 @@
/**
* Time unit representing one thousandth of a microsecond.
*/
- NANOSECONDS(TimeUnit.NANO_SCALE),
+ NANOSECONDS {
+ public long toNanos(long d) { return d; }
+ public long toMicros(long d) { return d/(C1/C0); }
+ public long toMillis(long d) { return d/(C2/C0); }
+ public long toSeconds(long d) { return d/(C3/C0); }
+ public long toMinutes(long d) { return d/(C4/C0); }
+ public long toHours(long d) { return d/(C5/C0); }
+ public long toDays(long d) { return d/(C6/C0); }
+ public long convert(long d, TimeUnit u) { return u.toNanos(d); }
+ int excessNanos(long d, long m) { return (int)(d - (m*C2)); }
+ },
+
/**
* Time unit representing one thousandth of a millisecond.
*/
- MICROSECONDS(TimeUnit.MICRO_SCALE),
+ MICROSECONDS {
+ public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); }
+ public long toMicros(long d) { return d; }
+ public long toMillis(long d) { return d/(C2/C1); }
+ public long toSeconds(long d) { return d/(C3/C1); }
+ public long toMinutes(long d) { return d/(C4/C1); }
+ public long toHours(long d) { return d/(C5/C1); }
+ public long toDays(long d) { return d/(C6/C1); }
+ public long convert(long d, TimeUnit u) { return u.toMicros(d); }
+ int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); }
+ },
+
/**
* Time unit representing one thousandth of a second.
*/
- MILLISECONDS(TimeUnit.MILLI_SCALE),
+ MILLISECONDS {
+ public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); }
+ public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); }
+ public long toMillis(long d) { return d; }
+ public long toSeconds(long d) { return d/(C3/C2); }
+ public long toMinutes(long d) { return d/(C4/C2); }
+ public long toHours(long d) { return d/(C5/C2); }
+ public long toDays(long d) { return d/(C6/C2); }
+ public long convert(long d, TimeUnit u) { return u.toMillis(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+
/**
* Time unit representing one second.
*/
- SECONDS(TimeUnit.SECOND_SCALE),
+ SECONDS {
+ public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); }
+ public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); }
+ public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); }
+ public long toSeconds(long d) { return d; }
+ public long toMinutes(long d) { return d/(C4/C3); }
+ public long toHours(long d) { return d/(C5/C3); }
+ public long toDays(long d) { return d/(C6/C3); }
+ public long convert(long d, TimeUnit u) { return u.toSeconds(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+
/**
* Time unit representing sixty seconds.
* @since 1.6
*/
- MINUTES(TimeUnit.MINUTE_SCALE),
+ MINUTES {
+ public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); }
+ public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); }
+ public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); }
+ public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); }
+ public long toMinutes(long d) { return d; }
+ public long toHours(long d) { return d/(C5/C4); }
+ public long toDays(long d) { return d/(C6/C4); }
+ public long convert(long d, TimeUnit u) { return u.toMinutes(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+
/**
* Time unit representing sixty minutes.
* @since 1.6
*/
- HOURS(TimeUnit.HOUR_SCALE),
+ HOURS {
+ public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); }
+ public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); }
+ public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); }
+ public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); }
+ public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); }
+ public long toHours(long d) { return d; }
+ public long toDays(long d) { return d/(C6/C5); }
+ public long convert(long d, TimeUnit u) { return u.toHours(d); }
+ int excessNanos(long d, long m) { return 0; }
+ },
+
/**
* Time unit representing twenty four hours.
* @since 1.6
*/
- DAYS(TimeUnit.DAY_SCALE);
+ DAYS {
+ public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); }
+ public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); }
+ public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); }
+ public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); }
+ public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); }
+ public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); }
+ public long toDays(long d) { return d; }
+ public long convert(long d, TimeUnit u) { return u.toDays(d); }
+ int excessNanos(long d, long m) { return 0; }
+ };
- // Scales as constants
- private static final long NANO_SCALE = 1L;
- private static final long MICRO_SCALE = 1000L * NANO_SCALE;
- private static final long MILLI_SCALE = 1000L * MICRO_SCALE;
- private static final long SECOND_SCALE = 1000L * MILLI_SCALE;
- private static final long MINUTE_SCALE = 60L * SECOND_SCALE;
- private static final long HOUR_SCALE = 60L * MINUTE_SCALE;
- private static final long DAY_SCALE = 24L * HOUR_SCALE;
+ // Handy constants for conversion methods
+ static final long C0 = 1L;
+ static final long C1 = C0 * 1000L;
+ static final long C2 = C1 * 1000L;
+ static final long C3 = C2 * 1000L;
+ static final long C4 = C3 * 60L;
+ static final long C5 = C4 * 60L;
+ static final long C6 = C5 * 24L;
- /*
- * Instances cache conversion ratios and saturation cutoffs for
- * the units up through SECONDS. Other cases compute them, in
- * method cvt.
- */
-
- private final long scale;
- private final long maxNanos;
- private final long maxMicros;
- private final long maxMillis;
- private final long maxSecs;
- private final long microRatio;
- private final int milliRatio; // fits in 32 bits
- private final int secRatio; // fits in 32 bits
-
- private TimeUnit(long s) {
- this.scale = s;
- this.maxNanos = Long.MAX_VALUE / s;
- long ur = (s >= MICRO_SCALE) ? (s / MICRO_SCALE) : (MICRO_SCALE / s);
- this.microRatio = ur;
- this.maxMicros = Long.MAX_VALUE / ur;
- long mr = (s >= MILLI_SCALE) ? (s / MILLI_SCALE) : (MILLI_SCALE / s);
- this.milliRatio = (int)mr;
- this.maxMillis = Long.MAX_VALUE / mr;
- long sr = (s >= SECOND_SCALE) ? (s / SECOND_SCALE) : (SECOND_SCALE / s);
- this.secRatio = (int)sr;
- this.maxSecs = Long.MAX_VALUE / sr;
- }
+ static final long MAX = Long.MAX_VALUE;
/**
- * General conversion utility.
- *
- * @param d duration
- * @param dst result unit scale
- * @param src source unit scale
+ * Scale d by m, checking for overflow.
+ * This has a short name to make above code more readable.
*/
- private static long cvt(long d, long dst, long src) {
- long r, m;
- if (src == dst)
- return d;
- else if (src < dst)
- return d / (dst / src);
- else if (d > (m = Long.MAX_VALUE / (r = src / dst)))
- return Long.MAX_VALUE;
- else if (d < -m)
- return Long.MIN_VALUE;
- else
- return d * r;
+ static long x(long d, long m, long over) {
+ if (d > +over) return Long.MAX_VALUE;
+ if (d < -over) return Long.MIN_VALUE;
+ return d * m;
}
+ // To maintain full signature compatibility with 1.5, and to improve the
+ // clarity of the generated javadoc (see 6287639: Abstract methods in
+ // enum classes should not be listed as abstract), method convert
+ // etc. are not declared abstract but otherwise act as abstract methods.
+
/**
* Converts the given time duration in the given unit to this unit.
* Conversions from finer to coarser granularities truncate, so
@@ -179,65 +220,11 @@
* @param sourceDuration the time duration in the given {@code sourceUnit}
* @param sourceUnit the unit of the {@code sourceDuration} argument
* @return the converted duration in this unit,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long convert(long sourceDuration, TimeUnit sourceUnit) {
- switch (this) {
- case NANOSECONDS: return sourceUnit.toNanos(sourceDuration);
- case MICROSECONDS: return sourceUnit.toMicros(sourceDuration);
- case MILLISECONDS: return sourceUnit.toMillis(sourceDuration);
- case SECONDS: return sourceUnit.toSeconds(sourceDuration);
- default: return cvt(sourceDuration, scale, sourceUnit.scale);
- }
- }
-
- /**
- * Converts the given time duration to this unit.
- *
- * <p>For any TimeUnit {@code unit},
- * {@code unit.convert(Duration.ofNanos(n))}
- * is equivalent to
- * {@code unit.convert(n, NANOSECONDS)}, and
- * {@code unit.convert(Duration.of(n, unit.toChronoUnit()))}
- * is equivalent to {@code n} (in the absence of overflow).
- *
- * @apiNote
- * This method differs from {@link Duration#toNanos()} in that it
- * does not throw {@link ArithmeticException} on numeric overflow.
- *
- * @param duration the time duration
- * @return the converted duration in this unit,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
- * @throws NullPointerException if {@code duration} is null
- * @see Duration#of(long,TemporalUnit)
- * @since 11
- */
- public long convert(Duration duration) {
- long secs = duration.getSeconds();
- int nano = duration.getNano();
- if (secs < 0 && nano > 0) {
- // use representation compatible with integer division
- secs++;
- nano -= (int) SECOND_SCALE;
- }
- final long s, nanoVal;
- // Optimize for the common case - NANOSECONDS without overflow
- if (this == NANOSECONDS)
- nanoVal = nano;
- else if ((s = scale) < SECOND_SCALE)
- nanoVal = nano / s;
- else if (this == SECONDS)
- return secs;
- else
- return secs / secRatio;
- long val = secs * secRatio + nanoVal;
- return ((secs < maxSecs && secs > -maxSecs) ||
- (secs == maxSecs && val > 0) ||
- (secs == -maxSecs && val < 0))
- ? val
- : (secs > 0) ? Long.MAX_VALUE : Long.MIN_VALUE;
+ throw new AbstractMethodError();
}
/**
@@ -245,19 +232,11 @@
* {@link #convert(long, TimeUnit) NANOSECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toNanos(long duration) {
- long s, m;
- if ((s = scale) == NANO_SCALE)
- return duration;
- else if (duration > (m = maxNanos))
- return Long.MAX_VALUE;
- else if (duration < -m)
- return Long.MIN_VALUE;
- else
- return duration * s;
+ throw new AbstractMethodError();
}
/**
@@ -265,19 +244,11 @@
* {@link #convert(long, TimeUnit) MICROSECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toMicros(long duration) {
- long s, m;
- if ((s = scale) <= MICRO_SCALE)
- return (s == MICRO_SCALE) ? duration : duration / microRatio;
- else if (duration > (m = maxMicros))
- return Long.MAX_VALUE;
- else if (duration < -m)
- return Long.MIN_VALUE;
- else
- return duration * microRatio;
+ throw new AbstractMethodError();
}
/**
@@ -285,19 +256,11 @@
* {@link #convert(long, TimeUnit) MILLISECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toMillis(long duration) {
- long s, m;
- if ((s = scale) <= MILLI_SCALE)
- return (s == MILLI_SCALE) ? duration : duration / milliRatio;
- else if (duration > (m = maxMillis))
- return Long.MAX_VALUE;
- else if (duration < -m)
- return Long.MIN_VALUE;
- else
- return duration * milliRatio;
+ throw new AbstractMethodError();
}
/**
@@ -305,19 +268,11 @@
* {@link #convert(long, TimeUnit) SECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toSeconds(long duration) {
- long s, m;
- if ((s = scale) <= SECOND_SCALE)
- return (s == SECOND_SCALE) ? duration : duration / secRatio;
- else if (duration > (m = maxSecs))
- return Long.MAX_VALUE;
- else if (duration < -m)
- return Long.MIN_VALUE;
- else
- return duration * secRatio;
+ throw new AbstractMethodError();
}
/**
@@ -325,12 +280,12 @@
* {@link #convert(long, TimeUnit) MINUTES.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
* @since 1.6
*/
public long toMinutes(long duration) {
- return cvt(duration, MINUTE_SCALE, scale);
+ throw new AbstractMethodError();
}
/**
@@ -338,12 +293,12 @@
* {@link #convert(long, TimeUnit) HOURS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or {@code Long.MIN_VALUE} if conversion would negatively overflow,
- * or {@code Long.MAX_VALUE} if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
* @since 1.6
*/
public long toHours(long duration) {
- return cvt(duration, HOUR_SCALE, scale);
+ throw new AbstractMethodError();
}
/**
@@ -354,7 +309,7 @@
* @since 1.6
*/
public long toDays(long duration) {
- return cvt(duration, DAY_SCALE, scale);
+ throw new AbstractMethodError();
}
/**
@@ -364,15 +319,7 @@
* @param m the number of milliseconds
* @return the number of nanoseconds
*/
- private int excessNanos(long d, long m) {
- long s;
- if ((s = scale) == NANO_SCALE)
- return (int)(d - (m * MILLI_SCALE));
- else if (s == MICRO_SCALE)
- return (int)((d * 1000L) - (m * MILLI_SCALE));
- else
- return 0;
- }
+ abstract int excessNanos(long d, long m);
/**
* Performs a timed {@link Object#wait(long, int) Object.wait}
@@ -380,18 +327,16 @@
* This is a convenience method that converts timeout arguments
* into the form required by the {@code Object.wait} method.
*
- * <p>For example, you could implement a blocking {@code poll} method
- * (see {@link BlockingQueue#poll(long, TimeUnit) BlockingQueue.poll})
+ * <p>For example, you could implement a blocking {@code poll}
+ * method (see {@link BlockingQueue#poll BlockingQueue.poll})
* using:
*
* <pre> {@code
- * public E poll(long timeout, TimeUnit unit)
+ * public synchronized Object poll(long timeout, TimeUnit unit)
* throws InterruptedException {
- * synchronized (lock) {
- * while (isEmpty()) {
- * unit.timedWait(lock, timeout);
- * ...
- * }
+ * while (empty) {
+ * unit.timedWait(this, timeout);
+ * ...
* }
* }}</pre>
*
@@ -447,12 +392,14 @@
}
}
+ // BEGIN Android-removed: OpenJDK 9 ChronoUnit related code.
+ /*
/**
* Converts this {@code TimeUnit} to the equivalent {@code ChronoUnit}.
*
* @return the converted equivalent ChronoUnit
* @since 9
- */
+ *
public ChronoUnit toChronoUnit() {
switch (this) {
case NANOSECONDS: return ChronoUnit.NANOS;
@@ -475,7 +422,7 @@
* equivalent TimeUnit
* @throws NullPointerException if {@code chronoUnit} is null
* @since 9
- */
+ *
public static TimeUnit of(ChronoUnit chronoUnit) {
switch (Objects.requireNonNull(chronoUnit, "chronoUnit")) {
case NANOS: return TimeUnit.NANOSECONDS;
@@ -490,5 +437,7 @@
"No TimeUnit equivalent for " + chronoUnit);
}
}
+ */
+ // END Android-removed: OpenJDK 9 ChronoUnit related code.
}
diff --git a/ojluni/src/main/java/java/util/concurrent/TransferQueue.java b/ojluni/src/main/java/java/util/concurrent/TransferQueue.java
index 72a4108..53da597 100644
--- a/ojluni/src/main/java/java/util/concurrent/TransferQueue.java
+++ b/ojluni/src/main/java/java/util/concurrent/TransferQueue.java
@@ -35,6 +35,10 @@
package java.util.concurrent;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
* A {@link BlockingQueue} in which producers may wait for consumers
* to receive elements. A {@code TransferQueue} may be useful for
@@ -57,10 +61,6 @@
* with zero capacity, such as {@link SynchronousQueue}, {@code put}
* and {@code transfer} are effectively synonymous.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
- * Java Collections Framework</a>.
- *
* @since 1.7
* @author Doug Lea
* @param <E> the type of elements held in this queue
diff --git a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index 34a200b..447a642 100644
--- a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -40,13 +40,10 @@
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.util.Objects;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
-import jdk.internal.misc.Unsafe;
-import jdk.internal.reflect.CallerSensitive;
-import jdk.internal.reflect.Reflection;
-import java.lang.invoke.VarHandle;
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
/**
* A reflection-based utility that enables atomic updates to
@@ -62,10 +59,6 @@
* guarantee atomicity only with respect to other invocations of
* {@code compareAndSet} and {@code set} on the same updater.
*
- * <p>Object arguments for parameters of type {@code T} that are not
- * instances of the class passed to {@link #newUpdater} will result in
- * a {@link ClassCastException} being thrown.
- *
* @since 1.5
* @author Doug Lea
* @param <T> The type of the object holding the updatable field
@@ -114,6 +107,8 @@
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful
+ * @throws ClassCastException if {@code obj} is not an instance
+ * of the class possessing the field established in the constructor
*/
public abstract boolean compareAndSet(T obj, long expect, long update);
@@ -132,6 +127,8 @@
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful
+ * @throws ClassCastException if {@code obj} is not an instance
+ * of the class possessing the field established in the constructor
*/
public abstract boolean weakCompareAndSet(T obj, long expect, long update);
@@ -156,8 +153,8 @@
public abstract void lazySet(T obj, long newValue);
/**
- * Returns the current value held in the field of the given object
- * managed by this updater.
+ * Gets the current value held in the field of the given object managed
+ * by this updater.
*
* @param obj An object whose field to get
* @return the current value
@@ -279,12 +276,10 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given
- * function, returning the previous value. The function should be
- * side-effect-free, since it may be re-applied when attempted
- * updates fail due to contention among threads.
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the previous
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
*
* @param obj An object whose field to get and set
* @param updateFunction a side-effect-free function
@@ -301,12 +296,10 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given
- * function, returning the updated value. The function should be
- * side-effect-free, since it may be re-applied when attempted
- * updates fail due to contention among threads.
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the updated
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
*
* @param obj An object whose field to get and set
* @param updateFunction a side-effect-free function
@@ -323,14 +316,13 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given function
- * to the current and given values, returning the previous value.
- * The function should be side-effect-free, since it may be
- * re-applied when attempted updates fail due to contention among
- * threads. The function is applied with the current value as its
- * first argument, and the given update as the second argument.
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
*
* @param obj An object whose field to get and set
* @param x the update value
@@ -349,14 +341,13 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given function
- * to the current and given values, returning the updated value.
- * The function should be side-effect-free, since it may be
- * re-applied when attempted updates fail due to contention among
- * threads. The function is applied with the current value as its
- * first argument, and the given update as the second argument.
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
*
* @param obj An object whose field to get and set
* @param x the update value
@@ -375,7 +366,7 @@
}
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe U = Unsafe.getUnsafe();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
@@ -427,17 +418,7 @@
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- // Access to protected field members is restricted to receivers only
- // of the accessing class, or one of its subclasses, and the
- // accessing class must in turn be a subclass (or package sibling)
- // of the protected member's defining class.
- // If the updater refers to a protected field of a declaring class
- // outside the current package, the receiver argument will be
- // narrowed to the type of the accessing class.
- this.cclass = (Modifier.isProtected(modifiers) &&
- tclass.isAssignableFrom(caller) &&
- !isSamePackage(tclass, caller))
- ? caller : tclass;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
this.offset = U.objectFieldOffset(field);
}
@@ -471,12 +452,12 @@
public final boolean compareAndSet(T obj, long expect, long update) {
accessCheck(obj);
- return U.compareAndSetLong(obj, offset, expect, update);
+ return U.compareAndSwapLong(obj, offset, expect, update);
}
public final boolean weakCompareAndSet(T obj, long expect, long update) {
accessCheck(obj);
- return U.compareAndSetLong(obj, offset, expect, update);
+ return U.compareAndSwapLong(obj, offset, expect, update);
}
public final void set(T obj, long newValue) {
@@ -486,7 +467,7 @@
public final void lazySet(T obj, long newValue) {
accessCheck(obj);
- U.putLongRelease(obj, offset, newValue);
+ U.putOrderedLong(obj, offset, newValue);
}
public final long get(T obj) {
@@ -526,7 +507,7 @@
}
private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe U = Unsafe.getUnsafe();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
@@ -538,8 +519,8 @@
LockedUpdater(final Class<T> tclass, final String fieldName,
final Class<?> caller) {
- final Field field;
- final int modifiers;
+ Field field = null;
+ int modifiers = 0;
try {
// Android-changed: Skip privilege escalation which is a noop on Android.
/*
@@ -578,17 +559,7 @@
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- // Access to protected field members is restricted to receivers only
- // of the accessing class, or one of its subclasses, and the
- // accessing class must in turn be a subclass (or package sibling)
- // of the protected member's defining class.
- // If the updater refers to a protected field of a declaring class
- // outside the current package, the receiver argument will be
- // narrowed to the type of the accessing class.
- this.cclass = (Modifier.isProtected(modifiers) &&
- tclass.isAssignableFrom(caller) &&
- !isSamePackage(tclass, caller))
- ? caller : tclass;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
this.offset = U.objectFieldOffset(field);
}
@@ -672,13 +643,4 @@
return false;
}
*/
-
- /**
- * Returns true if the two classes have the same class loader and
- * package qualifier
- */
- static boolean isSamePackage(Class<?> class1, Class<?> class2) {
- return class1.getClassLoader() == class2.getClassLoader()
- && Objects.equals(class1.getPackageName(), class2.getPackageName());
- }
}
diff --git a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
index 51ea84c..b49118b 100644
--- a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
+++ b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
@@ -35,9 +35,6 @@
package java.util.concurrent.atomic;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-
/**
* An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
@@ -191,19 +188,20 @@
casPair(current, Pair.of(expectedReference, newMark)));
}
- // VarHandle mechanics
- private static final VarHandle PAIR;
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PAIR;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair",
- Pair.class);
+ PAIR = U.objectFieldOffset
+ (AtomicMarkableReference.class.getDeclaredField("pair"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return PAIR.compareAndSet(this, cmp, val);
+ return U.compareAndSwapObject(this, PAIR, cmp, val);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
index 513a243..17423ad 100644
--- a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
+++ b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
@@ -40,13 +40,10 @@
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.util.Objects;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
-import jdk.internal.misc.Unsafe;
-import jdk.internal.reflect.CallerSensitive;
-import jdk.internal.reflect.Reflection;
-import java.lang.invoke.VarHandle;
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
/**
* A reflection-based utility that enables atomic updates to
@@ -79,10 +76,6 @@
* guarantee atomicity only with respect to other invocations of
* {@code compareAndSet} and {@code set} on the same updater.
*
- * <p>Object arguments for parameters of type {@code T} that are not
- * instances of the class passed to {@link #newUpdater} will result in
- * a {@link ClassCastException} being thrown.
- *
* @since 1.5
* @author Doug Lea
* @param <T> The type of the object holding the updatable field
@@ -175,8 +168,8 @@
public abstract void lazySet(T obj, V newValue);
/**
- * Returns the current value held in the field of the given object
- * managed by this updater.
+ * Gets the current value held in the field of the given object managed
+ * by this updater.
*
* @param obj An object whose field to get
* @return the current value
@@ -200,12 +193,10 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given
- * function, returning the previous value. The function should be
- * side-effect-free, since it may be re-applied when attempted
- * updates fail due to contention among threads.
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the previous
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
*
* @param obj An object whose field to get and set
* @param updateFunction a side-effect-free function
@@ -222,12 +213,10 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given
- * function, returning the updated value. The function should be
- * side-effect-free, since it may be re-applied when attempted
- * updates fail due to contention among threads.
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the updated
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
*
* @param obj An object whose field to get and set
* @param updateFunction a side-effect-free function
@@ -244,14 +233,13 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given function
- * to the current and given values, returning the previous value.
- * The function should be side-effect-free, since it may be
- * re-applied when attempted updates fail due to contention among
- * threads. The function is applied with the current value as its
- * first argument, and the given update as the second argument.
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
*
* @param obj An object whose field to get and set
* @param x the update value
@@ -270,14 +258,13 @@
}
/**
- * Atomically updates (with memory effects as specified by {@link
- * VarHandle#compareAndSet}) the field of the given object managed
- * by this updater with the results of applying the given function
- * to the current and given values, returning the updated value.
- * The function should be side-effect-free, since it may be
- * re-applied when attempted updates fail due to contention among
- * threads. The function is applied with the current value as its
- * first argument, and the given update as the second argument.
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
*
* @param obj An object whose field to get and set
* @param x the update value
@@ -297,7 +284,7 @@
private static final class AtomicReferenceFieldUpdaterImpl<T,V>
extends AtomicReferenceFieldUpdater<T,V> {
- private static final Unsafe U = Unsafe.getUnsafe();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
@@ -369,17 +356,7 @@
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- // Access to protected field members is restricted to receivers only
- // of the accessing class, or one of its subclasses, and the
- // accessing class must in turn be a subclass (or package sibling)
- // of the protected member's defining class.
- // If the updater refers to a protected field of a declaring class
- // outside the current package, the receiver argument will be
- // narrowed to the type of the accessing class.
- this.cclass = (Modifier.isProtected(modifiers) &&
- tclass.isAssignableFrom(caller) &&
- !isSamePackage(tclass, caller))
- ? caller : tclass;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
this.vclass = vclass;
this.offset = U.objectFieldOffset(field);
@@ -405,15 +382,6 @@
*/
/**
- * Returns true if the two classes have the same class loader and
- * package qualifier
- */
- private static boolean isSamePackage(Class<?> class1, Class<?> class2) {
- return class1.getClassLoader() == class2.getClassLoader()
- && Objects.equals(class1.getPackageName(), class2.getPackageName());
- }
-
- /**
* Checks that target argument is instance of cclass. On
* failure, throws cause.
*/
@@ -452,14 +420,14 @@
public final boolean compareAndSet(T obj, V expect, V update) {
accessCheck(obj);
valueCheck(update);
- return U.compareAndSetObject(obj, offset, expect, update);
+ return U.compareAndSwapObject(obj, offset, expect, update);
}
public final boolean weakCompareAndSet(T obj, V expect, V update) {
// same implementation as strong form for now
accessCheck(obj);
valueCheck(update);
- return U.compareAndSetObject(obj, offset, expect, update);
+ return U.compareAndSwapObject(obj, offset, expect, update);
}
public final void set(T obj, V newValue) {
@@ -471,7 +439,7 @@
public final void lazySet(T obj, V newValue) {
accessCheck(obj);
valueCheck(newValue);
- U.putObjectRelease(obj, offset, newValue);
+ U.putOrderedObject(obj, offset, newValue);
}
@SuppressWarnings("unchecked")
diff --git a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
index 47b7c7b..40ceeb2 100644
--- a/ojluni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
+++ b/ojluni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
@@ -35,9 +35,6 @@
package java.util.concurrent.atomic;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-
/**
* An {@code AtomicStampedReference} maintains an object reference
* along with an integer "stamp", that can be updated atomically.
@@ -191,19 +188,20 @@
casPair(current, Pair.of(expectedReference, newStamp)));
}
- // VarHandle mechanics
- private static final VarHandle PAIR;
+ // Unsafe mechanics
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PAIR;
static {
try {
- MethodHandles.Lookup l = MethodHandles.lookup();
- PAIR = l.findVarHandle(AtomicStampedReference.class, "pair",
- Pair.class);
+ PAIR = U.objectFieldOffset
+ (AtomicStampedReference.class.getDeclaredField("pair"));
} catch (ReflectiveOperationException e) {
- throw new ExceptionInInitializerError(e);
+ throw new Error(e);
}
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return PAIR.compareAndSet(this, cmp, val);
+ return U.compareAndSwapObject(this, PAIR, cmp, val);
}
}
diff --git a/ojluni/src/main/java/java/util/concurrent/package-info.java b/ojluni/src/main/java/java/util/concurrent/package-info.java
index 0e992a2..387068d 100644
--- a/ojluni/src/main/java/java/util/concurrent/package-info.java
+++ b/ojluni/src/main/java/java/util/concurrent/package-info.java
@@ -200,7 +200,7 @@
* concurrent collection is thread-safe, but not governed by a
* single exclusion lock. In the particular case of
* ConcurrentHashMap, it safely permits any number of
- * concurrent reads as well as a large number of concurrent
+ * concurrent reads as well as a tunable number of concurrent
* writes. "Synchronized" classes can be useful when you need
* to prevent all access to a collection via a single lock, at
* the expense of poorer scalability. In other cases in which
@@ -262,6 +262,7 @@
*
* </ul>
*
+ *
* The methods of all classes in {@code java.util.concurrent} and its
* subpackages extend these guarantees to higher-level
* synchronization. In particular:
diff --git a/ojluni/src/test/java/io/ByteArrayInputStream/ReadAllReadNTransferTo.java b/ojluni/src/test/java/io/ByteArrayInputStream/ReadAllReadNTransferTo.java
new file mode 100644
index 0000000..c3e6e6c
--- /dev/null
+++ b/ojluni/src/test/java/io/ByteArrayInputStream/ReadAllReadNTransferTo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.ByteArrayInputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Random;
+
+import org.testng.annotations.Test;
+
+/* @test
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main ReadAllReadNTransferTo
+ * @bug 8180451
+ * @summary Verify ByteArrayInputStream readAllBytes, readNBytes, and transferTo
+ * @key randomness
+ */
+public class ReadAllReadNTransferTo {
+ private static final int SIZE = 0x4d4d;
+
+ private static Random random = new Random();
+
+ @Test
+ public void testRead() throws IOException {
+ byte[] buf = new byte[SIZE];
+ random.nextBytes(buf);
+ int position = random.nextInt(SIZE/2);
+ int size = random.nextInt(SIZE - position);
+
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buf, position, size);
+ int off = size < 2 ? 0 : random.nextInt(size / 2);
+ int len = size - off < 1 ? 0 : random.nextInt(size - off);
+
+ byte[] bN = new byte[off + len];
+ if (bais.readNBytes(bN, off, len) != len) {
+ throw new RuntimeException("readNBytes return value");
+ }
+
+ if (!Arrays.equals(Arrays.copyOfRange(bN, off, off+len),
+ Arrays.copyOfRange(buf, position, position+len))) {
+ throw new RuntimeException("readNBytes content");
+ }
+
+ byte[] bAll = bais.readAllBytes();
+ Objects.requireNonNull(bAll, "readAllBytes return value");
+ if (bAll.length != size - len) {
+ throw new RuntimeException("readAllBytes return value length");
+ }
+ if (!Arrays.equals(bAll,
+ Arrays.copyOfRange( buf, position + len, position + len + bAll.length))) {
+ throw new RuntimeException("readAllBytes content");
+ }
+
+ // XXX transferTo()
+ bais = new ByteArrayInputStream(buf);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(buf.length);
+ if (bais.transferTo(baos) != buf.length) {
+ throw new RuntimeException("transferTo return value length");
+ }
+ if (!Arrays.equals(buf, baos.toByteArray())) {
+ throw new RuntimeException("transferTo content");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/ByteArrayOutputStream/ToString.java b/ojluni/src/test/java/io/ByteArrayOutputStream/ToString.java
new file mode 100644
index 0000000..cbd8781
--- /dev/null
+++ b/ojluni/src/test/java/io/ByteArrayOutputStream/ToString.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 1226190
+ @summary Heartbeat test of ByteArrayOutputStream's toString methods
+ */
+package test.java.io.ByteArrayOutputStream;
+
+import java.io.*;
+
+import org.testng.annotations.Test;
+
+public class ToString {
+
+ @Test
+ public void testToString() throws IOException {
+ String test = "This is a test.";
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ PrintStream p = new PrintStream(b);
+ p.print(test);
+ p.close();
+
+ if (! b.toString().equals(test))
+ throw new RuntimeException("Default encoding failed");
+ if (! b.toString("UTF8").equals(test))
+ throw new RuntimeException("UTF8 encoding failed");
+ if (! b.toString(0).equals(test))
+ throw new RuntimeException("Hibyte0 encoding failed");
+ }
+
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/ByteArrayOutputStream/Write.java b/ojluni/src/test/java/io/ByteArrayOutputStream/Write.java
new file mode 100644
index 0000000..2424263
--- /dev/null
+++ b/ojluni/src/test/java/io/ByteArrayOutputStream/Write.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 4017158 8180410
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run testng Write
+ * @summary Check for correct implementation of ByteArrayInputStream.write
+ * @key randomness
+ */
+package test.java.io.ByteArrayOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class Write {
+
+ private static void doBoundsTest(byte[] b, int off, int len,
+ ByteArrayOutputStream baos)
+ throws Exception {
+ if (b != null) {
+ System.out.println("ByteArrayOutStream.write: b.length = " +
+ b.length + " off = " + off + " len = " + len);
+ } else{
+ System.out.println("ByteArrayOutStream.write: b is null off = " +
+ off + " len = " + len);
+ }
+
+ try {
+ baos.write(b, off, len);
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println("IndexOutOfBoundsException is thrown: OKAY");
+ } catch (NullPointerException e) {
+ System.out.println("NullPointerException is thrown: OKAY");
+ } catch (Throwable e){
+ throw new RuntimeException("Unexpected Exception is thrown", e);
+ }
+
+ if (b != null) {
+ System.out.println("ByteArrayOutStream.writeBytes: b.length = " +
+ b.length);
+ } else{
+ System.out.println("ByteArrayOutStream.writeBytes: b is null");
+ }
+
+ try {
+ baos.writeBytes(b);
+ } catch (NullPointerException e) {
+ System.out.println("NullPointerException is thrown: OKAY");
+ } catch (Throwable e){
+ throw new RuntimeException("Unexpected Exception is thrown", e);
+ }
+ }
+
+ @Test
+ public void boundsTest() throws Exception {
+ byte array1[] = {1 , 2 , 3 , 4 , 5}; // Simple array
+
+ //Create new ByteArrayOutputStream object
+ ByteArrayOutputStream y1 = new ByteArrayOutputStream(5);
+
+ doBoundsTest(array1, 0, Integer.MAX_VALUE , y1);
+ doBoundsTest(array1, 0, array1.length+100, y1);
+ doBoundsTest(array1, -1, 2, y1);
+ doBoundsTest(array1, 0, -1, y1);
+ doBoundsTest(null, 0, 2, y1);
+ }
+
+ @Test
+ public void writeTest() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Random rnd = new Random();
+ final int size = 17 + rnd.nextInt(128);
+
+ byte[] b = new byte[size];
+ rnd.nextBytes(b);
+
+ int off1 = rnd.nextInt(size / 4) + 1;
+ int len1 = Math.min(rnd.nextInt(size / 4) + 1, size - off1);
+ int off2 = rnd.nextInt(size / 2) + 1;
+ int len2 = Math.min(rnd.nextInt(size / 2) + 1, size - off2);
+
+ System.out.format("size: %d, off1: %d, len1: %d, off2: %d, len2: %d%n",
+ size, off1, len1, off2, len2);
+
+ baos.write(b, off1, len1);
+ byte[] b1 = baos.toByteArray();
+ assertEquals(b1.length, len1, "Array length test 1 failed.");
+ assertEquals(b1, Arrays.copyOfRange(b, off1, off1 + len1),
+ "Array equality test 1 failed.");
+
+ baos.write(b, off2, len2);
+ byte[] b2 = baos.toByteArray();
+ assertEquals(b2.length, len1 + len2, "Array length test 2 failed.");
+ assertEquals(Arrays.copyOfRange(b2, 0, len1),
+ Arrays.copyOfRange(b, off1, off1 + len1),
+ "Array equality test 2A failed.");
+ assertEquals(Arrays.copyOfRange(b2, len1, len1 + len2),
+ Arrays.copyOfRange(b, off2, off2 + len2),
+ "Array equality test 2B failed.");
+
+ baos.writeBytes(b);
+ byte[] b3 = baos.toByteArray();
+ int len3 = len1 + len2 + b.length;
+ if (b3.length != len1 + len2 + b.length) {
+ throw new RuntimeException("Array length test 3 failed.");
+ }
+ assertEquals(b3.length, len3, "Array length test 3 failed.");
+ assertEquals(Arrays.copyOfRange(b3, 0, len1),
+ Arrays.copyOfRange(b, off1, off1 + len1),
+ "Array equality test 3A failed.");
+ assertEquals(Arrays.copyOfRange(b3, len1, len1 + len2),
+ Arrays.copyOfRange(b, off2, off2 + len2),
+ "Array equality test 3B failed.");
+ assertEquals(Arrays.copyOfRange(b3, len1 + len2, len3), b,
+ "Array equality test 3C failed.");
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/InputStream/NullInputStream.java b/ojluni/src/test/java/io/InputStream/NullInputStream.java
new file mode 100644
index 0000000..ad281b7
--- /dev/null
+++ b/ojluni/src/test/java/io/InputStream/NullInputStream.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.InputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 4358774 8139206
+ * @run testng NullInputStream
+ * @summary Check for expected behavior of InputStream.nullInputStream().
+ */
+public class NullInputStream {
+ private static InputStream openStream;
+ private static InputStream closedStream;
+
+ @BeforeGroups(groups="open")
+ public static void openStream() {
+ openStream = InputStream.nullInputStream();
+ }
+
+ @BeforeGroups(groups="closed")
+ public static void openAndCloseStream() {
+ closedStream = InputStream.nullInputStream();
+ try {
+ closedStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @AfterGroups(groups="open")
+ public static void closeStream() {
+ try {
+ openStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testOpen() {
+ assertNotNull(openStream, "InputStream.nullInputStream() returned null");
+ }
+
+ @Test(groups = "open")
+ public static void testAvailable() {
+ try {
+ assertEquals(0, openStream.available(), "available() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testRead() {
+ try {
+ assertEquals(-1, openStream.read(), "read() != -1");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadBII() {
+ try {
+ assertEquals(-1, openStream.read(new byte[1], 0, 1),
+ "read(byte[],int,int) != -1");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadAllBytes() {
+ try {
+ assertEquals(0, openStream.readAllBytes().length,
+ "readAllBytes().length != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadNBytes() {
+ try {
+ assertEquals(0, openStream.readNBytes(new byte[1], 0, 1),
+ "readNBytes(byte[],int,int) != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testReadNBytesWithLength() {
+ try {
+ assertEquals(0, openStream.readNBytes(-1).length,
+ "readNBytes(-1) != 0");
+ fail("Expected IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException iae) {
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ try {
+ assertEquals(0, openStream.readNBytes(0).length,
+ "readNBytes(0, false) != 0");
+ assertEquals(0, openStream.readNBytes(1).length,
+ "readNBytes(1, false) != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testSkip() {
+ try {
+ assertEquals(0, openStream.skip(1), "skip() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "open")
+ public static void testTransferTo() {
+ try {
+ assertEquals(0, openStream.transferTo(new ByteArrayOutputStream(7)),
+ "transferTo() != 0");
+ } catch (IOException ioe) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testAvailableClosed() {
+ try {
+ closedStream.available();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadClosed() {
+ try {
+ closedStream.read();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadBIIClosed() {
+ try {
+ closedStream.read(new byte[1], 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadAllBytesClosed() {
+ try {
+ closedStream.readAllBytes();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadNBytesClosed() {
+ try {
+ closedStream.readNBytes(new byte[1], 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testReadNBytesWithLengthClosed() {
+ try {
+ closedStream.readNBytes(1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testSkipClosed() {
+ try {
+ closedStream.skip(1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups = "closed")
+ public static void testTransferToClosed() {
+ try {
+ closedStream.transferTo(new ByteArrayOutputStream(7));
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/InputStream/ReadAllBytes.java b/ojluni/src/test/java/io/InputStream/ReadAllBytes.java
new file mode 100644
index 0000000..2851a1f
--- /dev/null
+++ b/ojluni/src/test/java/io/InputStream/ReadAllBytes.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.InputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Random;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @bug 8080835 8193832
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main ReadAllBytes
+ * @summary Basic test for InputStream.readAllBytes
+ * @key randomness
+ */
+
+public class ReadAllBytes {
+
+ private static Random generator = new Random();
+
+ @Test
+ public void testReadAllBytes() throws IOException {
+ test(new byte[]{});
+ test(new byte[]{1, 2, 3});
+ test(createRandomBytes(1024));
+ for (int shift : new int[] {13, 14, 15, 17}) {
+ for (int offset : new int[] {-1, 0, 1}) {
+ test(createRandomBytes((1 << shift) + offset));
+ }
+ }
+ }
+
+ static void test(byte[] expectedBytes) throws IOException {
+ int expectedLength = expectedBytes.length;
+ WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(expectedBytes));
+ byte[] readBytes = in.readAllBytes();
+
+ int x;
+ byte[] tmp = new byte[10];
+ check((x = in.read()) == -1,
+ "Expected end of stream from read(), got " + x);
+ check((x = in.read(tmp)) == -1,
+ "Expected end of stream from read(byte[]), got " + x);
+ check((x = in.read(tmp, 0, tmp.length)) == -1,
+ "Expected end of stream from read(byte[], int, int), got " + x);
+ check(in.readAllBytes().length == 0,
+ "Expected readAllBytes to return empty byte array");
+ check(expectedLength == readBytes.length,
+ "Expected length " + expectedLength + ", got " + readBytes.length);
+ check(Arrays.equals(expectedBytes, readBytes),
+ "Expected[" + expectedBytes + "], got:[" + readBytes + "]");
+ check(!in.isClosed(), "Stream unexpectedly closed");
+ }
+
+ static byte[] createRandomBytes(int size) {
+ byte[] bytes = new byte[size];
+ generator.nextBytes(bytes);
+ return bytes;
+ }
+
+ static void check(boolean cond, Object ... failedArgs) {
+ if (cond)
+ return;
+ StringBuilder sb = new StringBuilder();
+ for (Object o : failedArgs)
+ sb.append(o);
+ throw new RuntimeException(sb.toString());
+ }
+
+ static class WrapperInputStream extends FilterInputStream {
+ private boolean closed;
+ WrapperInputStream(InputStream in) { super(in); }
+ @Override public void close() throws IOException { closed = true; in.close(); }
+ boolean isClosed() { return closed; }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/InputStream/ReadNBytes.java b/ojluni/src/test/java/io/InputStream/ReadNBytes.java
new file mode 100644
index 0000000..68644ba
--- /dev/null
+++ b/ojluni/src/test/java/io/InputStream/ReadNBytes.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.InputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Random;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @bug 8080835 8139206
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main ReadNBytes
+ * @summary Basic test for InputStream.readNBytes
+ * @key randomness
+ */
+
+public class ReadNBytes {
+
+ private static Random generator = new Random();
+
+ @Test
+ public void testReadNBytes() throws IOException {
+ test(new byte[]{1, 2, 3});
+ test(createRandomBytes(1024));
+ for (int shift : new int[] {13, 15, 17}) {
+ for (int offset : new int[] {-1, 0, 1}) {
+ test(createRandomBytes((1 << shift) + offset));
+ }
+ }
+
+ test(-1);
+ test(0);
+ for (int shift : new int[] {13, 15, 17}) {
+ for (int offset : new int[] {-1, 0, 1}) {
+ test((1 << shift) + offset);
+ }
+ }
+ }
+
+ static void test(byte[] inputBytes) throws IOException {
+ int length = inputBytes.length;
+ WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(inputBytes));
+ byte[] readBytes = new byte[(length / 2) + 1];
+ int nread = in.readNBytes(readBytes, 0, readBytes.length);
+
+ int x;
+ byte[] tmp;
+ check(nread == readBytes.length,
+ "Expected number of bytes read: " + readBytes.length + ", got: " + nread);
+ check(Arrays.equals((tmp = Arrays.copyOf(inputBytes, nread)), readBytes),
+ "Expected[" + tmp + "], got:[" + readBytes + "]");
+ check(!in.isClosed(), "Stream unexpectedly closed");
+
+ // Read again
+ nread = in.readNBytes(readBytes, 0, readBytes.length);
+
+ check(nread == length - readBytes.length,
+ "Expected number of bytes read: " + (length - readBytes.length) + ", got: " + nread);
+ check(Arrays.equals((tmp = Arrays.copyOfRange(inputBytes, readBytes.length, length)),
+ Arrays.copyOf(readBytes, nread)),
+ "Expected[" + tmp + "], got:[" + readBytes + "]");
+ // Expect end of stream
+ check((x = in.read()) == -1,
+ "Expected end of stream from read(), got " + x);
+ check((x = in.read(tmp)) == -1,
+ "Expected end of stream from read(byte[]), got " + x);
+ check((x = in.read(tmp, 0, tmp.length)) == -1,
+ "Expected end of stream from read(byte[], int, int), got " + x);
+ check((x = in.readNBytes(tmp, 0, tmp.length)) == 0,
+ "Expected end of stream, 0, from readNBytes(byte[], int, int), got " + x);
+ check(!in.isClosed(), "Stream unexpectedly closed");
+ }
+
+ static void test(int max) throws IOException {
+ byte[] inputBytes = max <= 0 ? new byte[0] : createRandomBytes(max);
+ WrapperInputStream in =
+ new WrapperInputStream(new ByteArrayInputStream(inputBytes));
+
+ if (max < 0) {
+ try {
+ in.readNBytes(max);
+ check(false, "Expected IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException iae) {
+ return;
+ }
+ } else if (max == 0) {
+ int x;
+ check((x = in.readNBytes(max).length) == 0,
+ "Expected zero bytes, got " + x);
+ return;
+ }
+
+ int off = Math.toIntExact(in.skip(generator.nextInt(max/2)));
+ int len = generator.nextInt(max - 1 - off);
+ byte[] readBytes = in.readNBytes(len);
+ check(readBytes.length == len,
+ "Expected " + len + " bytes, got " + readBytes.length);
+ check(Arrays.equals(Arrays.copyOfRange(inputBytes, off, off + len), readBytes),
+ "Expected[" + Arrays.copyOfRange(inputBytes, off, off + len) +
+ "], got:[" + readBytes + "]");
+
+ int remaining = max - (off + len);
+ readBytes = in.readNBytes(remaining);
+ check(readBytes.length == remaining,
+ "Expected " + remaining + "bytes, got " + readBytes.length);
+ check(Arrays.equals(Arrays.copyOfRange(inputBytes, off + len, max),
+ readBytes),
+ "Expected[" + Arrays.copyOfRange(inputBytes, off + len, max) +
+ "], got:[" + readBytes + "]");
+
+ check(!in.isClosed(), "Stream unexpectedly closed");
+ }
+
+ static byte[] createRandomBytes(int size) {
+ byte[] bytes = new byte[size];
+ generator.nextBytes(bytes);
+ return bytes;
+ }
+
+ static void check(boolean cond, Object ... failedArgs) {
+ if (cond)
+ return;
+ StringBuilder sb = new StringBuilder();
+ for (Object o : failedArgs)
+ sb.append(o);
+ throw new RuntimeException(sb.toString());
+ }
+
+
+ static class WrapperInputStream extends FilterInputStream {
+ private boolean closed;
+ WrapperInputStream(InputStream in) { super(in); }
+ @Override public void close() throws IOException { closed = true; in.close(); }
+ boolean isClosed() { return closed; }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/InputStream/Skip.java b/ojluni/src/test/java/io/InputStream/Skip.java
new file mode 100644
index 0000000..47814d1
--- /dev/null
+++ b/ojluni/src/test/java/io/InputStream/Skip.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.InputStream;
+
+import org.testng.annotations.Test;
+
+/* @test
+ @bug 4016710
+ @summary check for correct implementation of InputStream.skip
+ */
+
+import java.io.*;
+
+
+public class Skip {
+
+ private static void dotest(InputStream in , int curpos ,
+ long total , long toskip , long expected)
+ throws Exception
+ {
+
+ try {
+
+ System.err.println("\n\nCurrently at pos = " + curpos +
+ "\nTotal bytes in the Stream = " + total +
+ "\nNumber of bytes to skip = " + toskip +
+ "\nNumber of bytes that should be skipped = " +
+ expected);
+
+ long skipped = in.skip(toskip);
+
+ System.err.println("actual number skipped: "+ skipped);
+
+ if ((skipped < 0) || (skipped > expected)) {
+ throw new RuntimeException("Unexpected number of bytes skipped");
+ }
+
+ } catch (IOException e) {
+ System.err.println("IOException is thrown - possible result");
+ } catch (Throwable e) {
+ throw new RuntimeException("Unexpected "+e+" is thrown!");
+ }
+
+ }
+
+ @Test
+ public void testSkip() throws Exception {
+
+ MyInputStream in = new MyInputStream(11);
+
+ /* test for negative skip */
+ dotest(in, 0, 11, -23, 0);
+
+ /* check for skip beyond EOF starting from before EOF */
+ dotest(in, 0, 11, 20, 11);
+
+ /* check for skip after EOF */
+ dotest(in, -1, 11, 20, 0);
+
+ in = new MyInputStream(9000);
+ /* check for skip equal to the read chunk size in InputStream.java */
+ dotest(in, 0, 9000, 2048, 2048);
+
+ /* check for skip greater than the read chunk size in InputStream.java */
+ dotest(in, 2048, 9000, 5000, 5000);
+
+ /* check for skip beyond EOF starting from before EOF */
+ dotest(in, 7048, 9000, 5000, 1952);
+
+ in = new MyInputStream(5000);
+
+ /* check for multiple chunk reads */
+ dotest(in, 0, 5000, 6000, 5000);
+
+ /*
+ * check for skip larger than Integer.MAX_VALUE
+ * (Takes about 2 hrs on a sparc ultra-1)
+ * long total = (long)Integer.MAX_VALUE + (long)10;
+ * long toskip = total - (long)6;
+ * in = new MyInputStream(total);
+ * dotest(in, 0, total, toskip, toskip);
+ */
+
+ }
+
+}
+
+class MyInputStream extends InputStream {
+
+ private int readctr = 0;
+ private long endoffile;
+
+ public MyInputStream(long endoffile) {
+ this.endoffile = endoffile;
+ }
+
+ public int read() {
+
+ if (readctr == endoffile) {
+ return -1;
+ }
+ else {
+ readctr++;
+ return 0;
+ }
+ }
+
+ public int available() { return 0; }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/InputStream/TransferTo.java b/ojluni/src/test/java/io/InputStream/TransferTo.java
new file mode 100644
index 0000000..b35bc1b
--- /dev/null
+++ b/ojluni/src/test/java/io/InputStream/TransferTo.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.InputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import org.testng.annotations.Test;
+
+import static java.lang.String.format;
+
+/*
+ * @test
+ * @bug 8066867
+ * @summary tests whether java.io.InputStream.transferTo conforms to its
+ * contract defined in the javadoc
+ * @key randomness
+ */
+public class TransferTo {
+
+ @Test
+ public void ifOutIsNullThenNpeIsThrown() throws IOException {
+ try (InputStream in = input()) {
+ assertThrowsNPE(() -> in.transferTo(null), "out");
+ }
+
+ try (InputStream in = input((byte) 1)) {
+ assertThrowsNPE(() -> in.transferTo(null), "out");
+ }
+
+ try (InputStream in = input((byte) 1, (byte) 2)) {
+ assertThrowsNPE(() -> in.transferTo(null), "out");
+ }
+
+ InputStream in = null;
+ try {
+ InputStream fin = in = new ThrowingInputStream();
+ // null check should precede everything else:
+ // InputStream shouldn't be touched if OutputStream is null
+ assertThrowsNPE(() -> fin.transferTo(null), "out");
+ } finally {
+ if (in != null)
+ try {
+ in.close();
+ } catch (IOException ignored) { }
+ }
+ }
+
+ @Test
+ public void ifExceptionInInputNeitherStreamIsClosed()
+ throws IOException {
+ transferToThenCheckIfAnyClosed(input(0, new byte[]{1, 2, 3}), output());
+ transferToThenCheckIfAnyClosed(input(1, new byte[]{1, 2, 3}), output());
+ transferToThenCheckIfAnyClosed(input(2, new byte[]{1, 2, 3}), output());
+ }
+
+ @Test
+ public void ifExceptionInOutputNeitherStreamIsClosed()
+ throws IOException {
+ transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(0));
+ transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(1));
+ transferToThenCheckIfAnyClosed(input(new byte[]{1, 2, 3}), output(2));
+ }
+
+ private static void transferToThenCheckIfAnyClosed(InputStream input,
+ OutputStream output)
+ throws IOException {
+ try (CloseLoggingInputStream in = new CloseLoggingInputStream(input);
+ CloseLoggingOutputStream out =
+ new CloseLoggingOutputStream(output)) {
+ boolean thrown = false;
+ try {
+ in.transferTo(out);
+ } catch (IOException ignored) {
+ thrown = true;
+ }
+ if (!thrown)
+ throw new AssertionError();
+
+ if (in.wasClosed() || out.wasClosed()) {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ @Test
+ public void onReturnNeitherStreamIsClosed()
+ throws IOException {
+ try (CloseLoggingInputStream in =
+ new CloseLoggingInputStream(input(new byte[]{1, 2, 3}));
+ CloseLoggingOutputStream out =
+ new CloseLoggingOutputStream(output())) {
+
+ in.transferTo(out);
+
+ if (in.wasClosed() || out.wasClosed()) {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ @Test
+ public void onReturnInputIsAtEnd() throws IOException {
+ try (InputStream in = input(new byte[]{1, 2, 3});
+ OutputStream out = output()) {
+
+ in.transferTo(out);
+
+ if (in.read() != -1) {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ @Test
+ public void contents() throws IOException {
+ checkTransferredContents(new byte[0]);
+ checkTransferredContents(createRandomBytes(1024, 4096));
+ // to span through several batches
+ checkTransferredContents(createRandomBytes(16384, 16384));
+ }
+
+ private static void checkTransferredContents(byte[] bytes)
+ throws IOException {
+ try (InputStream in = input(bytes);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ in.transferTo(out);
+
+ byte[] outBytes = out.toByteArray();
+ if (!Arrays.equals(bytes, outBytes)) {
+ throw new AssertionError(
+ format("bytes.length=%s, outBytes.length=%s",
+ bytes.length, outBytes.length));
+ }
+ }
+ }
+
+ private static byte[] createRandomBytes(int min, int maxRandomAdditive) {
+ Random rnd = new Random();
+ byte[] bytes = new byte[min + rnd.nextInt(maxRandomAdditive)];
+ rnd.nextBytes(bytes);
+ return bytes;
+ }
+
+ private static OutputStream output() {
+ return output(-1);
+ }
+
+ private static OutputStream output(int exceptionPosition) {
+ return new OutputStream() {
+
+ int pos;
+
+ @Override
+ public void write(int b) throws IOException {
+ if (pos++ == exceptionPosition)
+ throw new IOException();
+ }
+ };
+ }
+
+ private static InputStream input(byte... bytes) {
+ return input(-1, bytes);
+ }
+
+ private static InputStream input(int exceptionPosition, byte... bytes) {
+ return new InputStream() {
+
+ int pos;
+
+ @Override
+ public int read() throws IOException {
+ if (pos == exceptionPosition) {
+ // because of the pesky IOException swallowing in
+ // java.io.InputStream.read(byte[], int, int)
+ // pos++;
+ throw new IOException();
+ }
+
+ if (pos >= bytes.length)
+ return -1;
+ return bytes[pos++] & 0xff;
+ }
+ };
+ }
+
+ private static class ThrowingInputStream extends InputStream {
+
+ boolean closed;
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public int available() throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!closed) {
+ closed = true;
+ throw new IOException();
+ }
+ }
+
+ @Override
+ public void reset() throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public int read() throws IOException {
+ throw new IOException();
+ }
+ }
+
+ private static class CloseLoggingInputStream extends FilterInputStream {
+
+ boolean closed;
+
+ CloseLoggingInputStream(InputStream in) {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ super.close();
+ }
+
+ boolean wasClosed() {
+ return closed;
+ }
+ }
+
+ private static class CloseLoggingOutputStream extends FilterOutputStream {
+
+ boolean closed;
+
+ CloseLoggingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ super.close();
+ }
+
+ boolean wasClosed() {
+ return closed;
+ }
+ }
+
+ public interface Thrower {
+ public void run() throws Throwable;
+ }
+
+ public static void assertThrowsNPE(Thrower thrower, String message) {
+ assertThrows(thrower, NullPointerException.class, message);
+ }
+
+ public static <T extends Throwable> void assertThrows(Thrower thrower,
+ Class<T> throwable,
+ String message) {
+ Throwable thrown;
+ try {
+ thrower.run();
+ thrown = null;
+ } catch (Throwable caught) {
+ thrown = caught;
+ }
+
+ if (!throwable.isInstance(thrown)) {
+ String caught = thrown == null ?
+ "nothing" : thrown.getClass().getCanonicalName();
+ throw new AssertionError(
+ format("Expected to catch %s, but caught %s",
+ throwable, caught), thrown);
+ }
+
+ if (thrown != null && !message.equals(thrown.getMessage())) {
+ throw new AssertionError(
+ format("Expected exception message to be '%s', but it's '%s'",
+ message, thrown.getMessage()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/io/OutputStream/NullOutputStream.java b/ojluni/src/test/java/io/OutputStream/NullOutputStream.java
new file mode 100644
index 0000000..982313d
--- /dev/null
+++ b/ojluni/src/test/java/io/OutputStream/NullOutputStream.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.io.OutputStream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 4358774
+ * @run testng NullOutputStream
+ * @summary Check for expected behavior of OutputStream.nullOutputStream().
+ */
+public class NullOutputStream {
+ private static OutputStream openStream;
+ private static OutputStream closedStream;
+
+ @BeforeGroups(groups="open")
+ public static void openStream() {
+ openStream = OutputStream.nullOutputStream();
+ }
+
+ @BeforeGroups(groups="closed")
+ public static void openAndCloseStream() {
+ closedStream = OutputStream.nullOutputStream();
+ try {
+ closedStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @AfterGroups(groups="open")
+ public static void closeStream() {
+ try {
+ openStream.close();
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="open")
+ public static void testOpen() {
+ assertNotNull(openStream,
+ "OutputStream.nullOutputStream() returned null");
+ }
+
+ @Test(groups="open")
+ public static void testWrite() {
+ try {
+ openStream.write(62832);
+ } catch (IOException e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="open")
+ public static void testWriteBII() {
+ try {
+ openStream.write(new byte[] {(byte)6}, 0, 1);
+ } catch (Exception e) {
+ fail("Unexpected IOException");
+ }
+ }
+
+ @Test(groups="closed")
+ public static void testWriteClosed() {
+ try {
+ closedStream.write(62832);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+
+ @Test(groups="closed")
+ public static void testWriteBIIClosed() {
+ try {
+ closedStream.write(new byte[] {(byte)6}, 0, 1);
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java b/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java
index ada751c..e97a0ab 100644
--- a/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java
+++ b/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java
@@ -170,7 +170,7 @@
assertFalse(f.isCancelled());
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
@@ -225,7 +225,7 @@
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
assertTrue(f.isCancelled());
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
/**
@@ -372,12 +372,12 @@
f = new CompletableFuture<String>();
assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
f = new CompletableFuture<String>();
assertTrue(f.cancel(mayInterruptIfRunning));
- assertTrue(f.toString().contains("[Completed exceptionally:"));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
}
}
diff --git a/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java b/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java
index f821a3b..575a8f3 100644
--- a/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java
+++ b/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java
@@ -590,7 +590,6 @@
"AtomicReferenceArray9Test",
"ExecutorCompletionService9Test",
"ForkJoinPool9Test",
- "SubmissionPublisherTest",
};
addNamedTestClasses(suite, java9TestClassNames);
}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
index d0e4182..ce8baa8 100644
--- a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
+++ b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
@@ -856,7 +856,9 @@
1, 1, MILLISECONDS));
periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
1, 1, MILLISECONDS));
- delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ // Android-changed: Use a longer delay to ensure task does not expire
+ // delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.getQueue().containsAll(periodics));
assertTrue(p.getQueue().containsAll(delayeds));
diff --git a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java
index 7f85917..803adaf 100644
--- a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java
+++ b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java
@@ -804,7 +804,9 @@
1, 1, MILLISECONDS));
periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
1, 1, MILLISECONDS));
- delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ // Android-changed: Use a longer delay to ensure task does not expire
+ // delayeds.add(p.schedule(task, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.getQueue().containsAll(periodics));
assertTrue(p.getQueue().containsAll(delayeds));
diff --git a/ojluni/src/test/java/util/concurrent/tck/SubmissionPublisherTest.java b/ojluni/src/test/java/util/concurrent/tck/SubmissionPublisherTest.java
deleted file mode 100644
index bfdecbe..0000000
--- a/ojluni/src/test/java/util/concurrent/tck/SubmissionPublisherTest.java
+++ /dev/null
@@ -1,1036 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * This file is available under and governed by the GNU General Public
- * License version 2 only, as published by the Free Software Foundation.
- * However, the following notice accompanied the original version of this
- * file:
- *
- * Written by Doug Lea and Martin Buchholz with assistance from
- * members of JCP JSR-166 Expert Group and released to the public
- * domain, as explained at
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-package test.java.util.concurrent.tck;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Flow;
-import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.SubmissionPublisher;
-import java.util.concurrent.atomic.AtomicInteger;
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import static java.util.concurrent.Flow.Subscriber;
-import static java.util.concurrent.Flow.Subscription;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
-public class SubmissionPublisherTest extends JSR166TestCase {
-
- public static void main(String[] args) {
- main(suite(), args);
- }
- public static Test suite() {
- return new TestSuite(SubmissionPublisherTest.class);
- }
-
- final Executor basicExecutor = basicPublisher().getExecutor();
-
- static SubmissionPublisher<Integer> basicPublisher() {
- return new SubmissionPublisher<Integer>();
- }
-
- static class SPException extends RuntimeException {}
-
- class TestSubscriber implements Subscriber<Integer> {
- volatile Subscription sn;
- int last; // Requires that onNexts are in numeric order
- volatile int nexts;
- volatile int errors;
- volatile int completes;
- volatile boolean throwOnCall = false;
- volatile boolean request = true;
- volatile Throwable lastError;
-
- public synchronized void onSubscribe(Subscription s) {
- threadAssertTrue(sn == null);
- sn = s;
- notifyAll();
- if (throwOnCall)
- throw new SPException();
- if (request)
- sn.request(1L);
- }
- public synchronized void onNext(Integer t) {
- ++nexts;
- notifyAll();
- int current = t.intValue();
- threadAssertTrue(current >= last);
- last = current;
- if (request)
- sn.request(1L);
- if (throwOnCall)
- throw new SPException();
- }
- public synchronized void onError(Throwable t) {
- threadAssertTrue(completes == 0);
- threadAssertTrue(errors == 0);
- lastError = t;
- ++errors;
- notifyAll();
- }
- public synchronized void onComplete() {
- threadAssertTrue(completes == 0);
- ++completes;
- notifyAll();
- }
-
- synchronized void awaitSubscribe() {
- while (sn == null) {
- try {
- wait();
- } catch (Exception ex) {
- threadUnexpectedException(ex);
- break;
- }
- }
- }
- synchronized void awaitNext(int n) {
- while (nexts < n) {
- try {
- wait();
- } catch (Exception ex) {
- threadUnexpectedException(ex);
- break;
- }
- }
- }
- synchronized void awaitComplete() {
- while (completes == 0 && errors == 0) {
- try {
- wait();
- } catch (Exception ex) {
- threadUnexpectedException(ex);
- break;
- }
- }
- }
- synchronized void awaitError() {
- while (errors == 0) {
- try {
- wait();
- } catch (Exception ex) {
- threadUnexpectedException(ex);
- break;
- }
- }
- }
-
- }
-
- /**
- * A new SubmissionPublisher has no subscribers, a non-null
- * executor, a power-of-two capacity, is not closed, and reports
- * zero demand and lag
- */
- void checkInitialState(SubmissionPublisher<?> p) {
- assertFalse(p.hasSubscribers());
- assertEquals(0, p.getNumberOfSubscribers());
- assertTrue(p.getSubscribers().isEmpty());
- assertFalse(p.isClosed());
- assertNull(p.getClosedException());
- int n = p.getMaxBufferCapacity();
- assertTrue((n & (n - 1)) == 0); // power of two
- assertNotNull(p.getExecutor());
- assertEquals(0, p.estimateMinimumDemand());
- assertEquals(0, p.estimateMaximumLag());
- }
-
- /**
- * A default-constructed SubmissionPublisher has no subscribers,
- * is not closed, has default buffer size, and uses the
- * defaultExecutor
- */
- public void testConstructor1() {
- SubmissionPublisher<Integer> p = new SubmissionPublisher<>();
- checkInitialState(p);
- assertEquals(p.getMaxBufferCapacity(), Flow.defaultBufferSize());
- Executor e = p.getExecutor(), c = ForkJoinPool.commonPool();
- if (ForkJoinPool.getCommonPoolParallelism() > 1)
- assertSame(e, c);
- else
- assertNotSame(e, c);
- }
-
- /**
- * A new SubmissionPublisher has no subscribers, is not closed,
- * has the given buffer size, and uses the given executor
- */
- public void testConstructor2() {
- Executor e = Executors.newFixedThreadPool(1);
- SubmissionPublisher<Integer> p = new SubmissionPublisher<>(e, 8);
- checkInitialState(p);
- assertSame(p.getExecutor(), e);
- assertEquals(8, p.getMaxBufferCapacity());
- }
-
- /**
- * A null Executor argument to SubmissionPublisher constructor
- * throws NullPointerException
- */
- public void testConstructor3() {
- try {
- new SubmissionPublisher<Integer>(null, 8);
- shouldThrow();
- } catch (NullPointerException success) {}
- }
-
- /**
- * A negative capacity argument to SubmissionPublisher constructor
- * throws IllegalArgumentException
- */
- public void testConstructor4() {
- Executor e = Executors.newFixedThreadPool(1);
- try {
- new SubmissionPublisher<Integer>(e, -1);
- shouldThrow();
- } catch (IllegalArgumentException success) {}
- }
-
- /**
- * A closed publisher reports isClosed with no closedException and
- * throws IllegalStateException upon attempted submission; a
- * subsequent close or closeExceptionally has no additional
- * effect.
- */
- public void testClose() {
- SubmissionPublisher<Integer> p = basicPublisher();
- checkInitialState(p);
- p.close();
- assertTrue(p.isClosed());
- assertNull(p.getClosedException());
- try {
- p.submit(1);
- shouldThrow();
- } catch (IllegalStateException success) {}
- Throwable ex = new SPException();
- p.closeExceptionally(ex);
- assertTrue(p.isClosed());
- assertNull(p.getClosedException());
- }
-
- /**
- * A publisher closedExceptionally reports isClosed with the
- * closedException and throws IllegalStateException upon attempted
- * submission; a subsequent close or closeExceptionally has no
- * additional effect.
- */
- public void testCloseExceptionally() {
- SubmissionPublisher<Integer> p = basicPublisher();
- checkInitialState(p);
- Throwable ex = new SPException();
- p.closeExceptionally(ex);
- assertTrue(p.isClosed());
- assertSame(p.getClosedException(), ex);
- try {
- p.submit(1);
- shouldThrow();
- } catch (IllegalStateException success) {}
- p.close();
- assertTrue(p.isClosed());
- assertSame(p.getClosedException(), ex);
- }
-
- /**
- * Upon subscription, the subscriber's onSubscribe is called, no
- * other Subscriber methods are invoked, the publisher
- * hasSubscribers, isSubscribed is true, and existing
- * subscriptions are unaffected.
- */
- public void testSubscribe1() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- p.subscribe(s);
- assertTrue(p.hasSubscribers());
- assertEquals(1, p.getNumberOfSubscribers());
- assertTrue(p.getSubscribers().contains(s));
- assertTrue(p.isSubscribed(s));
- s.awaitSubscribe();
- assertNotNull(s.sn);
- assertEquals(0, s.nexts);
- assertEquals(0, s.errors);
- assertEquals(0, s.completes);
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s2);
- assertTrue(p.hasSubscribers());
- assertEquals(2, p.getNumberOfSubscribers());
- assertTrue(p.getSubscribers().contains(s));
- assertTrue(p.getSubscribers().contains(s2));
- assertTrue(p.isSubscribed(s));
- assertTrue(p.isSubscribed(s2));
- s2.awaitSubscribe();
- assertNotNull(s2.sn);
- assertEquals(0, s2.nexts);
- assertEquals(0, s2.errors);
- assertEquals(0, s2.completes);
- p.close();
- }
-
- /**
- * If closed, upon subscription, the subscriber's onComplete
- * method is invoked
- */
- public void testSubscribe2() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- p.close();
- p.subscribe(s);
- s.awaitComplete();
- assertEquals(0, s.nexts);
- assertEquals(0, s.errors);
- assertEquals(1, s.completes, 1);
- }
-
- /**
- * If closedExceptionally, upon subscription, the subscriber's
- * onError method is invoked
- */
- public void testSubscribe3() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- Throwable ex = new SPException();
- p.closeExceptionally(ex);
- assertTrue(p.isClosed());
- assertSame(p.getClosedException(), ex);
- p.subscribe(s);
- s.awaitError();
- assertEquals(0, s.nexts);
- assertEquals(1, s.errors);
- }
-
- /**
- * Upon attempted resubscription, the subscriber's onError is
- * called and the subscription is cancelled.
- */
- public void testSubscribe4() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- p.subscribe(s);
- assertTrue(p.hasSubscribers());
- assertEquals(1, p.getNumberOfSubscribers());
- assertTrue(p.getSubscribers().contains(s));
- assertTrue(p.isSubscribed(s));
- s.awaitSubscribe();
- assertNotNull(s.sn);
- assertEquals(0, s.nexts);
- assertEquals(0, s.errors);
- assertEquals(0, s.completes);
- p.subscribe(s);
- s.awaitError();
- assertEquals(0, s.nexts);
- assertEquals(1, s.errors);
- assertFalse(p.isSubscribed(s));
- }
-
- /**
- * An exception thrown in onSubscribe causes onError
- */
- public void testSubscribe5() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- s.throwOnCall = true;
- p.subscribe(s);
- s.awaitError();
- assertEquals(0, s.nexts);
- assertEquals(1, s.errors);
- assertEquals(0, s.completes);
- }
-
- /**
- * subscribe(null) throws NPE
- */
- public void testSubscribe6() {
- SubmissionPublisher<Integer> p = basicPublisher();
- try {
- p.subscribe(null);
- shouldThrow();
- } catch (NullPointerException success) {}
- checkInitialState(p);
- }
-
- /**
- * Closing a publisher causes onComplete to subscribers
- */
- public void testCloseCompletes() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- p.submit(1);
- p.close();
- assertTrue(p.isClosed());
- assertNull(p.getClosedException());
- s1.awaitComplete();
- assertEquals(1, s1.nexts);
- assertEquals(1, s1.completes);
- s2.awaitComplete();
- assertEquals(1, s2.nexts);
- assertEquals(1, s2.completes);
- }
-
- /**
- * Closing a publisher exceptionally causes onError to subscribers
- * after they are subscribed
- */
- public void testCloseExceptionallyError() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- p.submit(1);
- p.closeExceptionally(new SPException());
- assertTrue(p.isClosed());
- s1.awaitSubscribe();
- s1.awaitError();
- assertTrue(s1.nexts <= 1);
- assertEquals(1, s1.errors);
- s2.awaitSubscribe();
- s2.awaitError();
- assertTrue(s2.nexts <= 1);
- assertEquals(1, s2.errors);
- }
-
- /**
- * Cancelling a subscription eventually causes no more onNexts to be issued
- */
- public void testCancel() {
- SubmissionPublisher<Integer> p =
- new SubmissionPublisher<>(basicExecutor, 4); // must be < 20
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- s1.awaitSubscribe();
- p.submit(1);
- s1.sn.cancel();
- for (int i = 2; i <= 20; ++i)
- p.submit(i);
- p.close();
- s2.awaitComplete();
- assertEquals(20, s2.nexts);
- assertEquals(1, s2.completes);
- assertTrue(s1.nexts < 20);
- assertFalse(p.isSubscribed(s1));
- }
-
- /**
- * Throwing an exception in onNext causes onError
- */
- public void testThrowOnNext() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- s1.awaitSubscribe();
- p.submit(1);
- s1.throwOnCall = true;
- p.submit(2);
- p.close();
- s2.awaitComplete();
- assertEquals(2, s2.nexts);
- s1.awaitComplete();
- assertEquals(1, s1.errors);
- }
-
- /**
- * If a handler is supplied in constructor, it is invoked when
- * subscriber throws an exception in onNext
- */
- public void testThrowOnNextHandler() {
- AtomicInteger calls = new AtomicInteger();
- SubmissionPublisher<Integer> p = new SubmissionPublisher<>(
- basicExecutor, 8, (s, e) -> calls.getAndIncrement());
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- s1.awaitSubscribe();
- p.submit(1);
- s1.throwOnCall = true;
- p.submit(2);
- p.close();
- s2.awaitComplete();
- assertEquals(2, s2.nexts);
- assertEquals(1, s2.completes);
- s1.awaitError();
- assertEquals(1, s1.errors);
- assertEquals(1, calls.get());
- }
-
- /**
- * onNext items are issued in the same order to each subscriber
- */
- public void testOrder() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- for (int i = 1; i <= 20; ++i)
- p.submit(i);
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertEquals(20, s2.nexts);
- assertEquals(1, s2.completes);
- assertEquals(20, s1.nexts);
- assertEquals(1, s1.completes);
- }
-
- /**
- * onNext is issued only if requested
- */
- public void testRequest1() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- p.subscribe(s1);
- s1.awaitSubscribe();
- assertEquals(0, p.estimateMinimumDemand());
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s2);
- p.submit(1);
- p.submit(2);
- s2.awaitNext(1);
- assertEquals(0, s1.nexts);
- s1.sn.request(3);
- p.submit(3);
- p.close();
- s2.awaitComplete();
- assertEquals(3, s2.nexts);
- assertEquals(1, s2.completes);
- s1.awaitComplete();
- assertTrue(s1.nexts > 0);
- assertEquals(1, s1.completes);
- }
-
- /**
- * onNext is not issued when requests become zero
- */
- public void testRequest2() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- s1.request = false;
- p.submit(1);
- p.submit(2);
- p.close();
- s2.awaitComplete();
- assertEquals(2, s2.nexts);
- assertEquals(1, s2.completes);
- s1.awaitNext(1);
- assertEquals(1, s1.nexts);
- }
-
- /**
- * Non-positive request causes error
- */
- public void testRequest3() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- TestSubscriber s3 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- p.subscribe(s3);
- s3.awaitSubscribe();
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- s1.sn.request(-1L);
- s3.sn.request(0L);
- p.submit(1);
- p.submit(2);
- p.close();
- s2.awaitComplete();
- assertEquals(2, s2.nexts);
- assertEquals(1, s2.completes);
- s1.awaitError();
- assertEquals(1, s1.errors);
- assertTrue(s1.lastError instanceof IllegalArgumentException);
- s3.awaitError();
- assertEquals(1, s3.errors);
- assertTrue(s3.lastError instanceof IllegalArgumentException);
- }
-
- /**
- * estimateMinimumDemand reports 0 until request, nonzero after
- * request
- */
- public void testEstimateMinimumDemand() {
- TestSubscriber s = new TestSubscriber();
- SubmissionPublisher<Integer> p = basicPublisher();
- s.request = false;
- p.subscribe(s);
- s.awaitSubscribe();
- assertEquals(0, p.estimateMinimumDemand());
- s.sn.request(1);
- assertEquals(1, p.estimateMinimumDemand());
- }
-
- /**
- * submit to a publisher with no subscribers returns lag 0
- */
- public void testEmptySubmit() {
- SubmissionPublisher<Integer> p = basicPublisher();
- assertEquals(0, p.submit(1));
- }
-
- /**
- * submit(null) throws NPE
- */
- public void testNullSubmit() {
- SubmissionPublisher<Integer> p = basicPublisher();
- try {
- p.submit(null);
- shouldThrow();
- } catch (NullPointerException success) {}
- }
-
- /**
- * submit returns number of lagged items, compatible with result
- * of estimateMaximumLag.
- */
- public void testLaggedSubmit() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- assertEquals(1, p.submit(1));
- assertTrue(p.estimateMaximumLag() >= 1);
- assertTrue(p.submit(2) >= 2);
- assertTrue(p.estimateMaximumLag() >= 2);
- s1.sn.request(4);
- assertTrue(p.submit(3) >= 3);
- assertTrue(p.estimateMaximumLag() >= 3);
- s2.sn.request(4);
- p.submit(4);
- p.close();
- s2.awaitComplete();
- assertEquals(4, s2.nexts);
- s1.awaitComplete();
- assertEquals(4, s2.nexts);
- }
-
- /**
- * submit eventually issues requested items when buffer capacity is 1
- */
- public void testCap1Submit() {
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 1);
- TestSubscriber s1 = new TestSubscriber();
- TestSubscriber s2 = new TestSubscriber();
- p.subscribe(s1);
- p.subscribe(s2);
- for (int i = 1; i <= 20; ++i) {
- assertTrue(p.submit(i) >= 0);
- }
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertEquals(20, s2.nexts);
- assertEquals(1, s2.completes);
- assertEquals(20, s1.nexts);
- assertEquals(1, s1.completes);
- }
-
- static boolean noopHandle(AtomicInteger count) {
- count.getAndIncrement();
- return false;
- }
-
- static boolean reqHandle(AtomicInteger count, Subscriber s) {
- count.getAndIncrement();
- ((TestSubscriber)s).sn.request(Long.MAX_VALUE);
- return true;
- }
-
- /**
- * offer to a publisher with no subscribers returns lag 0
- */
- public void testEmptyOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- assertEquals(0, p.offer(1, null));
- }
-
- /**
- * offer(null) throws NPE
- */
- public void testNullOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- try {
- p.offer(null, null);
- shouldThrow();
- } catch (NullPointerException success) {}
- }
-
- /**
- * offer returns number of lagged items if not saturated
- */
- public void testLaggedOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- assertTrue(p.offer(1, null) >= 1);
- assertTrue(p.offer(2, null) >= 2);
- s1.sn.request(4);
- assertTrue(p.offer(3, null) >= 3);
- s2.sn.request(4);
- p.offer(4, null);
- p.close();
- s2.awaitComplete();
- assertEquals(4, s2.nexts);
- s1.awaitComplete();
- assertEquals(4, s2.nexts);
- }
-
- /**
- * offer reports drops if saturated
- */
- public void testDroppedOffer() {
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- for (int i = 1; i <= 4; ++i)
- assertTrue(p.offer(i, null) >= 0);
- p.offer(5, null);
- assertTrue(p.offer(6, null) < 0);
- s1.sn.request(64);
- assertTrue(p.offer(7, null) < 0);
- s2.sn.request(64);
- p.close();
- s2.awaitComplete();
- assertTrue(s2.nexts >= 4);
- s1.awaitComplete();
- assertTrue(s1.nexts >= 4);
- }
-
- /**
- * offer invokes drop handler if saturated
- */
- public void testHandledDroppedOffer() {
- AtomicInteger calls = new AtomicInteger();
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- for (int i = 1; i <= 4; ++i)
- assertTrue(p.offer(i, (s, x) -> noopHandle(calls)) >= 0);
- p.offer(4, (s, x) -> noopHandle(calls));
- assertTrue(p.offer(6, (s, x) -> noopHandle(calls)) < 0);
- s1.sn.request(64);
- assertTrue(p.offer(7, (s, x) -> noopHandle(calls)) < 0);
- s2.sn.request(64);
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertTrue(calls.get() >= 4);
- }
-
- /**
- * offer succeeds if drop handler forces request
- */
- public void testRecoveredHandledDroppedOffer() {
- AtomicInteger calls = new AtomicInteger();
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- int n = 0;
- for (int i = 1; i <= 8; ++i) {
- int d = p.offer(i, (s, x) -> reqHandle(calls, s));
- n = n + 2 + (d < 0 ? d : 0);
- }
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertEquals(n, s1.nexts + s2.nexts);
- assertTrue(calls.get() >= 2);
- }
-
- /**
- * Timed offer to a publisher with no subscribers returns lag 0
- */
- public void testEmptyTimedOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- long startTime = System.nanoTime();
- assertEquals(0, p.offer(1, LONG_DELAY_MS, MILLISECONDS, null));
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
- }
-
- /**
- * Timed offer with null item or TimeUnit throws NPE
- */
- public void testNullTimedOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- long startTime = System.nanoTime();
- try {
- p.offer(null, LONG_DELAY_MS, MILLISECONDS, null);
- shouldThrow();
- } catch (NullPointerException success) {}
- try {
- p.offer(1, LONG_DELAY_MS, null, null);
- shouldThrow();
- } catch (NullPointerException success) {}
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
- }
-
- /**
- * Timed offer returns number of lagged items if not saturated
- */
- public void testLaggedTimedOffer() {
- SubmissionPublisher<Integer> p = basicPublisher();
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- long startTime = System.nanoTime();
- assertTrue(p.offer(1, LONG_DELAY_MS, MILLISECONDS, null) >= 1);
- assertTrue(p.offer(2, LONG_DELAY_MS, MILLISECONDS, null) >= 2);
- s1.sn.request(4);
- assertTrue(p.offer(3, LONG_DELAY_MS, MILLISECONDS, null) >= 3);
- s2.sn.request(4);
- p.offer(4, LONG_DELAY_MS, MILLISECONDS, null);
- p.close();
- s2.awaitComplete();
- assertEquals(4, s2.nexts);
- s1.awaitComplete();
- assertEquals(4, s2.nexts);
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
- }
-
- /**
- * Timed offer reports drops if saturated
- */
- public void testDroppedTimedOffer() {
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- long delay = timeoutMillis();
- for (int i = 1; i <= 4; ++i)
- assertTrue(p.offer(i, delay, MILLISECONDS, null) >= 0);
- long startTime = System.nanoTime();
- assertTrue(p.offer(5, delay, MILLISECONDS, null) < 0);
- s1.sn.request(64);
- assertTrue(p.offer(6, delay, MILLISECONDS, null) < 0);
- // 2 * delay should elapse but check only 1 * delay to allow timer slop
- assertTrue(millisElapsedSince(startTime) >= delay);
- s2.sn.request(64);
- p.close();
- s2.awaitComplete();
- assertTrue(s2.nexts >= 2);
- s1.awaitComplete();
- assertTrue(s1.nexts >= 2);
- }
-
- /**
- * Timed offer invokes drop handler if saturated
- */
- public void testHandledDroppedTimedOffer() {
- AtomicInteger calls = new AtomicInteger();
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- long delay = timeoutMillis();
- for (int i = 1; i <= 4; ++i)
- assertTrue(p.offer(i, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) >= 0);
- long startTime = System.nanoTime();
- assertTrue(p.offer(5, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0);
- s1.sn.request(64);
- assertTrue(p.offer(6, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0);
- assertTrue(millisElapsedSince(startTime) >= delay);
- s2.sn.request(64);
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertTrue(calls.get() >= 2);
- }
-
- /**
- * Timed offer succeeds if drop handler forces request
- */
- public void testRecoveredHandledDroppedTimedOffer() {
- AtomicInteger calls = new AtomicInteger();
- SubmissionPublisher<Integer> p
- = new SubmissionPublisher<>(basicExecutor, 4);
- TestSubscriber s1 = new TestSubscriber();
- s1.request = false;
- TestSubscriber s2 = new TestSubscriber();
- s2.request = false;
- p.subscribe(s1);
- p.subscribe(s2);
- s2.awaitSubscribe();
- s1.awaitSubscribe();
- int n = 0;
- long delay = timeoutMillis();
- long startTime = System.nanoTime();
- for (int i = 1; i <= 6; ++i) {
- int d = p.offer(i, delay, MILLISECONDS, (s, x) -> reqHandle(calls, s));
- n = n + 2 + (d < 0 ? d : 0);
- }
- assertTrue(millisElapsedSince(startTime) >= delay);
- p.close();
- s2.awaitComplete();
- s1.awaitComplete();
- assertEquals(n, s1.nexts + s2.nexts);
- assertTrue(calls.get() >= 2);
- }
-
- /**
- * consume returns a CompletableFuture that is done when
- * publisher completes
- */
- public void testConsume() {
- AtomicInteger sum = new AtomicInteger();
- SubmissionPublisher<Integer> p = basicPublisher();
- CompletableFuture<Void> f =
- p.consume((Integer x) -> sum.getAndAdd(x.intValue()));
- int n = 20;
- for (int i = 1; i <= n; ++i)
- p.submit(i);
- p.close();
- f.join();
- assertEquals((n * (n + 1)) / 2, sum.get());
- }
-
- /**
- * consume(null) throws NPE
- */
- public void testConsumeNPE() {
- SubmissionPublisher<Integer> p = basicPublisher();
- try {
- CompletableFuture<Void> f = p.consume(null);
- shouldThrow();
- } catch (NullPointerException success) {}
- }
-
- /**
- * consume eventually stops processing published items if cancelled
- */
- public void testCancelledConsume() {
- AtomicInteger count = new AtomicInteger();
- SubmissionPublisher<Integer> p = basicPublisher();
- CompletableFuture<Void> f = p.consume(x -> count.getAndIncrement());
- f.cancel(true);
- int n = 1000000; // arbitrary limit
- for (int i = 1; i <= n; ++i)
- p.submit(i);
- assertTrue(count.get() < n);
- }
-
- /**
- * Tests scenario for
- * JDK-8187947: A race condition in SubmissionPublisher
- * cvs update -D '2017-11-25' src/main/java/util/concurrent/SubmissionPublisher.java && ant -Djsr166.expensiveTests=true -Djsr166.tckTestClass=SubmissionPublisherTest -Djsr166.methodFilter=testMissedSignal tck; cvs update -A src/main/java/util/concurrent/SubmissionPublisher.java
- */
- public void testMissedSignal_8187947() throws Exception {
- if (!atLeastJava9()) return; // backport to jdk8 too hard
- final int N = expensiveTests ? (1 << 20) : (1 << 10);
- final CountDownLatch finished = new CountDownLatch(1);
- final SubmissionPublisher<Boolean> pub = new SubmissionPublisher<>();
- class Sub implements Subscriber<Boolean> {
- int received;
- public void onSubscribe(Subscription s) {
- s.request(N);
- }
- public void onNext(Boolean item) {
- if (++received == N)
- finished.countDown();
- else
- CompletableFuture.runAsync(() -> pub.submit(Boolean.TRUE));
- }
- public void onError(Throwable t) { throw new AssertionError(t); }
- public void onComplete() {}
- }
- pub.subscribe(new Sub());
- CompletableFuture.runAsync(() -> pub.submit(Boolean.TRUE));
- await(finished);
- }
-}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TimeUnit8Test.java b/ojluni/src/test/java/util/concurrent/tck/TimeUnit8Test.java
deleted file mode 100644
index 5cddf1c..0000000
--- a/ojluni/src/test/java/util/concurrent/tck/TimeUnit8Test.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * This file is available under and governed by the GNU General Public
- * License version 2 only, as published by the Free Software Foundation.
- * However, the following notice accompanied the original version of this
- * file:
- *
- * Written by Doug Lea and Martin Buchholz with assistance from
- * members of JCP JSR-166 Expert Group and released to the public
- * domain, as explained at
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-package test.java.util.concurrent.tck;
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.MICROSECONDS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.LongStream;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-public class TimeUnit8Test extends JSR166TestCase {
- public static void main(String[] args) {
- main(suite(), args);
- }
-
- public static Test suite() {
- return new TestSuite(TimeUnit8Test.class);
- }
-
- /**
- * tests for toChronoUnit.
- */
- public void testToChronoUnit() throws Exception {
- assertSame(ChronoUnit.NANOS, NANOSECONDS.toChronoUnit());
- assertSame(ChronoUnit.MICROS, MICROSECONDS.toChronoUnit());
- assertSame(ChronoUnit.MILLIS, MILLISECONDS.toChronoUnit());
- assertSame(ChronoUnit.SECONDS, SECONDS.toChronoUnit());
- assertSame(ChronoUnit.MINUTES, MINUTES.toChronoUnit());
- assertSame(ChronoUnit.HOURS, HOURS.toChronoUnit());
- assertSame(ChronoUnit.DAYS, DAYS.toChronoUnit());
-
- // Every TimeUnit has a defined ChronoUnit equivalent
- for (TimeUnit x : TimeUnit.values())
- assertSame(x, TimeUnit.of(x.toChronoUnit()));
- }
-
- /**
- * tests for TimeUnit.of(ChronoUnit).
- */
- public void testTimeUnitOf() throws Exception {
- assertSame(NANOSECONDS, TimeUnit.of(ChronoUnit.NANOS));
- assertSame(MICROSECONDS, TimeUnit.of(ChronoUnit.MICROS));
- assertSame(MILLISECONDS, TimeUnit.of(ChronoUnit.MILLIS));
- assertSame(SECONDS, TimeUnit.of(ChronoUnit.SECONDS));
- assertSame(MINUTES, TimeUnit.of(ChronoUnit.MINUTES));
- assertSame(HOURS, TimeUnit.of(ChronoUnit.HOURS));
- assertSame(DAYS, TimeUnit.of(ChronoUnit.DAYS));
-
- assertThrows(NullPointerException.class,
- () -> TimeUnit.of((ChronoUnit)null));
-
- // ChronoUnits either round trip to their TimeUnit
- // equivalents, or throw IllegalArgumentException.
- for (ChronoUnit cu : ChronoUnit.values()) {
- final TimeUnit tu;
- try {
- tu = TimeUnit.of(cu);
- } catch (IllegalArgumentException acceptable) {
- continue;
- }
- assertSame(cu, tu.toChronoUnit());
- }
- }
-
- /**
- * convert(Duration) roundtrips with Duration.ofXXXX and Duration.of(long, ChronoUnit)
- */
- public void testConvertDuration_roundtripDurationOf() {
- long n = ThreadLocalRandom.current().nextLong();
-
- assertEquals(n, NANOSECONDS.convert(Duration.ofNanos(n)));
- assertEquals(n, NANOSECONDS.convert(Duration.of(n, ChronoUnit.NANOS)));
- assertEquals(n, MILLISECONDS.convert(Duration.ofMillis(n)));
- assertEquals(n, MILLISECONDS.convert(Duration.of(n, ChronoUnit.MILLIS)));
- assertEquals(n, SECONDS.convert(Duration.ofSeconds(n)));
- assertEquals(n, SECONDS.convert(Duration.of(n, ChronoUnit.SECONDS)));
- n /= 60;
- assertEquals(n, MINUTES.convert(Duration.ofMinutes(n)));
- assertEquals(n, MINUTES.convert(Duration.of(n, ChronoUnit.MINUTES)));
- n /= 60;
- assertEquals(n, HOURS.convert(Duration.ofHours(n)));
- assertEquals(n, HOURS.convert(Duration.of(n, ChronoUnit.HOURS)));
- n /= 24;
- assertEquals(n, DAYS.convert(Duration.ofDays(n)));
- assertEquals(n, DAYS.convert(Duration.of(n, ChronoUnit.DAYS)));
- }
-
- /**
- * convert(Duration.ofNanos(n)) agrees with convert(n, NANOSECONDS)
- */
- public void testConvertDuration_roundtripDurationOfNanos() {
- // Test values near unit transitions and near overflow.
- LongStream.concat(
- Arrays.stream(TimeUnit.values()).mapToLong(u -> u.toNanos(1)),
- LongStream.of(Long.MAX_VALUE, Long.MIN_VALUE))
- .flatMap(n -> LongStream.of(n, n + 1, n - 1))
- .flatMap(n -> LongStream.of(n, n + 1_000_000_000, n - 1_000_000_000))
- .flatMap(n -> LongStream.of(n, -n))
- // .peek(System.err::println)
- .forEach(n -> Arrays.stream(TimeUnit.values()).forEach(
- u -> assertEquals(u.convert(n, NANOSECONDS),
- u.convert(Duration.ofNanos(n)))));
- }
-
- /**
- * convert(Duration) doesn't misbehave near Long.MAX_VALUE and Long.MIN_VALUE.
- */
- public void testConvertDuration_nearOverflow() {
- ChronoUnit NANOS = ChronoUnit.NANOS;
- Duration maxDuration = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
- Duration minDuration = Duration.ofSeconds(Long.MIN_VALUE, 0);
-
- for (TimeUnit u : TimeUnit.values()) {
- ChronoUnit cu = u.toChronoUnit();
- long r;
- if (u.toNanos(1) > SECONDS.toNanos(1)) {
- r = u.toNanos(1) / SECONDS.toNanos(1);
-
- assertThrows(ArithmeticException.class,
- () -> Duration.of(Long.MAX_VALUE, cu),
- () -> Duration.of(Long.MIN_VALUE, cu));
- } else {
- r = 1;
-
- Duration max = Duration.of(Long.MAX_VALUE, cu);
- Duration min = Duration.of(Long.MIN_VALUE, cu);
- assertEquals(Long.MAX_VALUE, u.convert(max));
- assertEquals(Long.MAX_VALUE - 1, u.convert(max.minus(1, NANOS)));
- assertEquals(Long.MAX_VALUE - 1, u.convert(max.minus(1, cu)));
- assertEquals(Long.MIN_VALUE, u.convert(min));
- assertEquals(Long.MIN_VALUE + 1, u.convert(min.plus(1, NANOS)));
- assertEquals(Long.MIN_VALUE + 1, u.convert(min.plus(1, cu)));
- assertEquals(Long.MAX_VALUE, u.convert(max.plus(1, NANOS)));
- if (u != SECONDS) {
- assertEquals(Long.MAX_VALUE, u.convert(max.plus(1, cu)));
- assertEquals(Long.MIN_VALUE, u.convert(min.minus(1, NANOS)));
- assertEquals(Long.MIN_VALUE, u.convert(min.minus(1, cu)));
- }
- }
-
- assertEquals(Long.MAX_VALUE / r, u.convert(maxDuration));
- assertEquals(Long.MIN_VALUE / r, u.convert(minDuration));
- }
- }
-
-}
diff --git a/openjdk_java_files.bp b/openjdk_java_files.bp
index b13a5db..8eff1b3 100644
--- a/openjdk_java_files.bp
+++ b/openjdk_java_files.bp
@@ -980,7 +980,6 @@
"ojluni/src/main/java/java/util/concurrent/ScheduledFuture.java",
"ojluni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java",
"ojluni/src/main/java/java/util/concurrent/Semaphore.java",
- "ojluni/src/main/java/java/util/concurrent/SubmissionPublisher.java",
"ojluni/src/main/java/java/util/concurrent/SynchronousQueue.java",
"ojluni/src/main/java/java/util/concurrent/ThreadFactory.java",
"ojluni/src/main/java/java/util/concurrent/ThreadLocalRandom.java",