Skip to content

Introduce deprecated coroutine builder overloads accepting a Job #4435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ public final class kotlinx/coroutines/guava/ListenableFutureKt {
public static final fun asListenableFuture (Lkotlinx/coroutines/Deferred;)Lcom/google/common/util/concurrent/ListenableFuture;
public static final fun await (Lcom/google/common/util/concurrent/ListenableFuture;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lcom/google/common/util/concurrent/ListenableFuture;
public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lcom/google/common/util/concurrent/ListenableFuture;
public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/google/common/util/concurrent/ListenableFuture;
public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/google/common/util/concurrent/ListenableFuture;
}

19 changes: 19 additions & 0 deletions integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ public fun <T> CoroutineScope.future(
return coroutine.future
}

/**
* Deprecated version of [future] that accepts a [Job].
*
* See the documentation for the non-deprecated [future] function to learn about the functionality of this function.
*
* See the documentation for the deprecated [async] overload accepting a [Job] for an explanation of the reason
* this pattern is deprecated and the list of possible alternatives.
*/
@Deprecated(
"Passing a Job to coroutine builders breaks structured concurrency, leading to hard-to-diagnose errors. " +
"This pattern should be avoided. " +
"This overload will be deprecated with an error in the future.",
level = DeprecationLevel.WARNING)
public fun <T> CoroutineScope.future(
context: Job,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): ListenableFuture<T> = future(context as CoroutineContext, start, block)

