new: integrate a dispatcher provider and dispatch work

This commit is contained in:
2026-06-22 14:58:18 +02:00
parent 38d6107423
commit 8926b3237b
16 changed files with 151 additions and 9 deletions
+53
View File
@@ -0,0 +1,53 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidMultiplatformLibrary)
}
kotlin {
iosArm64()
iosSimulatorArm64()
jvm()
js {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
androidLibrary {
namespace = "fr.ajaury.gwenedeg.core.coroutines"
compileSdk =
libs.versions.androidCompileSdk
.get()
.toInt()
minSdk =
libs.versions.androidMinSdk
.get()
.toInt()
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
sourceSets {
commonMain.dependencies {
// Coroutines
implementation(libs.kotlinx.coroutinesCore)
// DI
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
}
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
@@ -0,0 +1,11 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal class DefaultDispatcherProvider : DispatcherProvider {
override val main: CoroutineDispatcher = Dispatchers.Main
override val default: CoroutineDispatcher = Dispatchers.Default
override val io: CoroutineDispatcher = ioDispatcher
}
@@ -0,0 +1,5 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
internal expect val ioDispatcher: CoroutineDispatcher
@@ -0,0 +1,10 @@
package fr.ajaury.gwenedeg.core.coroutines.di
import fr.ajaury.gwenedeg.core.coroutines.data.DefaultDispatcherProvider
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import org.koin.core.module.Module
import org.koin.dsl.module
val coroutinesModule: Module = module {
single<DispatcherProvider> { DefaultDispatcherProvider() }
}
@@ -0,0 +1,9 @@
package fr.ajaury.gwenedeg.core.coroutines.domain
import kotlinx.coroutines.CoroutineDispatcher
interface DispatcherProvider {
val main: CoroutineDispatcher
val default: CoroutineDispatcher
val io: CoroutineDispatcher
}
@@ -0,0 +1,8 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// `Dispatchers.IO` is not public API on Native; `Dispatchers.Default` is multi-threaded here, so it
// still runs work off the main thread.
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
@@ -0,0 +1,7 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// JS is single-threaded; there is no dedicated I/O dispatcher, use default.
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
@@ -0,0 +1,7 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// Wasm is single-threaded; there is no dedicated I/O dispatcher, use default
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
+5 -1
View File
@@ -39,9 +39,13 @@ kotlin {
sourceSets {
commonMain.dependencies {
// Logging
// Core modules
implementation(projects.core.coroutines)
implementation(projects.core.logging)
// Coroutines
implementation(libs.kotlinx.coroutinesCore)
// DI
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
@@ -1,20 +1,25 @@
package fr.ajaury.gwenedeg.subtitle.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import fr.ajaury.gwenedeg.core.logging.domain.Logger
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository
import fr.ajaury.gwenedeg.subtitle.model.CantGetSubtitleException
import kotlinx.coroutines.withContext
internal class LrcSubtitleRepository(
private val parser: LrcParser,
private val dispatcherProvider: DispatcherProvider,
private val logger: Logger,
) : SubtitleRepository {
override suspend fun getSubtitle(content: String): Subtitle =
try {
parser.parse(content = content)
} catch (exception: Exception) {
val message = "Failed to load subtitle: $content"
logger.error(message = message, throwable = exception)
throw CantGetSubtitleException(message)
withContext(dispatcherProvider.default) {
try {
parser.parse(content = content)
} catch (exception: Exception) {
val message = "Failed to load subtitle: $content"
logger.error(message = message, throwable = exception)
throw CantGetSubtitleException(message)
}
}
}
+1
View File
@@ -39,6 +39,7 @@ include(":shared")
// Core modules
include(":core:audioplayer")
include(":core:coroutines")
include(":core:logging")
// Data modules
+1
View File
@@ -60,6 +60,7 @@ kotlin {
commonMain.dependencies {
// Core modules
implementation(projects.core.audioplayer)
implementation(projects.core.coroutines)
implementation(projects.core.logging)
// Data modules
@@ -7,6 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.ui.NavDisplay
import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSerializer
import fr.ajaury.gwenedeg.core.coroutines.di.coroutinesModule
import fr.ajaury.gwenedeg.core.logging.di.loggingModule
import fr.ajaury.gwenedeg.di.sharedModule
import fr.ajaury.gwenedeg.navigation.Route
@@ -26,6 +27,7 @@ fun App() {
modules(
sharedModule,
audioPlayerModule,
coroutinesModule,
loggingModule,
subtitleModule
)
@@ -1,8 +1,15 @@
package fr.ajaury.gwenedeg.resourcereader.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader
import gwenedeg.shared.generated.resources.Res
import kotlinx.coroutines.withContext
internal class ComposeResourceReader : ResourceReader {
override suspend fun read(resourcePath: String): String = Res.readBytes(resourcePath).decodeToString()
internal class ComposeResourceReader(
private val dispatcherProvider: DispatcherProvider,
) : ResourceReader {
override suspend fun read(resourcePath: String): String =
withContext(dispatcherProvider.io) {
Res.readBytes(resourcePath).decodeToString()
}
}