Skip to content

Commit

Permalink
Revert "Introduce EXACTLY_ONCE contracts to coroutineScope, superviso…
Browse files Browse the repository at this point in the history
…rScope, withContext, runBlocking, withTimeout and select (#2030)"

This reverts commit 397f10e
  • Loading branch information
erokhins authored and qwwdfsad committed Aug 18, 2020
1 parent bbc99b9 commit 9d79f33
Show file tree
Hide file tree
Showing 9 changed files with 32 additions and 138 deletions.
11 changes: 2 additions & 9 deletions integration/kotlinx-coroutines-jdk8/src/time/Time.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines.time

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.selects.*
import java.time.*
import java.time.temporal.*
import kotlin.contracts.*

/**
* "java.time" adapter method for [kotlinx.coroutines.delay].
Expand Down Expand Up @@ -38,12 +35,8 @@ public fun <R> SelectBuilder<R>.onTimeout(duration: Duration, block: suspend ()
/**
* "java.time" adapter method for [kotlinx.coroutines.withTimeout].
*/
public suspend fun <T> withTimeout(duration: Duration, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return kotlinx.coroutines.withTimeout(duration.coerceToMillis(), block)
}
public suspend fun <T> withTimeout(duration: Duration, block: suspend CoroutineScope.() -> T): T =
kotlinx.coroutines.withTimeout(duration.coerceToMillis(), block)

/**
* "java.time" adapter method for [kotlinx.coroutines.withTimeoutOrNull].
Expand Down
49 changes: 21 additions & 28 deletions kotlinx-coroutines-core/common/src/Builders.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@

@file:JvmMultifileClass
@file:JvmName("BuildersKt")
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines

import kotlinx.atomicfu.*
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.intrinsics.*
import kotlinx.coroutines.selects.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.jvm.*
Expand Down Expand Up @@ -136,36 +134,31 @@ private class LazyDeferredCoroutine<T>(
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
): T = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// compute new context
val oldContext = uCont.context
val newContext = oldContext + context
// always check for cancellation of new context
newContext.checkCompletion()
// FAST PATH #1 -- new context is the same as the old one
if (newContext === oldContext) {
val coroutine = ScopeCoroutine(newContext, uCont)
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// compute new context
val oldContext = uCont.context
val newContext = oldContext + context
// always check for cancellation of new context
newContext.checkCompletion()
// FAST PATH #1 -- new context is the same as the old one
if (newContext === oldContext) {
val coroutine = ScopeCoroutine(newContext, uCont)
// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
// `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
val coroutine = UndispatchedCoroutine(newContext, uCont)
// There are changes in the context, so this thread needs to be updated
withCoroutineContext(newContext, null) {
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
// `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
val coroutine = UndispatchedCoroutine(newContext, uCont)
// There are changes in the context, so this thread needs to be updated
withCoroutineContext(newContext, null) {
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
// SLOW PATH -- use new dispatcher
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
// SLOW PATH -- use new dispatcher
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}

/**
Expand Down
10 changes: 2 additions & 8 deletions kotlinx-coroutines-core/common/src/CoroutineScope.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines

import kotlinx.coroutines.internal.*
import kotlinx.coroutines.intrinsics.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*

Expand Down Expand Up @@ -185,15 +183,11 @@ public object GlobalScope : CoroutineScope {
* or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
* (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
*/
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}

/**
* Creates a [CoroutineScope] that wraps the given coroutine [context].
Expand Down
11 changes: 3 additions & 8 deletions kotlinx-coroutines-core/common/src/Supervisor.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalContracts::class)

@file:Suppress("DEPRECATION_ERROR")

package kotlinx.coroutines

import kotlinx.coroutines.internal.*
import kotlinx.coroutines.intrinsics.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.jvm.*
Expand Down Expand Up @@ -48,15 +47,11 @@ public fun SupervisorJob0(parent: Job? = null) : Job = SupervisorJob(parent)
* A failure of the scope itself (exception thrown in the [block] or cancellation) fails the scope with all its children,
* but does not cancel parent job.
*/
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = SupervisorCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}

private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
override fun childCancelled(cause: Throwable): Boolean = false
Expand Down
13 changes: 2 additions & 11 deletions kotlinx-coroutines-core/common/src/Timeout.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines

import kotlinx.coroutines.internal.*
import kotlinx.coroutines.intrinsics.*
import kotlinx.coroutines.selects.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.jvm.*
Expand All @@ -29,9 +27,6 @@ import kotlin.time.*
* @param timeMillis timeout time in milliseconds.
*/
public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
if (timeMillis <= 0L) throw TimeoutCancellationException("Timed out immediately")
return suspendCoroutineUninterceptedOrReturn { uCont ->
setupTimeout(TimeoutCoroutine(timeMillis, uCont), block)
Expand All @@ -51,12 +46,8 @@ public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineSco
* Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
*/
@ExperimentalTime
public suspend fun <T> withTimeout(timeout: Duration, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withTimeout(timeout.toDelayMillis(), block)
}
public suspend fun <T> withTimeout(timeout: Duration, block: suspend CoroutineScope.() -> T): T =
withTimeout(timeout.toDelayMillis(), block)

/**
* Runs a given suspending block of code inside a coroutine with a specified [timeout][timeMillis] and returns
Expand Down
10 changes: 2 additions & 8 deletions kotlinx-coroutines-core/common/src/selects/Select.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines.selects

Expand All @@ -11,7 +10,6 @@ import kotlinx.coroutines.channels.*
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.intrinsics.*
import kotlinx.coroutines.sync.*
import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
import kotlin.jvm.*
Expand Down Expand Up @@ -201,11 +199,8 @@ public interface SelectInstance<in R> {
* Note that this function does not check for cancellation when it is not suspended.
* Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
*/
public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() -> Unit): R {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() -> Unit): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val scope = SelectBuilderImpl(uCont)
try {
builder(scope)
Expand All @@ -214,7 +209,6 @@ public suspend inline fun <R> select(crossinline builder: SelectBuilder<R>.() ->
}
scope.getResult()
}
}


@SharedImmutable
Expand Down
52 changes: 0 additions & 52 deletions kotlinx-coroutines-core/common/test/BuilderContractsTest.kt

This file was deleted.

5 changes: 0 additions & 5 deletions kotlinx-coroutines-core/jvm/src/Builders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

@file:JvmMultifileClass
@file:JvmName("BuildersKt")
@file:OptIn(ExperimentalContracts::class)

package kotlinx.coroutines

import java.util.concurrent.locks.*
import kotlin.contracts.*
import kotlin.coroutines.*

/**
Expand All @@ -36,9 +34,6 @@ import kotlin.coroutines.*
*/
@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
Expand Down
9 changes: 0 additions & 9 deletions kotlinx-coroutines-core/jvm/test/RunBlockingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,4 @@ class RunBlockingTest : TestBase() {

handle.dispose()
}

@Test
fun testContract() {
val rb: Int
runBlocking {
rb = 42
}
rb.hashCode() // unused
}
}

0 comments on commit 9d79f33

Please sign in to comment.