/**
* Returns a [Deferred] that is completed or failed by `this` [ListenableFuture].
*
Expand Down
12 changes: 12 additions & 0 deletions kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,19 @@ public final class kotlinx/coroutines/GlobalScope : kotlinx/coroutines/Coroutine
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
}

public final class kotlinx/coroutines/GuidanceJvmKt {
public static final fun runInterruptible (Lkotlinx/coroutines/Job;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class kotlinx/coroutines/GuidanceKt {
public static final fun async (Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Deferred;
public static final fun async (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Deferred;
public static synthetic fun async$default (Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Deferred;
public static synthetic fun async$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Deferred;
public static final fun launch (Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static final fun launch (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static synthetic fun launch$default (Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static synthetic fun launch$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
}

public abstract interface annotation class kotlinx/coroutines/InternalCoroutinesApi : java/lang/annotation/Annotation {
Expand Down Expand Up @@ -796,6 +804,8 @@ public final class kotlinx/coroutines/channels/ChannelsKt {
public static final synthetic fun minWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final synthetic fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final synthetic fun onReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1;
public static final fun produce (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;ILkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun produce$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;ILkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final synthetic fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final synthetic fun requireNoNulls (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final synthetic fun sendBlocking (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)V
Expand Down Expand Up @@ -1265,7 +1275,9 @@ public final class kotlinx/coroutines/future/FutureKt {
public static final fun asDeferred (Ljava/util/concurrent/CompletionStage;)Lkotlinx/coroutines/Deferred;
public static final fun await (Ljava/util/concurrent/CompletionStage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Ljava/util/concurrent/CompletableFuture;
public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Ljava/util/concurrent/CompletableFuture;
public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture;
public static synthetic fun future$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/Job;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture;
}

public final class kotlinx/coroutines/intrinsics/CancellableKt {
Expand Down
9 changes: 9 additions & 0 deletions kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/cancel(kotlin.c
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/cancel(kotlin/String, kotlin/Throwable? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.String;kotlin.Throwable?){}[0]
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/ensureActive() // kotlinx.coroutines/ensureActive|[email protected](){}[0]
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/launch(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, kotlin/Unit>): kotlinx.coroutines/Job // kotlinx.coroutines/launch|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>){}[0]
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/launch(kotlinx.coroutines/Job, kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, kotlin/Unit>): kotlinx.coroutines/Job // kotlinx.coroutines/launch|[email protected](kotlinx.coroutines.Job;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>){}[0]
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/newCoroutineContext(kotlin.coroutines/CoroutineContext): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/newCoroutineContext|[email protected](kotlin.coroutines.CoroutineContext){}[0]
final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/plus(kotlin.coroutines/CoroutineContext): kotlinx.coroutines/CoroutineScope // kotlinx.coroutines/plus|[email protected](kotlin.coroutines.CoroutineContext){}[0]
final fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancel(kotlin/String, kotlin/Throwable? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.String;kotlin.Throwable?){}[0]
Expand Down Expand Up @@ -912,7 +913,9 @@ final fun <#A: kotlin/Any?> (kotlinx.coroutines/CompletableDeferred<#A>).kotlinx
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/broadcast(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin/Function1<kotlin/Throwable?, kotlin/Unit>? = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/BroadcastChannel<#A> // kotlinx.coroutines.channels/broadcast|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.CoroutineStart;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/produce(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/produce|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/produce(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin/Function1<kotlin/Throwable?, kotlin/Unit>? = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/produce|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.CoroutineStart;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/produce(kotlinx.coroutines/Job, kotlin/Int = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/produce|[email protected](kotlinx.coroutines.Job;kotlin.Int;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/async(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlinx.coroutines/Deferred<#A> // kotlinx.coroutines/async|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/async(kotlinx.coroutines/Job, kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlinx.coroutines/Deferred<#A> // kotlinx.coroutines/async|[email protected](kotlinx.coroutines.Job;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/BroadcastChannel(kotlin/Int): kotlinx.coroutines.channels/BroadcastChannel<#A> // kotlinx.coroutines.channels/BroadcastChannel|BroadcastChannel(kotlin.Int){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/Channel(kotlin/Int = ...): kotlinx.coroutines.channels/Channel<#A> // kotlinx.coroutines.channels/Channel|Channel(kotlin.Int){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/Channel(kotlin/Int = ..., kotlinx.coroutines.channels/BufferOverflow = ..., kotlin/Function1<#A, kotlin/Unit>? = ...): kotlinx.coroutines.channels/Channel<#A> // kotlinx.coroutines.channels/Channel|Channel(kotlin.Int;kotlinx.coroutines.channels.BufferOverflow;kotlin.Function1<0:0,kotlin.Unit>?){0§<kotlin.Any?>}[0]
Expand Down Expand Up @@ -1083,6 +1086,9 @@ final fun <#A: kotlin/Any?> (kotlin.js/Promise<#A>).kotlinx.coroutines/asDeferre
// Targets: [js]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<#A> // kotlinx.coroutines/promise|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]

// Targets: [js]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlinx.coroutines/Job, kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<#A> // kotlinx.coroutines/promise|[email protected](kotlinx.coroutines.Job;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]

// Targets: [js]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/Deferred<#A>).kotlinx.coroutines/asPromise(): kotlin.js/Promise<#A> // kotlinx.coroutines/asPromise|[email protected]<0:0>(){0§<kotlin.Any?>}[0]

Expand All @@ -1098,6 +1104,9 @@ final fun <#A: kotlin/Any?> (kotlin.js/Promise<kotlin.js/JsAny?>).kotlinx.corout
// Targets: [wasmJs]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<kotlin.js/JsAny?> // kotlinx.coroutines/promise|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]

// Targets: [wasmJs]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlinx.coroutines/Job, kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<kotlin.js/JsAny?> // kotlinx.coroutines/promise|[email protected](kotlinx.coroutines.Job;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]

// Targets: [wasmJs]
final fun <#A: kotlin/Any?> (kotlinx.coroutines/Deferred<#A>).kotlinx.coroutines/asPromise(): kotlin.js/Promise<kotlin.js/JsAny?> // kotlinx.coroutines/asPromise|[email protected]<0:0>(){0§<kotlin.Any?>}[0]

Expand Down
23 changes: 23 additions & 0 deletions kotlinx-coroutines-core/common/src/Builders.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,29 @@ private class LazyDeferredCoroutine<T>(
* The cancellation behaviour described above is enabled if and only if the dispatcher is being changed.
* For example, when using `withContext(NonCancellable) { ... }` there is no change in dispatcher and
* this call will not be cancelled neither on entry to the block inside `withContext` nor on exit from it.
*
* It is incorrect to pass any instances of [Job] other than [NonCancellable] to [withContext].
* This does not raise an exception only for preserving backward compatibility.
* If the purpose of passing a [Job] to this function is to ensure that [block] gets cancelled when that job
* gets cancelled, this pattern can be used instead:
*
* ```
* val deferred = scopeWithTheRequiredJob.async {
* withContext(extraContextWithoutJob) {
* // your code here
* }
* }
* try {
* deferred.await()
* } finally {
* // if `await` fails because the current job is cancelled,
* // also cancel the now-unnecessary computations
* deferred.cancel()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be pointed out that just calling cancel on deferred won't prevent the current coroutine from completing before the now cancelled coroutine in the other scope completes?

* }
* ```
*
* This way, the [block] gets cancelled when either the caller or `scopeWithTheRequiredJob` gets cancelled,
* ensuring that unnecessary computations do not keep executing
*/
public suspend fun <T> withContext(
context: CoroutineContext,
Expand Down
Loading