I am working on a Compose Multiplatform application running on Linux (x86_64). I have a class DesktopNativeClient
that loads a native library as follows:
class DesktopNativeClient {
private companion object {
private val logger = Logger.withTag("NativeClient")
private const val NATIVE_LIBRARY_PATH = "/path/to/lib.so"
init {
logger.d { "Loading native library" }
System.load(NATIVE_LIBRARY_PATH)
logger.d { "Loaded native library" }
}
}
}
When I load the native library in the main
function, everything works fine:
fun main() = application {
DesktopNativeClient()
Window(
onCloseRequest = ::exitApplication,
title = "Title",
state = rememberWindowState(
width = ...,
height = ...
),
) {
MainApp()
}
}
However, when I try to load the native library inside a ViewModel
that is provided via Koin, I consistently get a JVM crash at the moment the native library is loaded:
class AppViewModel {
private val nativeClient = DesktopNativeClient()
...
}
ViewModel
is created like this:
actual val platformAppModule = module {
factory<BaseAppViewModel> {
AppViewModel(
defaultDispatcher = get(named(DispatchersNames.DEFAULT)),
ioDispatcher = get(named(DispatchersNames.IO)),
mainDispatcher = get(named(DispatchersNames.MAIN)),
)
}
}
And injected:
// in commonMain:
@Composable
expect inline fun <reified T : BaseAppViewModel> AppScreen(viewModel: T = koinNavViewModel())
// in desktopMain:
@Composable
actual inline fun <T : BaseAppViewModel> AppScreen(viewModel: T) {
...
Crash log:
Current thread (0x000074a7c88a1530): JavaThread "AWT-EventQueue-0" [_thread_in_native, id=49176, stack(0x000074a760d4e000,0x000074a760e4e000)]
Stack: [0x000074a760d4e000,0x000074a760e4e000], sp=0x000074a760e48420, free space=1001k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [libstdc++.so.6+0xeeb05] std::ctype<wchar_t>::do_scan_is(unsigned short, wchar_t const*, wchar_t const*) const+0x35
C [libstdc++.so.6+0xe631e] void std::__facet_shims::__numpunct_fill_cache<char>(std::integral_constant<bool, false>, std::locale::facet const*, std::__numpunct_cache<char>*)+0x6e
C [libstdc++.so.6+0xeb947] std::locale::facet::_M_sso_shim(std::locale::id const*) const+0x237
C [libstdc++.so.6+0xc46e9] std::locale::_Impl::_M_install_facet(std::locale::id const*, std::locale::facet const*)+0x229
C [libstdc++.so.6+0xdd8c3] std::locale::_Impl::_Impl(unsigned long)+0x653
C [libstdc++.so.6+0xde125] std::locale::_S_initialize_once()+0x35
C [libc.so.6+0x998fb]
C [libc.so.6+0x99979] pthread_once+0x19
C [libstdc++.so.6+0xde188] std::locale::_S_initialize()+0x38
C [libstdc++.so.6+0xde1e1] std::locale::locale()+0x21
C [libstdc++.so.6+0xc259b] std::ios_base::Init::Init()+0xeb
C [libstdc++.so.6+0xaa1cd] _GLOBAL__sub_I.00090_globals_io.cc+0x1d
C [libc.so.6+0x8ff14]
C [ld-linux-x86-64.so.2+0x1523] _dl_catch_exception+0xa3
C [ld-linux-x86-64.so.2+0x1679]
C [libc.so.6+0x8f9f3]
C [libc.so.6+0x8ffcf] dlopen+0x6f
V [libjvm.so+0x8e2030] JVM_LoadLibrary+0xd0
C [libjava.so+0xf1cf] Java_jdk_internal_loader_NativeLibraries_load+0x1df
j jdk.internal.loader.NativeLibraries.load(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZZ)Z+0 java.base@17.0.12
j jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open()Z+61 java.base@17.0.12
j jdk.internal.loader.NativeLibraries.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)Ljdk/internal/loader/NativeLibrary;+256 java.base@17.0.12
j jdk.internal.loader.NativeLibraries.loadLibrary(Ljava/lang/Class;Ljava/io/File;)Ljdk/internal/loader/NativeLibrary;+51 java.base@17.0.12
j java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/io/File;)Ljdk/internal/loader/NativeLibrary;+31 java.base@17.0.12
j java.lang.Runtime.load0(Ljava/lang/Class;Ljava/lang/String;)V+61 java.base@17.0.12
j java.lang.System.load(Ljava/lang/String;)V+7 java.base@17.0.12
j my.packageclient.client.DesktopNativeClient.<clinit>()V+109
v ~StubRoutines::call_stub
j my.packageclient.presentation.apps.detail.AppViewModel.<init>(Lkotlinx/coroutines/CoroutineDispatcher;Lkotlinx/coroutines/CoroutineDispatcher;Lkotlinx/coroutines/CoroutineDispatcher;)V+52
j my.packageclient.di.module.PlatformAppModule_desktopKt.platformAppModule$lambda$1$lambda$0(Lorg/koin/core/scope/Scope;Lorg/koin/core/parameter/ParametersHolder;)Lmy/package/client/presentation/apps/detail/BaseAppViewModel;+100
j my.packageclient.di.module.PlatformAppModule_desktopKt$$Lambda$215+0x000074a70c1e0488.invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+8
j org.koin.core.instance.InstanceFactory.create(Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+66
j org.koin.core.instance.FactoryInstanceFactory.get(Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+8
j org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(Lorg/koin/core/qualifier/Qualifier;Lkotlin/reflect/KClass;Lorg/koin/core/qualifier/Qualifier;Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+35
j org.koin.core.scope.Scope.resolveFromRegistry(Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+20
j org.koin.core.scope.Scope.resolveFromContext(Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+73
j org.koin.core.scope.Scope.stackParametersCall(Lorg/koin/core/parameter/ParametersHolder;Lorg/koin/core/instance/ResolutionContext;)Ljava/lang/Object;+107
j org.koin.core.scope.Scope.resolveInstance(Lorg/koin/core/qualifier/Qualifier;Lkotlin/reflect/KClass;Lorg/koin/core/parameter/ParametersHolder;)Ljava/lang/Object;+77
j org.koin.core.scope.Scope.resolveWithOptionalLogging(Lkotlin/reflect/KClass;Lorg/koin/core/qualifier/Qualifier;Lorg/koin/core/parameter/ParametersHolder;)Ljava/lang/Object;+45
j org.koin.core.scope.Scope.get(Lkotlin/reflect/KClass;Lorg/koin/core/qualifier/Qualifier;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;+27
j org.koin.viewmodel.factory.KoinViewModelFactory.create(Lkotlin/reflect/KClass;Landroidx/lifecycle/viewmodel/CreationExtras;)Landroidx/lifecycle/ViewModel;+43
j androidx.lifecycle.viewmodel.ViewModelProviderImpl_desktopKt.createViewModel(Landroidx/lifecycle/ViewModelProvider$Factory;Lkotlin/reflect/KClass;Landroidx/lifecycle/viewmodel/CreationExtras;)Landroidx/lifecycle/ViewModel;+21
j androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel(Lkotlin/reflect/KClass;Ljava/lang/String;)Landroidx/lifecycle/ViewModel;+103
j androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel$default(Landroidx/lifecycle/viewmodel/ViewModelProviderImpl;Lkotlin/reflect/KClass;Ljava/lang/String;ILjava/lang/Object;)Landroidx/lifecycle/ViewModel;+17
j androidx.lifecycle.ViewModelProvider.get(Lkotlin/reflect/KClass;)Landroidx/lifecycle/ViewModel;+14
j org.koin.viewmodel.GetViewModelKt.resolveViewModel(Lkotlin/reflect/KClass;Landroidx/lifecycle/ViewModelStore;Ljava/lang/String;Landroidx/lifecycle/viewmodel/CreationExtras;Lorg/koin/core/qualifier/Qualifier;Lorg/koin/core/scope/Scope;Lkotlin/jvm/functions/Function0;)Landroidx/lifecycle/ViewModel;+108
j my.packageclient.presentation.apps.list.RemoteAppsScreenKt.RemoteAppView(Lmy/package/client/presentation/apps/list/RemoteAppsViewModel;Lmy/package/client/presentation/apps/data/AppTab;Landroidx/compose/runtime/Composer;I)V+667
j my.packageclient.presentation.apps.list.RemoteAppsScreenKt$RemoteAppsScreen$4.invoke(Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/runtime/Composer;I)V+569
j my.packageclient.presentation.apps.list.RemoteAppsScreenKt$RemoteAppsScreen$4.invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+16
j androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/Object;+75
j androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$2.invoke(Landroidx/compose/runtime/Composer;I)V+24
j androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$2.invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+12
j androidx.compose.runtime.RecomposeScopeImpl.compose(Landroidx/compose/runtime/Composer;)V+86
j androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd()V+209
j androidx.compose.runtime.ComposerImpl.skipCurrentGroup()V+360
j androidx.compose.runtime.ComposerImpl.doCompose(Landroidx/compose/runtime/collection/ScopeMap;Lkotlin/jvm/functions/Function2;)V+585
j androidx.compose.runtime.ComposerImpl.recompose$runtime(Landroidx/compose/runtime/collection/ScopeMap;)Z+72
j androidx.compose.runtime.CompositionImpl.recompose()Z+104
j androidx.compose.runtime.Recomposer.performRecompose(Landroidx/compose/runtime/ControlledComposition;Landroidx/collection/MutableScatterSet;)Landroidx/compose/runtime/ControlledComposition;+141
j androidx.compose.runtime.Recomposer.access$performRecompose(Landroidx/compose/runtime/Recomposer;Landroidx/compose/runtime/ControlledComposition;Landroidx/collection/MutableScatterSet;)Landroidx/compose/runtime/ControlledComposition;+3
j androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(J)V+397
j androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Ljava/lang/Object;)Ljava/lang/Object;+8
j androidx.compose.runtime.BroadcastFrameClock$FrameAwaiter.resume(J)V+31
j androidx.compose.runtime.BroadcastFrameClock.sendFrame(J)V+78
j androidx.compose.ui.scene.BaseComposeScene.render(Landroidx/compose/ui/graphics/Canvas;J)V+127
j androidx.compose.ui.scene.ComposeSceneMediator$onRender$1$1.invoke()V+141
j androidx.compose.ui.scene.ComposeSceneMediator$onRender$1$1.invoke()Ljava/lang/Object;+1
j androidx.compose.ui.viewinterop.SwingInteropContainer.postponingExecutingScheduledUpdates(Lkotlin/jvm/functions/Function0;)V+83
j androidx.compose.ui.scene.ComposeSceneMediator.onRender(Lorg/jetbrains/skia/Canvas;IIJ)V+35
j org.jetbrains.skiko.SkiaLayer.update$skiko(J)V+223
j org.jetbrains.skiko.redrawer.AWTRedrawer.update(J)V+57
j org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer$Companion$frameDispatcher$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;+263
J 2246 c1 kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Ljava/lang/Object;)V (128 bytes) @ 0x000074a7b161637c [0x000074a7b1615f40+0x000000000000043c]
J 2425 c1 kotlinx.coroutines.DispatchedTask.run()V (400 bytes) @ 0x000074a7b167e8ec [0x000074a7b167d2c0+0x000000000000162c]
J 3697 c1 java.awt.event.InvocationEvent.dispatch()V java.desktop@17.0.12 (69 bytes) @ 0x000074a7b1936e9c [0x000074a7b1936c80+0x000000000000021c]
J 3261 c1 java.awt.EventQueue.dispatchEventImpl(Ljava/awt/AWTEvent;Ljava/lang/Object;)V java.desktop@17.0.12 (136 bytes) @ 0x000074a7b184afec [0x000074a7b1849500+0x0000000000001aec]
J 3260 c1 java.awt.EventQueue$4.run()Ljava/lang/Void; java.desktop@17.0.12 (60 bytes) @ 0x000074a7b184013c [0x000074a7b1840000+0x000000000000013c]
J 3255 c1 java.awt.EventQueue.dispatchEvent(Ljava/awt/AWTEvent;)V java.desktop@17.0.12 (80 bytes) @ 0x000074a7b184350c [0x000074a7b1842f60+0x00000000000005ac]
J 3264 c1 java.awt.EventDispatchThread.pumpOneEventForFilters(I)V java.desktop@17.0.12 (113 bytes) @ 0x000074a7b1846624 [0x000074a7b1845fa0+0x0000000000000684]
j java.awt.EventDispatchThread.pumpEventsForFilter(ILjava/awt/Conditional;Ljava/awt/EventFilter;)V+35 java.desktop@17.0.12
j java.awt.EventDispatchThread.pumpEventsForHierarchy(ILjava/awt/Conditional;Ljava/awt/Component;)V+11 java.desktop@17.0.12
j java.awt.EventDispatchThread.pumpEvents(ILjava/awt/Conditional;)V+4 java.desktop@17.0.12
j java.awt.EventDispatchThread.pumpEvents(Ljava/awt/Conditional;)V+3 java.desktop@17.0.12
j java.awt.EventDispatchThread.run()V+9 java.desktop@17.0.12
v ~StubRoutines::call_stub
What could be causing the JVM to crash when loading the native library in the ViewModel
?
Are there any specific considerations when loading native libraries in a Composable
context or when using Koin?
The crash only happens when the native library is loaded in the ViewModel
(which is injected via Koin).
The application runs fine when the library is loaded in the main
function.
I'm using zulu-17.52
Any guidance on how to resolve this issue would be greatly appreciated!
So, I haven't found the exact cause of it, but it happens only when loading native library while creating an object with Koin.
The simplest solution is to load libraries outside of it.