Add Thread.join(Duration) and Thread.sleep(Duration) methods
Bug: 417440397
Test: m droid
Change-Id: Ib12e07f113e744525553ccd55b9241a7142e66a6
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index 926b950..f15615c 100644
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -36,6 +36,7 @@
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
+import java.time.Duration;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
@@ -45,6 +46,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
+import jdk.internal.event.ThreadSleepEvent;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
@@ -67,6 +69,7 @@
import libcore.util.EmptyArray;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import android.compat.Compatibility;
@@ -588,14 +591,23 @@
return;
}
- final int nanosPerMilli = 1000000;
final long durationNanos;
- if (millis >= Long.MAX_VALUE / nanosPerMilli - 1L) {
- // > 292 years. Avoid overflow by capping it at roughly 292 years.
- durationNanos = Long.MAX_VALUE;
+ if (millis >= Long.MAX_VALUE / NANOS_PER_MILLI - 1L) {
+ // > 292 years. Avoid overflow by capping it at roughly 292 years.
+ durationNanos = Long.MAX_VALUE;
} else {
- durationNanos = (millis * nanosPerMilli) + nanos;
+ durationNanos = (millis * NANOS_PER_MILLI) + nanos;
}
+
+ sleep0(durationNanos);
+ // END Android-changed: Implement sleep() methods using a shared native implementation.
+ }
+
+ private static final int NANOS_PER_MILLI = 1000000;
+
+ // Android-changed: Implement sleep() methods using a shared native implementation.
+ // private static native void sleep0(long nanos) throws InterruptedException;
+ private static void sleep0(long durationNanos) throws InterruptedException {
long startNanos = System.nanoTime();
Object lock = currentThread().lock;
@@ -607,12 +619,85 @@
for (long elapsed = 0L; elapsed < durationNanos;
elapsed = System.nanoTime() - startNanos) {
final long remaining = durationNanos - elapsed;
- millis = remaining / nanosPerMilli;
- nanos = (int) (remaining % nanosPerMilli);
+ long millis = remaining / NANOS_PER_MILLI;
+ int nanos = (int) (remaining % NANOS_PER_MILLI);
sleep(lock, millis, nanos);
}
}
- // END Android-changed: Implement sleep() methods using a shared native implementation.
+ }
+
+ /**
+ * Causes the currently executing thread to sleep (temporarily cease
+ * execution) for the specified duration, subject to the precision and
+ * accuracy of system timers and schedulers. This method is a no-op if
+ * the duration is {@linkplain Duration#isNegative() negative}.
+ *
+ * @param duration
+ * the duration to sleep
+ *
+ * @throws InterruptedException
+ * if the current thread is interrupted while sleeping. The
+ * <i>interrupted status</i> of the current thread is
+ * cleared when this exception is thrown.
+ *
+ * @since 19
+ *
+ * @hide TODO: Expose this API.
+ */
+ public static void sleep(Duration duration) throws InterruptedException {
+ long nanos = NANOSECONDS.convert(duration); // MAX_VALUE if > 292 years
+ if (nanos < 0) {
+ return;
+ }
+
+ // Android-added: Handle nanos == 0 case.
+ // The JLS 3rd edition, section 17.9 says: "...sleep for zero
+ // time...need not have observable effects."
+ if (nanos == 0) {
+ // ...but we still have to handle being interrupted.
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+ return;
+ }
+
+ ThreadSleepEvent event = beforeSleep(nanos);
+ try {
+ sleep0(nanos);
+ } finally {
+ afterSleep(event);
+ }
+ }
+
+
+ /**
+ * Called before sleeping to create a jdk.ThreadSleep event.
+ */
+ private static ThreadSleepEvent beforeSleep(long nanos) {
+ ThreadSleepEvent event = null;
+ if (ThreadSleepEvent.isTurnedOn()) {
+ try {
+ event = new ThreadSleepEvent();
+ event.time = nanos;
+ event.begin();
+ } catch (OutOfMemoryError e) {
+ event = null;
+ }
+ }
+ return event;
+ }
+
+ /**
+ * Called after sleeping to commit the jdk.ThreadSleep event.
+ */
+ private static void afterSleep(ThreadSleepEvent event) {
+ if (event != null) {
+ try {
+ event.commit();
+ } catch (OutOfMemoryError e) {
+ // ignore
+ }
+ }
}
/**
@@ -2127,6 +2212,51 @@
}
/**
+ * Waits for this thread to terminate for up to the given waiting duration.
+ *
+ * <p> This method does not wait if the duration to wait is less than or
+ * equal to zero. In this case, the method just tests if the thread has
+ * terminated.
+ *
+ * @param duration
+ * the maximum duration to wait
+ *
+ * @return {@code true} if the thread has terminated, {@code false} if the
+ * thread has not terminated
+ *
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting.
+ * The <i>interrupted status</i> of the current thread is cleared
+ * when this exception is thrown.
+ *
+ * @throws IllegalThreadStateException
+ * if this thread has not been started.
+ *
+ * @since 19
+ *
+ * @hide TODO: Expose this new API.
+ */
+ public final boolean join(Duration duration) throws InterruptedException {
+ long nanos = NANOSECONDS.convert(duration); // MAX_VALUE if > 292 years
+
+ Thread.State state = threadState();
+ if (state == State.NEW)
+ throw new IllegalThreadStateException("Thread not started");
+ if (state == State.TERMINATED)
+ return true;
+ if (nanos <= 0)
+ return false;
+
+ // convert to milliseconds
+ long millis = MILLISECONDS.convert(nanos, NANOSECONDS);
+ if (nanos > NANOSECONDS.convert(millis, MILLISECONDS)) {
+ millis += 1L;
+ }
+ join(millis);
+ return isTerminated();
+ }
+
+ /**
* Prints a stack trace of the current thread to the standard error stream.
* This method is used only for debugging.
*