Rewrite assertions so that they are checked only in debug mode
* Make sure assertions are absent on native and JS
* Only print full toString for Job in debug mode
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
index a1a9097..40344c9 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
@@ -297,7 +297,7 @@
}
is CompletedIdempotentResult -> {
return if (state.idempotentResume === idempotent) {
- check(state.result === value) { "Non-idempotent resume" }
+ assert { state.result === value } // "Non-idempotent resume"
state.token
} else {
null
diff --git a/kotlinx-coroutines-core/common/src/Debug.common.kt b/kotlinx-coroutines-core/common/src/Debug.common.kt
index 92dd552..dd09a6a 100644
--- a/kotlinx-coroutines-core/common/src/Debug.common.kt
+++ b/kotlinx-coroutines-core/common/src/Debug.common.kt
@@ -1,8 +1,10 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
+internal expect val DEBUG: Boolean
internal expect val Any.hexAddress: String
internal expect val Any.classSimpleName: String
+internal expect fun assert(value: () -> Boolean)
diff --git a/kotlinx-coroutines-core/common/src/Dispatched.kt b/kotlinx-coroutines-core/common/src/Dispatched.kt
index 450163c..735a699 100644
--- a/kotlinx-coroutines-core/common/src/Dispatched.kt
+++ b/kotlinx-coroutines-core/common/src/Dispatched.kt
@@ -87,7 +87,7 @@
override fun takeState(): Any? {
val state = _state
- check(state !== UNDEFINED) // fail-fast if repeatedly invoked
+ assert { state !== UNDEFINED } // fail-fast if repeatedly invoked
_state = UNDEFINED
return state
}
diff --git a/kotlinx-coroutines-core/common/src/EventLoop.common.kt b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
index 3565292..7184654 100644
--- a/kotlinx-coroutines-core/common/src/EventLoop.common.kt
+++ b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
@@ -104,7 +104,7 @@
fun decrementUseCount(unconfined: Boolean = false) {
useCount -= delta(unconfined)
if (useCount > 0) return
- check(useCount == 0L) { "Extra decrementUseCount" }
+ assert { useCount == 0L } // "Extra decrementUseCount"
if (shared) {
// shut it down and remove from ThreadLocalEventLoop
shutdown()
diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt
index d8b6b92..63e34fd 100644
--- a/kotlinx-coroutines-core/common/src/JobSupport.kt
+++ b/kotlinx-coroutines-core/common/src/JobSupport.kt
@@ -136,10 +136,9 @@
/**
* Initializes parent job.
* It shall be invoked at most once after construction after all other initialization.
- * @suppress **This is unstable API and it is subject to change.**
*/
internal fun initParentJobInternal(parent: Job?) {
- check(parentHandle == null)
+ assert { parentHandle == null }
if (parent == null) {
parentHandle = NonDisposableHandle
return
@@ -269,8 +268,8 @@
// fast-path method to finalize normally completed coroutines without children
private fun tryFinalizeSimpleState(state: Incomplete, update: Any?, mode: Int): Boolean {
- check(state is Empty || state is JobNode<*>) // only simple state without lists where children can concurrently add
- check(update !is CompletedExceptionally) // only for normal completion
+ assert { state is Empty || state is JobNode<*> } // only simple state without lists where children can concurrently add
+ assert { update !is CompletedExceptionally } // only for normal completion
if (!_state.compareAndSet(state, update.boxIncomplete())) return false
onCancelling(null) // simple state is not a failure
onCompletionInternal(update)
@@ -397,16 +396,14 @@
*/
internal open fun onStartInternal() {}
- public final override fun getCancellationException(): CancellationException {
- val state = this.state
- return when (state) {
+ public final override fun getCancellationException(): CancellationException =
+ when (val state = this.state) {
is Finishing -> state.rootCause?.toCancellationException("$classSimpleName is cancelling")
?: error("Job is still new or active: $this")
is Incomplete -> error("Job is still new or active: $this")
is CompletedExceptionally -> state.cause.toCancellationException()
else -> JobCancellationException("$classSimpleName has completed normally", null, this)
}
- }
protected fun Throwable.toCancellationException(message: String? = null): CancellationException =
this as? CancellationException ?:
@@ -747,8 +744,8 @@
// try make new Cancelling state on the condition that we're still in the expected state
private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
- check(state !is Finishing) // only for non-finishing states
- check(state.isActive) // only for active states
+ assert { state !is Finishing } // only for non-finishing states
+ assert { state.isActive } // only for active states
// get state's list or else promote to list to correctly operate on child lists
val list = getOrPromoteCancellingList(state) ?: return false
// Create cancelling state (with rootCause!)
@@ -1037,8 +1034,7 @@
// Seals current state and returns list of exceptions
// guarded by `synchronized(this)`
fun sealLocked(proposedException: Throwable?): List<Throwable> {
- val eh = _exceptionsHolder // volatile read
- val list = when(eh) {
+ val list = when(val eh = _exceptionsHolder) { // volatile read
null -> allocateList()
is Throwable -> allocateList().also { it.add(eh) }
is ArrayList<*> -> eh as ArrayList<Throwable>
@@ -1305,14 +1301,15 @@
append("]")
}
- override fun toString(): String = getString("Active")
+ override fun toString(): String =
+ if (DEBUG) getString("Active") else super.toString()
}
internal class InactiveNodeList(
override val list: NodeList
) : Incomplete {
override val isActive: Boolean get() = false
- override fun toString(): String = list.getString("New")
+ override fun toString(): String = if (DEBUG) list.getString("New") else super.toString()
}
private class InvokeOnCompletion(
@@ -1337,7 +1334,7 @@
) : JobNode<JobSupport>(job) {
override fun invoke(cause: Throwable?) {
val state = job.state
- check(state !is Incomplete)
+ assert { state !is Incomplete }
if (state is CompletedExceptionally) {
// Resume with exception in atomic way to preserve exception
continuation.resumeWithExceptionMode(state.cause, MODE_ATOMIC_DEFAULT)
diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
index 61bc090..6419e20 100644
--- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.channels
@@ -449,7 +449,7 @@
if (select.trySelect(idempotent)) SELECT_STARTED else null
override fun completeResumeSend(token: Any) {
- check(token === SELECT_STARTED)
+ assert { token === SELECT_STARTED }
block.startCoroutine(receiver = channel, completion = select.completion)
}
@@ -474,7 +474,7 @@
) : LockFreeLinkedListNode(), Send {
override val pollResult: Any? get() = element
override fun tryResumeSend(idempotent: Any?): Any? = SEND_RESUMED
- override fun completeResumeSend(token: Any) { check(token === SEND_RESUMED) }
+ override fun completeResumeSend(token: Any) { assert { token === SEND_RESUMED } }
override fun resumeSendClosed(closed: Closed<*>) {}
}
}
@@ -654,7 +654,7 @@
while (true) {
val send = takeFirstSendOrPeekClosed() ?: error("Cannot happen")
if (send is Closed<*>) {
- check(send === closed)
+ assert { send === closed }
return // cleaned
}
send.resumeSendClosed(closed)
@@ -1065,10 +1065,10 @@
override val offerResult get() = this
override val pollResult get() = this
override fun tryResumeSend(idempotent: Any?): Any? = CLOSE_RESUMED
- override fun completeResumeSend(token: Any) { check(token === CLOSE_RESUMED) }
+ override fun completeResumeSend(token: Any) { assert { token === CLOSE_RESUMED } }
override fun tryResumeReceive(value: E, idempotent: Any?): Any? = CLOSE_RESUMED
- override fun completeResumeReceive(token: Any) { check(token === CLOSE_RESUMED) }
- override fun resumeSendClosed(closed: Closed<*>) = error("Should be never invoked")
+ override fun completeResumeReceive(token: Any) { assert { token === CLOSE_RESUMED } }
+ override fun resumeSendClosed(closed: Closed<*>) = assert { false } // "Should be never invoked"
override fun toString(): String = "Closed[$closeCause]"
}
diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
index 01afc21..688125d 100644
--- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
@@ -1,9 +1,10 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.channels
+import kotlinx.coroutines.*
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
import kotlin.jvm.*
@@ -94,7 +95,7 @@
this.size = size // restore size
receive = offerOp.result
token = offerOp.resumeToken
- check(token != null)
+ assert { token != null }
return@withLock
}
failure === OFFER_FAILED -> break@loop // cannot offer -> Ok to queue to buffer
@@ -180,7 +181,7 @@
failure == null -> { // polled successfully
send = pollOp.result
token = pollOp.resumeToken
- check(token != null)
+ assert { token != null }
replacement = send!!.pollResult
break@loop
}
diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
index 637a45b..b242639 100644
--- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.channels
@@ -142,7 +142,7 @@
private fun removeSubscriber(list: Array<Subscriber<E>>, subscriber: Subscriber<E>): Array<Subscriber<E>>? {
val n = list.size
val i = list.indexOf(subscriber)
- check(i >= 0)
+ assert { i >= 0 }
if (n == 1) return null
val update = arrayOfNulls<Subscriber<E>>(n - 1)
list.copyInto(
diff --git a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
index e676869..eda3abd 100644
--- a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
@@ -42,8 +42,8 @@
capacity == Channel.CONFLATED -> Channel.CONFLATED
else -> {
// sanity checks
- check(this.capacity >= 0) { "Unexpected capacity ${this.capacity}" }
- check(capacity >= 0) { "Unexpected capacity $capacity" }
+ assert { this.capacity >= 0 }
+ assert { capacity >= 0 }
// combine capacities clamping to UNLIMITED on overflow
val sum = this.capacity + capacity
if (sum >= 0) sum else Channel.UNLIMITED // unlimited on int overflow
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
index 3ed2c0b..b05d5a8 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
@@ -167,7 +167,7 @@
// Fast path in ChannelFlowOperator calls this function (channel was not created yet)
override suspend fun flowCollect(collector: FlowCollector<T>) {
// this function should not have been invoked when channel was explicitly requested
- check(capacity == OPTIONAL_CHANNEL)
+ assert { capacity == OPTIONAL_CHANNEL }
flowScope {
mergeImpl(this, collector.asConcurrentFlowCollector())
}
diff --git a/kotlinx-coroutines-core/common/src/internal/Atomic.kt b/kotlinx-coroutines-core/common/src/internal/Atomic.kt
index b589db2..bc52815 100644
--- a/kotlinx-coroutines-core/common/src/internal/Atomic.kt
+++ b/kotlinx-coroutines-core/common/src/internal/Atomic.kt
@@ -5,6 +5,7 @@
package kotlinx.coroutines.internal
import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.*
/**
* The most abstract operation that can be in process. Other threads observing an instance of this
@@ -40,7 +41,7 @@
val isDecided: Boolean get() = _consensus.value !== NO_DECISION
fun tryDecide(decision: Any?): Boolean {
- check(decision !== NO_DECISION)
+ assert { decision !== NO_DECISION }
return _consensus.compareAndSet(NO_DECISION, decision)
}
diff --git a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
index 4ad554f..370fcfc 100644
--- a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
+++ b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
@@ -1,8 +1,11 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
package kotlinx.coroutines.internal
-import kotlinx.atomicfu.AtomicRef
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.loop
+import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
/**
* Essentially, this segment queue is an infinite array of segments, which is represented as
@@ -133,7 +136,7 @@
* logically removed (so [removed] returns `true`) at the point of invocation.
*/
fun remove() {
- check(removed) { " The segment should be logically removed at first "}
+ assert { removed } // The segment should be logically removed at first
// Read `next` and `prev` pointers.
var next = this._next.value ?: return // tail cannot be removed
var prev = prev.value ?: return // head cannot be removed
diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt
index 9d71b7a..b42fde3 100644
--- a/kotlinx-coroutines-core/common/src/selects/Select.kt
+++ b/kotlinx-coroutines-core/common/src/selects/Select.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.selects
@@ -234,7 +234,7 @@
override val completion: Continuation<R> get() = this
private inline fun doResume(value: () -> Any?, block: () -> Unit) {
- check(isSelected) { "Must be selected first" }
+ assert { isSelected } // "Must be selected first"
_result.loop { result ->
when {
result === UNDECIDED -> if (_result.compareAndSet(UNDECIDED, value())) return
@@ -343,7 +343,7 @@
// it is just like start(), but support idempotent start
override fun trySelect(idempotent: Any?): Boolean {
- check(idempotent !is OpDescriptor) { "cannot use OpDescriptor as idempotent marker"}
+ assert { idempotent !is OpDescriptor } // "cannot use OpDescriptor as idempotent marker"
while (true) { // lock-free loop on state
val state = this.state
when {
diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
index b58885c..fa198e1 100644
--- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt
+++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.sync
@@ -206,7 +206,7 @@
is LockedQueue -> {
val curOwner = state.owner
check(curOwner !== owner) { "Already locked by $owner" }
- if (state.addLastIf(waiter, { _state.value === state })) {
+ if (state.addLastIf(waiter) { _state.value === state }) {
// added to waiter list!
cont.removeOnCancellation(waiter)
return@sc
@@ -226,8 +226,7 @@
override fun <R> registerSelectClause2(select: SelectInstance<R>, owner: Any?, block: suspend (Mutex) -> R) {
while (true) { // lock-free loop on state
if (select.isSelected) return
- val state = _state.value
- when (state) {
+ when (val state = _state.value) {
is Empty -> {
if (state.locked !== UNLOCKED) { // try upgrade to queue & retry
_state.compareAndSet(state, LockedQueue(state.locked))
@@ -388,7 +387,7 @@
) : LockWaiter(owner) {
override fun tryResumeLockWaiter(): Any? = if (select.trySelect(null)) SELECT_SUCCESS else null
override fun completeResumeLockWaiter(token: Any) {
- check(token === SELECT_SUCCESS)
+ assert { token === SELECT_SUCCESS }
block.startCoroutine(receiver = mutex, completion = select.completion)
}
override fun toString(): String = "LockSelect[$owner, $mutex, $select]"
diff --git a/kotlinx-coroutines-core/js/src/Debug.kt b/kotlinx-coroutines-core/js/src/Debug.kt
index 143cbb6..57a94d4 100644
--- a/kotlinx-coroutines-core/js/src/Debug.kt
+++ b/kotlinx-coroutines-core/js/src/Debug.kt
@@ -1,11 +1,13 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
private var counter = 0
+internal actual val DEBUG: Boolean = false
+
internal actual val Any.hexAddress: String
get() {
var result = this.asDynamic().__debug_counter
@@ -18,3 +20,5 @@
}
internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
+
+internal actual inline fun assert(value: () -> Boolean) {}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
index bd586d6..1d0c4d6 100644
--- a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
@@ -9,13 +9,6 @@
import java.util.concurrent.atomic.*
import kotlin.coroutines.*
-private val COROUTINE_ID = AtomicLong()
-
-// for tests only
-internal fun resetCoroutineId() {
- COROUTINE_ID.set(0)
-}
-
internal const val COROUTINES_SCHEDULER_PROPERTY_NAME = "kotlinx.coroutines.scheduler"
internal val useCoroutinesScheduler = systemProp(COROUTINES_SCHEDULER_PROPERTY_NAME).let { value ->
diff --git a/kotlinx-coroutines-core/jvm/src/Debug.kt b/kotlinx-coroutines-core/jvm/src/Debug.kt
index 3c750da..40de02a 100644
--- a/kotlinx-coroutines-core/jvm/src/Debug.kt
+++ b/kotlinx-coroutines-core/jvm/src/Debug.kt
@@ -1,11 +1,15 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
+// Need InlineOnly for efficient bytecode on Android
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
package kotlinx.coroutines
import kotlinx.coroutines.internal.*
-import kotlin.coroutines.*
+import java.util.concurrent.atomic.*
+import kotlin.internal.InlineOnly
/**
* Name of the property that controls coroutine debugging. See [newCoroutineContext][CoroutineScope.newCoroutineContext].
@@ -68,10 +72,13 @@
*/
public const val DEBUG_PROPERTY_VALUE_OFF = "off"
-@JvmField
-internal val DEBUG = systemProp(DEBUG_PROPERTY_NAME).let { value ->
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal val ASSERTIONS_ENABLED = CoroutineId::class.java.desiredAssertionStatus()
+
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal actual val DEBUG = systemProp(DEBUG_PROPERTY_NAME).let { value ->
when (value) {
- DEBUG_PROPERTY_VALUE_AUTO, null -> CoroutineId::class.java.desiredAssertionStatus()
+ DEBUG_PROPERTY_VALUE_AUTO, null -> ASSERTIONS_ENABLED
DEBUG_PROPERTY_VALUE_ON, "" -> true
DEBUG_PROPERTY_VALUE_OFF -> false
else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
@@ -79,18 +86,19 @@
}
// Note: stack-trace recovery is enabled only in debug mode
-@JvmField
-internal actual val RECOVER_STACK_TRACES = DEBUG && systemProp(STACKTRACE_RECOVERY_PROPERTY_NAME, true)
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal actual val RECOVER_STACK_TRACES =
+ DEBUG && systemProp(STACKTRACE_RECOVERY_PROPERTY_NAME, true)
-// internal debugging tools
+// It is used only in debug mode
+internal val COROUTINE_ID = AtomicLong(0)
-internal actual val Any.hexAddress: String
- get() = Integer.toHexString(System.identityHashCode(this))
-
-internal actual fun Continuation<*>.toDebugString(): String = when (this) {
- is DispatchedContinuation -> toString()
- // Workaround for #858
- else -> kotlin.runCatching { "$this@$hexAddress" }.getOrElse { "${this::class.java.name}@$hexAddress" }
+// for tests only
+internal fun resetCoroutineId() {
+ COROUTINE_ID.set(0)
}
-internal actual val Any.classSimpleName: String get() = this::class.java.simpleName
+@InlineOnly
+internal actual inline fun assert(value: () -> Boolean) {
+ if (ASSERTIONS_ENABLED && !value()) throw AssertionError()
+}
diff --git a/kotlinx-coroutines-core/jvm/src/DebugStrings.kt b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt
new file mode 100644
index 0000000..78ad418
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines
+
+import kotlin.coroutines.*
+
+// internal debugging tools for string representation
+
+internal actual val Any.hexAddress: String
+ get() = Integer.toHexString(System.identityHashCode(this))
+
+internal actual fun Continuation<*>.toDebugString(): String = when (this) {
+ is DispatchedContinuation -> toString()
+ // Workaround for #858
+ else -> runCatching { "$this@$hexAddress" }.getOrElse { "${this::class.java.name}@$hexAddress" }
+}
+
+internal actual val Any.classSimpleName: String get() = this::class.java.simpleName
diff --git a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
index 8358dcc..0d7055d 100644
--- a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
+++ b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
@@ -104,8 +104,8 @@
// used for tests
@Synchronized
internal fun ensureStarted() {
- assert(_thread == null) // ensure we are at a clean state
- assert(debugStatus == FRESH || debugStatus == SHUTDOWN_ACK)
+ assert { _thread == null } // ensure we are at a clean state
+ assert { debugStatus == FRESH || debugStatus == SHUTDOWN_ACK }
debugStatus = FRESH
createThreadSync() // create fresh thread
while (debugStatus == FRESH) (this as Object).wait()
diff --git a/kotlinx-coroutines-core/jvm/src/EventLoop.kt b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
index 5d214d1..7d92b67 100644
--- a/kotlinx-coroutines-core/jvm/src/EventLoop.kt
+++ b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
@@ -174,7 +174,7 @@
}
private fun closeQueue() {
- assert(isCompleted)
+ assert { isCompleted }
_queue.loop { queue ->
when (queue) {
null -> if (_queue.compareAndSet(null, CLOSED_EMPTY)) return
diff --git a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
index 7d765b9..7d28de2 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.internal
@@ -315,7 +315,7 @@
) : AbstractAtomicDesc() {
init {
// require freshly allocated node here
- check(node._next.value === node && node._prev.value === node)
+ assert { node._next.value === node && node._prev.value === node }
}
final override fun takeAffectedNode(op: OpDescriptor): Node {
@@ -390,7 +390,7 @@
@Suppress("UNCHECKED_CAST")
final override fun onPrepare(affected: Node, next: Node): Any? {
- check(affected !is LockFreeLinkedListHead)
+ assert { affected !is LockFreeLinkedListHead }
if (!validatePrepared(affected as T)) return REMOVE_PREPARED
// Note: onPrepare must use CAS to make sure the stale invocation is not
// going to overwrite the previous decision on successful preparation.
@@ -475,8 +475,8 @@
final override fun complete(op: AtomicOp<*>, failure: Any?) {
val success = failure == null
- val affectedNode = affectedNode ?: run { check(!success); return }
- val originalNext = originalNext ?: run { check(!success); return }
+ val affectedNode = affectedNode ?: run { assert { !success }; return }
+ val originalNext = originalNext ?: run { assert { !success }; return }
val update = if (success) updatedNext(affectedNode, originalNext) else originalNext
if (affectedNode._next.compareAndSet(op, update)) {
if (success) finishOnSuccess(affectedNode, originalNext)
@@ -564,7 +564,7 @@
while (true) {
if (cur is LockFreeLinkedListHead) return cur
cur = cur.nextNode
- check(cur !== this) { "Cannot loop to this while looking for list head" }
+ assert { cur !== this } // "Cannot loop to this while looking for list head"
}
}
@@ -648,8 +648,8 @@
}
internal fun validateNode(prev: Node, next: Node) {
- check(prev === this._prev.value)
- check(next === this._next.value)
+ assert { prev === this._prev.value }
+ assert { next === this._next.value }
}
override fun toString(): String = "${this::class.java.simpleName}@${Integer.toHexString(System.identityHashCode(this))}"
diff --git a/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt b/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
index 5855dbb..770aa8c 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
@@ -5,6 +5,7 @@
package kotlinx.coroutines.internal
import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
import java.util.*
import java.util.concurrent.atomic.*
@@ -207,7 +208,7 @@
private fun removeSlowPath(oldHead: Int, newHead: Int): Core<E>? {
_state.loop { state ->
state.withState { head, _ ->
- check(head == oldHead) { "This queue can have only one consumer" }
+ assert { head == oldHead } // "This queue can have only one consumer"
if (state and FROZEN_MASK != 0L) {
// state was already frozen, so removed element was copied to next
return next() // continue to correct head in next
diff --git a/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt b/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
index b164084..277e8ee 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.internal
@@ -75,7 +75,7 @@
false
} else {
val index = node.index
- check(index >= 0)
+ assert { index >= 0 }
removeAtImpl(index)
true
}
@@ -86,7 +86,7 @@
@PublishedApi
internal fun removeAtImpl(index: Int): T {
- check(size > 0)
+ assert { size > 0 }
val a = this.a!!
size--
if (index < size) {
@@ -100,7 +100,7 @@
}
}
val result = a[size]!!
- check(result.heap === this)
+ assert { result.heap === this }
result.heap = null
result.index = -1
a[size] = null
@@ -109,7 +109,7 @@
@PublishedApi
internal fun addImpl(node: T) {
- check(node.heap == null)
+ assert { node.heap == null }
node.heap = this
val a = realloc()
val i = size++
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
index e488905..365f3dd 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.scheduling
@@ -7,7 +7,7 @@
import kotlinx.atomicfu.*
import kotlinx.coroutines.*
import kotlinx.coroutines.internal.*
-import java.io.Closeable
+import java.io.*
import java.util.*
import java.util.concurrent.*
import java.util.concurrent.locks.*
@@ -146,7 +146,7 @@
val index = (top and PARKED_INDEX_MASK).toInt()
val updVersion = (top + PARKED_VERSION_INC) and PARKED_VERSION_MASK
val updIndex = worker.indexInArray
- assert(updIndex != 0) // only this worker can push itself, cannot be terminated
+ assert { updIndex != 0 } // only this worker can push itself, cannot be terminated
worker.nextParkedWorker = workers[index]
/*
* Other thread can be changing this worker's index at this point, but it
@@ -311,7 +311,7 @@
worker.join(timeout)
}
val state = worker.state
- check(state === WorkerState.TERMINATED) { "Expected TERMINATED state, but found $state"}
+ assert { state === WorkerState.TERMINATED } // Expected TERMINATED state
worker.localQueue.offloadAllWork(globalQueue)
}
}
@@ -325,7 +325,7 @@
// Shutdown current thread
currentWorker?.tryReleaseCpu(WorkerState.TERMINATED)
// check & cleanup state
- assert(cpuPermits.availablePermits() == corePoolSize)
+ assert { cpuPermits.availablePermits() == corePoolSize }
parkedWorkersStack.value = 0L
controlState.value = 0L
}
@@ -664,9 +664,8 @@
* This attempt may fail either because worker terminated itself or because someone else
* claimed this worker (though this case is rare, because require very bad timings)
*/
- fun tryForbidTermination(): Boolean {
- val state = terminationState.value
- return when (state) {
+ fun tryForbidTermination(): Boolean =
+ when (val state = terminationState.value) {
TERMINATED -> false // already terminated
FORBIDDEN -> false // already forbidden, someone else claimed this worker
ALLOWED -> terminationState.compareAndSet(
@@ -675,7 +674,6 @@
)
else -> error("Invalid terminationState = $state")
}
- }
/**
* Tries to acquire CPU token if worker doesn't have one
@@ -780,7 +778,7 @@
val currentState = state
// Shutdown sequence of blocking dispatcher
if (currentState !== WorkerState.TERMINATED) {
- assert(currentState == WorkerState.BLOCKING) { "Expected BLOCKING state, but has $currentState" }
+ assert { currentState == WorkerState.BLOCKING } // "Expected BLOCKING state, but has $currentState"
state = WorkerState.RETIRING
}
}
@@ -927,7 +925,7 @@
terminationDeadline = 0L // reset deadline for termination
lastStealIndex = 0 // reset steal index (next time try random)
if (state == WorkerState.PARKING) {
- assert(mode == TaskMode.PROBABLY_BLOCKING)
+ assert { mode == TaskMode.PROBABLY_BLOCKING }
state = WorkerState.BLOCKING
parkTimeNs = MIN_PARK_TIME_NS
}
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
index a0f209b..a9aa86d 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
@@ -5,6 +5,7 @@
package kotlinx.coroutines.scheduling
import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
import java.util.concurrent.atomic.*
internal const val BUFFER_CAPACITY_BASE = 7
diff --git a/kotlinx-coroutines-core/native/src/Debug.kt b/kotlinx-coroutines-core/native/src/Debug.kt
index e81e89a..653cb06 100644
--- a/kotlinx-coroutines-core/native/src/Debug.kt
+++ b/kotlinx-coroutines-core/native/src/Debug.kt
@@ -1,15 +1,18 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines
import kotlin.math.*
+internal actual val DEBUG: Boolean = false
+
internal actual val Any.hexAddress: String get() = abs(id().let { if (it == Int.MIN_VALUE) 0 else it }).toString(16)
internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
-
@SymbolName("Kotlin_Any_hashCode")
external fun Any.id(): Int // Note: can return negative value on K/N
+
+internal actual inline fun assert(value: () -> Boolean) {}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/native/src/EventLoop.kt b/kotlinx-coroutines-core/native/src/EventLoop.kt
index 37f5442..db212c9 100644
--- a/kotlinx-coroutines-core/native/src/EventLoop.kt
+++ b/kotlinx-coroutines-core/native/src/EventLoop.kt
@@ -153,7 +153,7 @@
}
protected fun closeQueue() {
- assert(isCompleted)
+ assert { isCompleted }
_queue.loop { queue ->
when (queue) {
null -> if (_queue.compareAndSet(null, CLOSED_EMPTY)) return