GC problems with `jl_gc_unsafe_enter` with multithreaded embedding

Keluaa
Sun, 01 Oct 2023 14:49:31 +0000
General Usage

I am currently developping a Kotlin library to embed Julia in the JVM, with the goal to support multithreading with jl_adopt_thread(). The main issue is that when using some frameworks, there may not be a way to be in control of which JVM thread would want to use Julia.

If Julia is used through JVM worker threads, then you would want to make sure that after the work is done, the adopted JVM thread does not prevent the GC from running: good management of the thread’s gc_state is required.

What I came up with is the following code (simplified):

fun runUsingJulia(func: () -> Unit) {
    var wasThreadAdopted = false
    if (!isThreadAdopted()) {
        jl_adopt_thread()
        wasThreadAdopted = true
    }

    val oldState = jl_gc_unsafe_enter()
    try {
        func()
    } finally {
        jl_gc_unsafe_leave(oldState)
        if (wasThreadAdopted) {
            jl_gc_safe_enter()
        }
    }
}

Then runUsingJulia(someFunction) would:

Adopt the current thread if needed

Mark the current thread as running Julia code (GC unsafe)

Run someFunction

Mark the current thread as GC safe (jl_gc_safe_enter is needed as jl_adopt_thread marks the thread as GC unsafe by default, and therefore must be called once)

However, this is not robust, as jl_gc_unsafe_enter will segfault if the GC is running.
The first lines of jl_adopt_thread show how to deal with a similar edge-case, but:

jl_gc_running is not exported: it is not possible to know if it is safe to call jl_gc_unsafe_enter beforehand

jl_gc_disable_counter is not exported: there is no way to prevent the GC from starting before we call jl_gc_unsafe_enter (writes should also be atomic, which is not possible within the JVM)

jl_safepoint_wait_gc would be a good way to wait for the GC to finish (plus dealing with another edge-case where the thread is marked as GC unsafe and the GC is currently waiting for it), but it is not exported

Therefore I cannot use those from Kotlin to prevent jl_gc_unsafe_enter to segfault.

Is there any way to adopt threads for temporary work with Julia without preventing GC and edge-cases?

I should mention that I managed to make this work reliably in Windows, where jl_gc_running and jl_safepoint_wait_gc are accessble for some reason. Obviously, supporting all platforms would be better.

4 posts - 3 participants