From 5812ee19693db8cc09cb3b3e4c7265299785d98d Mon Sep 17 00:00:00 2001 From: Thomas Nardone Date: Wed, 31 Jul 2024 11:28:07 -0700 Subject: [PATCH] Convert common.futures.SimpleSettableFuture (#45677) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45677 Changelog: [Internal] [Changed] - Convert com.facebook.react.common.futures.SimpleSettableFuture to Kotlin Reviewed By: rshest Differential Revision: D60237075 fbshipit-source-id: e8a94fa3bb5d09da85b13d86b55a020d42a770cd --- .../ReactAndroid/api/ReactAndroid.api | 10 +- .../common/futures/SimpleSettableFuture.java | 121 ------------------ .../common/futures/SimpleSettableFuture.kt | 111 ++++++++++++++++ 3 files changed, 116 insertions(+), 126 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 5b522c5a8c2..8a9d11116f6 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1923,17 +1923,17 @@ public class com/facebook/react/common/build/ReactBuildConfig { public fun ()V } -public class com/facebook/react/common/futures/SimpleSettableFuture : java/util/concurrent/Future { +public final class com/facebook/react/common/futures/SimpleSettableFuture : java/util/concurrent/Future { public fun ()V public fun cancel (Z)Z public fun get ()Ljava/lang/Object; public fun get (JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; - public fun getOrThrow ()Ljava/lang/Object; - public fun getOrThrow (JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; + public final fun getOrThrow ()Ljava/lang/Object; + public final fun getOrThrow (JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; public fun isCancelled ()Z public fun isDone ()Z - public fun set (Ljava/lang/Object;)V - public fun setException (Ljava/lang/Exception;)V + public final fun set (Ljava/lang/Object;)V + public final fun setException (Ljava/lang/Exception;)V } public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java deleted file mode 100644 index 14c618e4a5d..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.common.futures; - -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Nullsafe; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * A super simple Future-like class that can safely notify another Thread when a value is ready. - * Does not support canceling. - */ -@Nullsafe(Nullsafe.Mode.LOCAL) -public class SimpleSettableFuture implements Future { - private final CountDownLatch mReadyLatch = new CountDownLatch(1); - private @Nullable T mResult; - private @Nullable Exception mException; - - /** - * Sets the result. If another thread has called {@link #get}, they will immediately receive the - * value. set or setException must only be called once. - */ - public void set(@Nullable T result) { - checkNotSet(); - mResult = result; - mReadyLatch.countDown(); - } - - /** - * Sets the exception. If another thread has called {@link #get}, they will immediately receive - * the exception. set or setException must only be called once. - */ - public void setException(Exception exception) { - checkNotSet(); - mException = exception; - mReadyLatch.countDown(); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return mReadyLatch.getCount() == 0; - } - - @Override - public @Nullable T get() throws InterruptedException, ExecutionException { - mReadyLatch.await(); - if (mException != null) { - throw new ExecutionException(mException); - } - - return mResult; - } - - /** - * Wait up to the timeout time for another Thread to set a value on this future. If a value has - * already been set, this method will return immediately. - * - *

NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you are - * in the 1% of cases where you actually want to handle that. - */ - @Override - public @Nullable T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - if (!mReadyLatch.await(timeout, unit)) { - throw new TimeoutException("Timed out waiting for result"); - } - if (mException != null) { - throw new ExecutionException(mException); - } - - return mResult; - } - - /** - * Convenience wrapper for {@link #get()} that re-throws get()'s Exceptions as RuntimeExceptions. - */ - public @Nullable T getOrThrow() { - try { - return get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - - /** - * Convenience wrapper for {@link #get(long, TimeUnit)} that re-throws get()'s Exceptions as - * RuntimeExceptions. - */ - public @Nullable T getOrThrow(long timeout, TimeUnit unit) { - try { - return get(timeout, unit); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); - } - } - - private void checkNotSet() { - if (mReadyLatch.getCount() == 0) { - throw new RuntimeException("Result has already been set!"); - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.kt new file mode 100644 index 00000000000..79c5d5c3fdc --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.common.futures + +import java.util.concurrent.CountDownLatch +import java.util.concurrent.ExecutionException +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +/** + * A super simple Future-like class that can safely notify another Thread when a value is ready. + * Does not support canceling. + */ +public class SimpleSettableFuture : Future { + + private val readyLatch = CountDownLatch(1) + private var result: T? = null + private var exception: Exception? = null + + /** + * Sets the result. If another thread has called [get], they will immediately receive the value. + * set or setException must only be called once. + */ + public fun set(result: T?): Unit { + checkNotSet() + this.result = result + readyLatch.countDown() + } + + /** + * Sets the exception. If another thread has called [get], they will immediately receive the + * exception. set or setException must only be called once. + */ + public fun setException(exception: Exception): Unit { + checkNotSet() + this.exception = exception + readyLatch.countDown() + } + + override fun cancel(mayInterruptIfRunning: Boolean): Boolean { + throw UnsupportedOperationException() + } + + override fun isCancelled(): Boolean = false + + override fun isDone(): Boolean = readyLatch.count == 0L + + @Throws(InterruptedException::class, ExecutionException::class) + override fun get(): T? { + readyLatch.await() + if (exception != null) { + throw ExecutionException(exception) + } + return result + } + + /** + * Wait up to the timeout time for another Thread to set a value on this future. If a value has + * already been set, this method will return immediately. + * + * NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you are in + * the 1% of cases where you actually want to handle that. + */ + @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class) + override fun get(timeout: Long, unit: TimeUnit): T? { + if (!readyLatch.await(timeout, unit)) { + throw TimeoutException("Timed out waiting for result") + } + if (exception != null) { + throw ExecutionException(exception) + } + return result + } + + /** Convenience wrapper for [get()] that re-throws get()'s Exceptions as RuntimeExceptions. */ + public fun getOrThrow(): T? = + try { + get() + } catch (e: InterruptedException) { + throw RuntimeException(e) + } catch (e: ExecutionException) { + throw RuntimeException(e) + } + + /** + * Convenience wrapper for [get(long, TimeUnit)] that re-throws get()'s Exceptions as + * RuntimeExceptions. + */ + public fun getOrThrow(timeout: Long, unit: TimeUnit): T? = + try { + get(timeout, unit) + } catch (e: InterruptedException) { + throw RuntimeException(e) + } catch (e: ExecutionException) { + throw RuntimeException(e) + } catch (e: TimeoutException) { + throw RuntimeException(e) + } + + private fun checkNotSet() { + if (readyLatch.count == 0L) { + throw RuntimeException("Result has already been set!") + } + } +}