Skip to content

Commit cebf6b4

Browse files
committed
Use ANDROID_DETECTED
1 parent 7bfac6d commit cebf6b4

File tree

6 files changed

+14
-90
lines changed

6 files changed

+14
-90
lines changed

kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt

-81
Original file line numberDiff line numberDiff line change
@@ -7,87 +7,6 @@ package kotlinx.coroutines
77
import kotlinx.coroutines.internal.*
88
import kotlin.coroutines.*
99

10-
internal expect fun propagateExceptionToPlatform(context: CoroutineContext, exception: Throwable)
11-
12-
/**
13-
* If [addOnExceptionCallback] is called, the provided callback will be evaluated each time
14-
* [handleCoroutineException] is executed and can't find a [CoroutineExceptionHandler] to
15-
* process the exception.
16-
*
17-
* When a callback is registered once, even if it's later removed, the system starts to assume that
18-
* other callbacks will eventually be registered, and so collects the exceptions.
19-
* Once a new callback is registered, it tries.
20-
*
21-
* The callbacks in this object are the last resort before relying on platform-dependent
22-
* ways to report uncaught exceptions from coroutines.
23-
*/
24-
@PublishedApi
25-
internal object ExceptionCollector {
26-
private val lock = SynchronizedObject()
27-
private var enabled = false
28-
private var unprocessedExceptions = mutableListOf<Throwable>()
29-
private val callbacks = mutableMapOf<Any, (Throwable) -> Unit>()
30-
31-
/**
32-
* Registers [callback] to be executed when an uncaught exception happens.
33-
* [owner] is a key by which to distinguish different callbacks.
34-
*/
35-
fun addOnExceptionCallback(owner: Any, callback: (Throwable) -> Unit) = synchronized(lock) {
36-
enabled = true // never becomes `false` again
37-
val previousValue = callbacks.put(owner, callback)
38-
assert { previousValue === null }
39-
// try to process the exceptions using the newly-registered callback
40-
unprocessedExceptions.forEach { reportException(it) }
41-
unprocessedExceptions = mutableListOf()
42-
}
43-
44-
/**
45-
* Unregisters the callback associated with [owner].
46-
*/
47-
fun removeOnExceptionCallback(owner: Any) = synchronized(lock) {
48-
val existingValue = callbacks.remove(owner)
49-
assert { existingValue !== null }
50-
}
51-
52-
/**
53-
* Tries to handle the exception by propagating it to an interested consumer.
54-
* Returns `true` if the exception does not need further processing.
55-
*
56-
* Doesn't throw.
57-
*/
58-
fun handleException(exception: Throwable): Boolean = synchronized(lock) {
59-
if (!enabled) return false
60-
if (reportException(exception)) return true
61-
/** we don't return the result of the `add` function because we don't have a guarantee
62-
* that a callback will eventually appear and collect the unprocessed exceptions, so
63-
* we can't consider [exception] to be properly handled. */
64-
unprocessedExceptions.add(exception)
65-
return false
66-
}
67-
68-
/**
69-
* Try to report [exception] to the existing callbacks.
70-
*/
71-
private fun reportException(exception: Throwable): Boolean {
72-
var executedACallback = false
73-
for (callback in callbacks.values) {
74-
callback(exception)
75-
executedACallback = true
76-
/** We don't leave the function here because we want to fan-out the exceptions to every interested consumer,
77-
* it's not enough to have the exception processed by one of them.
78-
* The reason is, it's less big of a deal to observe multiple concurrent reports of bad behavior than not
79-
* to observe the report in the exact callback that is connected to that bad behavior. */
80-
}
81-
return executedACallback
82-
}
83-
}
84-
85-
internal fun handleUncaughtCoroutineException(context: CoroutineContext, exception: Throwable) {
86-
// TODO: if ANDROID_DETECTED
87-
if (!ExceptionCollector.handleException(exception))
88-
propagateExceptionToPlatform(context, exception)
89-
}
90-
9110
/**
9211
* Helper function for coroutine builder implementations to handle uncaught and unexpected exceptions in coroutines,
9312
* that could not be otherwise handled in a normal way through structured concurrency, saving them to a future, and

kotlinx-coroutines-core/js/src/CoroutineExceptionHandlerImpl.kt renamed to kotlinx-coroutines-core/js/src/internal/CoroutineExceptionHandlerImpl.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
/*
2-
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5-
package kotlinx.coroutines
5+
package kotlinx.coroutines.internal
66

77
import kotlin.coroutines.*
88

9+
internal actual val ANDROID_DETECTED = false
10+
911
internal actual fun propagateExceptionToPlatform(context: CoroutineContext, exception: Throwable) {
1012
// log exception
1113
console.error(exception)

kotlinx-coroutines-core/jvm/src/CoroutineExceptionHandlerImpl.kt renamed to kotlinx-coroutines-core/jvm/src/internal/CoroutineExceptionHandlerImplJvm.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/*
2-
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5-
package kotlinx.coroutines
5+
package kotlinx.coroutines.internal
66

77
import java.util.*
88
import kotlin.coroutines.*
9+
import kotlinx.coroutines.*
910

1011
/**
1112
* A list of globally installed [CoroutineExceptionHandler] instances.

kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import kotlin.collections.ArrayList
1414
/**
1515
* Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
1616
*/
17-
internal val ANDROID_DETECTED = runCatching { Class.forName("android.os.Build") }.isSuccess
17+
internal actual val ANDROID_DETECTED = runCatching { Class.forName("android.os.Build") }.isSuccess
1818

1919
/**
2020
* A simplified version of [ServiceLoader].

kotlinx-coroutines-core/native/src/CoroutineExceptionHandlerImpl.kt renamed to kotlinx-coroutines-core/native/src/internal/CoroutineExceptionHandlerImpl.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
/*
2-
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5-
package kotlinx.coroutines
5+
package kotlinx.coroutines.internal
66

77
import kotlin.coroutines.*
88
import kotlin.native.*
99

10+
internal actual val ANDROID_DETECTED = false
11+
1012
@OptIn(ExperimentalStdlibApi::class)
1113
internal actual fun propagateExceptionToPlatform(context: CoroutineContext, exception: Throwable) {
1214
// log exception

kotlinx-coroutines-test/common/src/TestScope.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ internal class TestScopeImpl(context: CoroutineContext) :
227227
* after the previous one, and learning about such exceptions as soon is possible is nice. */
228228
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
229229
run {
230-
ExceptionCollector.addOnExceptionCallback(lock, this::reportException)
230+
kotlinx.coroutines.internal.ExceptionCollector.addOnExceptionCallback(lock, this::reportException)
231231
}
232232
uncaughtExceptions
233233
}
@@ -246,7 +246,7 @@ internal class TestScopeImpl(context: CoroutineContext) :
246246
/** After [finished] becomes `true`, it is no longer valid to have [reportException] as the callback. */
247247
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
248248
run {
249-
ExceptionCollector.removeOnExceptionCallback(lock)
249+
kotlinx.coroutines.internal.ExceptionCollector.removeOnExceptionCallback(lock)
250250
}
251251
finished = true
252252
uncaughtExceptions

0 commit comments

Comments
 (0)