Skip to content

Commit

Permalink
Move unhandledExceptionHook to kotlin code (JetBrains#4704)
Browse files Browse the repository at this point in the history
  • Loading branch information
projedi authored Feb 16, 2021
1 parent 8519223 commit ce946a9
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 64 deletions.
10 changes: 9 additions & 1 deletion backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2531,12 +2531,20 @@ standaloneTest("kt-37572") {
}

standaloneTest("custom_hook") {
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
enabled = (project.testTarget != 'wasm32') && // Uses exceptions.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "value 42: Error\n"
expectedExitStatus = 1
source = "runtime/exceptions/custom_hook.kt"
}

standaloneTest("exception_in_global_init") {
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
source = "runtime/exceptions/exception_in_global_init.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Uncaught Kotlin exception: kotlin.IllegalStateException: FAIL") && !s.contains("in kotlin main") }
}

standaloneTest("runtime_math_exceptions") {
enabled = (project.testTarget != 'wasm32')
source = "stdlib_external/numbers/MathExceptionTest.kt"
Expand Down
24 changes: 5 additions & 19 deletions backend.native/tests/runtime/exceptions/custom_hook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,17 @@ import kotlin.test.*

import kotlin.native.concurrent.*

fun setHookLegacyMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
fun main(args : Array<String>) {
assertFailsWith<InvalidMutabilityException> {
setUnhandledExceptionHook { _ -> println("wrong") }
}

return setUnhandledExceptionHook(hook.freeze())
}

fun setHookNewMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
return setUnhandledExceptionHook(hook)
}

fun setHook(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
return when (kotlin.native.Platform.memoryModel) {
kotlin.native.MemoryModel.EXPERIMENTAL -> setHookNewMM(hook)
else -> setHookLegacyMM(hook)
}
}

fun main() {
val x = 42
val old = setHook {
val old = setUnhandledExceptionHook({
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
}
}.freeze())

assertNull(old)

throw Error("an error")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/

import kotlin.test.*

val p: Nothing = error("FAIL")

fun main() {
println("in kotlin main")
}
25 changes: 0 additions & 25 deletions runtime/src/main/cpp/Exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@

namespace {

// RuntimeUtils.kt
extern "C" void ReportUnhandledException(KRef throwable);
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);

KRef currentUnhandledExceptionHook = nullptr;
int32_t currentUnhandledExceptionHookLock = 0;
int32_t currentUnhandledExceptionHookCookie = 0;

#if USE_GCC_UNWIND
struct Backtrace {
Backtrace(int count, int skip) : index(0), skipCount(skip) {
Expand Down Expand Up @@ -220,23 +212,6 @@ void ThrowException(KRef exception) {
#endif
}

OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
RETURN_RESULT_OF(SwapHeapRefLocked,
&currentUnhandledExceptionHook, currentUnhandledExceptionHook, hook, &currentUnhandledExceptionHookLock,
&currentUnhandledExceptionHookCookie);
}

void OnUnhandledException(KRef throwable) {
ObjHolder handlerHolder;
auto* handler = SwapHeapRefLocked(&currentUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
&currentUnhandledExceptionHookLock, &currentUnhandledExceptionHookCookie, handlerHolder.slot());
if (handler == nullptr) {
ReportUnhandledException(throwable);
} else {
ExceptionReporterLaunchpad(handler, throwable);
}
}

namespace {

class {
Expand Down
3 changes: 1 addition & 2 deletions runtime/src/main/cpp/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ OBJ_GETTER0(Kotlin_getCurrentStackTrace);

OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace);

OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook);

// Throws arbitrary exception.
void ThrowException(KRef exception);

// RuntimeUtils.kt
void OnUnhandledException(KRef throwable);

RUNTIME_NORETURN void TerminateWithUnhandledException(KRef exception);
Expand Down
14 changes: 5 additions & 9 deletions runtime/src/main/kotlin/kotlin/native/Runtime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
*/
package kotlin.native

import kotlin.native.concurrent.isFrozen
import kotlin.native.concurrent.InvalidMutabilityException
import kotlin.native.internal.Escapes
import kotlin.native.internal.UnhandledExceptionHookHolder

/**
* Initializes Kotlin runtime for the current thread, if not inited already.
Expand Down Expand Up @@ -45,19 +44,16 @@ public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
* with custom exception hooks.
*/
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL && !hook.isFrozen) {
try {
return UnhandledExceptionHookHolder.hook.swap(hook)
} catch (e: InvalidMutabilityException) {
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
}
return setUnhandledExceptionHook0(hook)
}

@SymbolName("Kotlin_setUnhandledExceptionHook")
@Escapes(0b01) // <hook> escapes
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?

/**
* Compute stable wrt potential object relocations by the memory manager identity hash code.
* @return 0 for `null` object, identity hash code otherwise.
*/
@SymbolName("Kotlin_Any_hashCode")
public external fun Any?.identityHashCode(): Int
public external fun Any?.identityHashCode(): Int
13 changes: 13 additions & 0 deletions runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,19 @@ public class AtomicReference<T> {
public override fun toString(): String =
"${debugString(this)} -> ${debugString(value)}"

// TODO: Consider making this public.
internal fun swap(new: T): T {
while (true) {
val old = value
if (old === new) {
return old
}
if (compareAndSet(old, new)) {
return old
}
}
}

// Implementation details.
@SymbolName("Kotlin_AtomicReference_set")
private external fun setImpl(new: Any?): Unit
Expand Down
23 changes: 16 additions & 7 deletions runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package kotlin.native.internal

import kotlin.internal.getProgressionLastElement
import kotlin.reflect.KClass
import kotlin.native.concurrent.AtomicReference

@ExportForCppRuntime
fun ThrowNullPointerException(): Nothing {
Expand Down Expand Up @@ -118,10 +119,23 @@ internal fun ReportUnhandledException(throwable: Throwable) {
@SymbolName("TerminateWithUnhandledException")
internal external fun TerminateWithUnhandledException(throwable: Throwable)

// Using object to make sure that `hook` is initialized when it's needed instead of
// in a normal global initialization flow. This is important if some global happens
// to throw an exception during it's initialization before this hook would've been initialized.
internal object UnhandledExceptionHookHolder {
internal val hook: AtomicReference<ReportUnhandledExceptionHook?> = AtomicReference(null)
}

@PublishedApi
@ExportForCppRuntime
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
internal fun OnUnhandledException(throwable: Throwable) {
val handler = UnhandledExceptionHookHolder.hook.swap(null)
if (handler == null) {
ReportUnhandledException(throwable);
return
}
try {
reporter(throwable)
handler(throwable)
} catch (t: Throwable) {
ReportUnhandledException(t)
}
Expand Down Expand Up @@ -210,8 +224,3 @@ internal fun <T> listOfInternal(vararg elements: T): List<T> {
result.add(elements[i])
return result
}


@PublishedApi
@SymbolName("OnUnhandledException")
external internal fun OnUnhandledException(throwable: Throwable)
2 changes: 1 addition & 1 deletion runtime/src/test_support/cpp/CompilerGenerated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ RUNTIME_NORETURN OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo
throw std::runtime_error("Not implemented for tests");
}

void ExceptionReporterLaunchpad(KRef reporter, KRef throwable) {
void OnUnhandledException(KRef throwable) {
throw std::runtime_error("Not implemented for tests");
}

Expand Down

0 comments on commit ce946a9

Please sign in to comment.