diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 172b1f3..dc27176 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -126,7 +126,7 @@ jobs:
# Prefix the list with "+" to use these queries, and those in the config file.
#
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
- # queries: security-extended, security-and-quality
+ # queries: security-extended, security-and-quality.
- name: 'Build and check'
timeout-minutes: 18
diff --git a/.run/check.run.xml b/.run/check.run.xml
index cd9fbdb..e2b79b4 100644
--- a/.run/check.run.xml
+++ b/.run/check.run.xml
@@ -10,12 +10,14 @@
-
+
diff --git a/README.md b/README.md
index 342ae8c..6d690bf 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,10 @@ Different platform-specific implementations are provided.
| [() -> ReadableByteChannel] Factory | JVM, Android | JVM, Android |
| [AsynchronousFileChannel] | JVM, Android | JVM, Android |
+
+> [!IMPORTANT]
+> For using [AsynchronousFileChannel], you need to add Kotlin Coroutines dependency to your project.
+
[RandomAccessData]: fluxo-io-rad/src/commonMain/kotlin/fluxo/io/rad/RandomAccessData.common.kt#L29
[ByteArray]: fluxo-io-rad/src/commonMain/kotlin/fluxo/io/rad/RadByteArrayAccessor.kt#L21
diff --git a/fluxo-io-rad/api/android/fluxo-io-rad.api b/fluxo-io-rad/api/android/fluxo-io-rad.api
index d3b6b63..4248c5f 100644
--- a/fluxo-io-rad/api/android/fluxo-io-rad.api
+++ b/fluxo-io-rad/api/android/fluxo-io-rad.api
@@ -4,10 +4,11 @@ public final class fluxo/io/FluxoIoLogger {
public abstract class fluxo/io/SharedCloseable : java/io/Closeable {
public fun ()V
+ public final fun addOnSharedCloseListener (Lkotlin/jvm/functions/Function1;)V
public final fun close ()V
public final fun isOpen ()Z
protected abstract fun onSharedClose ()V
- public final fun onSharedClose (Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
+ public final fun removeOnSharedCloseListener (Lkotlin/jvm/functions/Function1;)V
public final fun retain ()V
}
diff --git a/fluxo-io-rad/api/fluxo-io-rad.klib.api b/fluxo-io-rad/api/fluxo-io-rad.klib.api
index 3252bf7..0084926 100644
--- a/fluxo-io-rad/api/fluxo-io-rad.klib.api
+++ b/fluxo-io-rad/api/fluxo-io-rad.klib.api
@@ -26,8 +26,9 @@ abstract class fluxo.io/SharedCloseable : kotlin/AutoCloseable { // fluxo.io/Sha
final fun (): kotlin/Boolean // fluxo.io/SharedCloseable.isOpen.|(){}[0]
abstract fun onSharedClose() // fluxo.io/SharedCloseable.onSharedClose|onSharedClose(){}[0]
+ final fun addOnSharedCloseListener(kotlin/Function1) // fluxo.io/SharedCloseable.addOnSharedCloseListener|addOnSharedCloseListener(kotlin.Function1){}[0]
final fun close() // fluxo.io/SharedCloseable.close|close(){}[0]
- final fun onSharedClose(kotlin/Function1): kotlinx.coroutines/DisposableHandle // fluxo.io/SharedCloseable.onSharedClose|onSharedClose(kotlin.Function1){}[0]
+ final fun removeOnSharedCloseListener(kotlin/Function1) // fluxo.io/SharedCloseable.removeOnSharedCloseListener|removeOnSharedCloseListener(kotlin.Function1){}[0]
final fun retain() // fluxo.io/SharedCloseable.retain|retain(){}[0]
}
diff --git a/fluxo-io-rad/api/jvm/fluxo-io-rad.api b/fluxo-io-rad/api/jvm/fluxo-io-rad.api
index d3b6b63..4248c5f 100644
--- a/fluxo-io-rad/api/jvm/fluxo-io-rad.api
+++ b/fluxo-io-rad/api/jvm/fluxo-io-rad.api
@@ -4,10 +4,11 @@ public final class fluxo/io/FluxoIoLogger {
public abstract class fluxo/io/SharedCloseable : java/io/Closeable {
public fun ()V
+ public final fun addOnSharedCloseListener (Lkotlin/jvm/functions/Function1;)V
public final fun close ()V
public final fun isOpen ()Z
protected abstract fun onSharedClose ()V
- public final fun onSharedClose (Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
+ public final fun removeOnSharedCloseListener (Lkotlin/jvm/functions/Function1;)V
public final fun retain ()V
}
diff --git a/fluxo-io-rad/build.gradle.kts b/fluxo-io-rad/build.gradle.kts
index ee7fa31..a2644e8 100644
--- a/fluxo-io-rad/build.gradle.kts
+++ b/fluxo-io-rad/build.gradle.kts
@@ -23,26 +23,24 @@ fkcSetupMultiplatform(
},
) {
common.main.dependencies {
- implementation(libs.coroutines)
// implementation(libs.kotlinx.io.core)
}
- val commonJvm = commonJvm
commonJvm.main.dependencies {
compileOnly(rootProject.extra["androidJar"]!!)
compileOnly(libs.androidx.annotation)
compileOnly(libs.jetbrains.annotation)
+ compileOnly(libs.coroutines)
}
val commonJs = commonJs
commonJs.main.dependencies {
- api(libs.kotlinx.atomicfu)
+ // Fix JS build KLIB issue
+ implementation(libs.kotlinx.atomicfu)
}
- arrayOf(commonJvm, commonApple, commonJs, commonLinux, commonMingw).forEach {
- it.main.dependencies {
- // implementation(libs.okio)
- }
+ commonNative.main.dependencies {
+ implementation(libs.stately.concurrent.collections)
}
}
diff --git a/fluxo-io-rad/dependencies/androidDebugRuntimeClasspath.txt b/fluxo-io-rad/dependencies/androidDebugRuntimeClasspath.txt
index 8b9c5ce..52f513f 100644
--- a/fluxo-io-rad/dependencies/androidDebugRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/androidDebugRuntimeClasspath.txt
@@ -1,5 +1 @@
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
-org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
-org.jetbrains:annotations:23.0.0
diff --git a/fluxo-io-rad/dependencies/androidReleaseRuntimeClasspath.txt b/fluxo-io-rad/dependencies/androidReleaseRuntimeClasspath.txt
index 8b9c5ce..52f513f 100644
--- a/fluxo-io-rad/dependencies/androidReleaseRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/androidReleaseRuntimeClasspath.txt
@@ -1,5 +1 @@
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
-org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
-org.jetbrains:annotations:23.0.0
diff --git a/fluxo-io-rad/dependencies/debugRuntimeClasspath.txt b/fluxo-io-rad/dependencies/debugRuntimeClasspath.txt
index 81aefab..52f513f 100644
--- a/fluxo-io-rad/dependencies/debugRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/debugRuntimeClasspath.txt
@@ -1,5 +1 @@
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
-org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
-org.jetbrains:annotations:24.1.0
diff --git a/fluxo-io-rad/dependencies/jsCompileClasspath.txt b/fluxo-io-rad/dependencies/jsCompileClasspath.txt
index 342e0ae..da28072 100644
--- a/fluxo-io-rad/dependencies/jsCompileClasspath.txt
+++ b/fluxo-io-rad/dependencies/jsCompileClasspath.txt
@@ -4,5 +4,3 @@ org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
org.jetbrains.kotlin:kotlinx-atomicfu-runtime:2.0.20-Beta1
org.jetbrains.kotlinx:atomicfu-js:0.25.0
org.jetbrains.kotlinx:atomicfu:0.25.0
-org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
diff --git a/fluxo-io-rad/dependencies/jsRuntimeClasspath.txt b/fluxo-io-rad/dependencies/jsRuntimeClasspath.txt
index 342e0ae..da28072 100644
--- a/fluxo-io-rad/dependencies/jsRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/jsRuntimeClasspath.txt
@@ -4,5 +4,3 @@ org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
org.jetbrains.kotlin:kotlinx-atomicfu-runtime:2.0.20-Beta1
org.jetbrains.kotlinx:atomicfu-js:0.25.0
org.jetbrains.kotlinx:atomicfu:0.25.0
-org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
diff --git a/fluxo-io-rad/dependencies/jvmRuntimeClasspath.txt b/fluxo-io-rad/dependencies/jvmRuntimeClasspath.txt
index 8b9c5ce..52f513f 100644
--- a/fluxo-io-rad/dependencies/jvmRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/jvmRuntimeClasspath.txt
@@ -1,5 +1 @@
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
-org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
-org.jetbrains:annotations:23.0.0
diff --git a/fluxo-io-rad/dependencies/releaseRuntimeClasspath.txt b/fluxo-io-rad/dependencies/releaseRuntimeClasspath.txt
index 81aefab..52f513f 100644
--- a/fluxo-io-rad/dependencies/releaseRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/releaseRuntimeClasspath.txt
@@ -1,5 +1 @@
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
-org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
-org.jetbrains:annotations:24.1.0
diff --git a/fluxo-io-rad/dependencies/wasmJsCompileClasspath.txt b/fluxo-io-rad/dependencies/wasmJsCompileClasspath.txt
index b7c3e12..464ac5c 100644
--- a/fluxo-io-rad/dependencies/wasmJsCompileClasspath.txt
+++ b/fluxo-io-rad/dependencies/wasmJsCompileClasspath.txt
@@ -2,5 +2,3 @@ org.jetbrains.kotlin:kotlin-stdlib-wasm-js:2.0.20-Beta1
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
org.jetbrains.kotlinx:atomicfu-wasm-js:0.25.0
org.jetbrains.kotlinx:atomicfu:0.25.0
-org.jetbrains.kotlinx:kotlinx-coroutines-core-wasm-js:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
diff --git a/fluxo-io-rad/dependencies/wasmJsRuntimeClasspath.txt b/fluxo-io-rad/dependencies/wasmJsRuntimeClasspath.txt
index b7c3e12..464ac5c 100644
--- a/fluxo-io-rad/dependencies/wasmJsRuntimeClasspath.txt
+++ b/fluxo-io-rad/dependencies/wasmJsRuntimeClasspath.txt
@@ -2,5 +2,3 @@ org.jetbrains.kotlin:kotlin-stdlib-wasm-js:2.0.20-Beta1
org.jetbrains.kotlin:kotlin-stdlib:2.0.20-Beta1
org.jetbrains.kotlinx:atomicfu-wasm-js:0.25.0
org.jetbrains.kotlinx:atomicfu:0.25.0
-org.jetbrains.kotlinx:kotlinx-coroutines-core-wasm-js:1.9.0-RC
-org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC
diff --git a/fluxo-io-rad/src/commonJsMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJs.kt b/fluxo-io-rad/src/commonJsMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJs.kt
new file mode 100644
index 0000000..d6e0b7c
--- /dev/null
+++ b/fluxo-io-rad/src/commonJsMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJs.kt
@@ -0,0 +1,6 @@
+@file:Suppress("FunctionName")
+
+package fluxo.io.util
+
+// WASM and JS are single-threaded, we can use a regular HashMap.
+internal actual fun ConcurrentHashMap(): MutableMap = HashMap()
diff --git a/fluxo-io-rad/src/commonJvmMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJvm.kt b/fluxo-io-rad/src/commonJvmMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJvm.kt
new file mode 100644
index 0000000..9b7b004
--- /dev/null
+++ b/fluxo-io-rad/src/commonJvmMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonJvm.kt
@@ -0,0 +1,6 @@
+@file:Suppress("FunctionName")
+
+package fluxo.io.util
+
+internal actual fun ConcurrentHashMap(): MutableMap =
+ java.util.concurrent.ConcurrentHashMap()
diff --git a/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/SharedCloseable.kt b/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/SharedCloseable.kt
index d4ca9df..d65f865 100644
--- a/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/SharedCloseable.kt
+++ b/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/SharedCloseable.kt
@@ -3,11 +3,8 @@
package fluxo.io
import fluxo.io.internal.ThreadSafe
+import fluxo.io.util.ConcurrentHashMap
import kotlinx.atomicfu.atomic
-import kotlinx.coroutines.CompletableJob
-import kotlinx.coroutines.CompletionHandler
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.Job
/**
* A [SharedCloseable] is a resource that can be shared between multiple consumers.
@@ -23,19 +20,45 @@ public abstract class SharedCloseable : Closeable {
public val isOpen: Boolean
get() = retainsCount.value > 0
- // Job is used to handle shared close listeners.
- private val job: CompletableJob = Job()
+
+ private val sharedCloseListeners = ConcurrentHashMap<(cause: Throwable?) -> Unit, Boolean>()
+
+ public fun addOnSharedCloseListener(cb: (cause: Throwable?) -> Unit) {
+ sharedCloseListeners[cb] = true
+ }
+
+ public fun removeOnSharedCloseListener(cb: (cause: Throwable?) -> Unit) {
+ sharedCloseListeners.remove(cb)
+ }
+
public final override fun close() {
- if (retainsCount.decrementAndGet() == 0) {
- @Suppress("TooGenericExceptionCaught")
- try {
- onSharedClose()
- job.complete()
- } catch (e: Throwable) {
- job.completeExceptionally(e)
- throw e
+ if (retainsCount.decrementAndGet() != 0) {
+ return
+ }
+
+ @Suppress("TooGenericExceptionCaught")
+ try {
+ onSharedClose()
+ notifyListenersOnce(e = null)
+ } catch (e: Throwable) {
+ while (true) {
+ try {
+ notifyListenersOnce(e)
+ break
+ } catch (e2: Throwable) {
+ e.addSuppressed(e2)
+ }
}
+ throw e
+ }
+ }
+
+ private fun notifyListenersOnce(e: Throwable?) {
+ val iterator = sharedCloseListeners.keys.iterator()
+ for (listener in iterator) {
+ iterator.remove()
+ listener(e)
}
}
@@ -49,8 +72,6 @@ public abstract class SharedCloseable : Closeable {
@Throws(IOException::class)
protected abstract fun onSharedClose()
- public fun onSharedClose(cb: CompletionHandler): DisposableHandle =
- job.invokeOnCompletion(cb)
public fun retain() {
while (true) {
diff --git a/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/util/ConcurrentHashMap.common.kt b/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/util/ConcurrentHashMap.common.kt
new file mode 100644
index 0000000..de36b68
--- /dev/null
+++ b/fluxo-io-rad/src/commonMain/kotlin/fluxo/io/util/ConcurrentHashMap.common.kt
@@ -0,0 +1,5 @@
+@file:Suppress("FunctionName")
+
+package fluxo.io.util
+
+internal expect fun ConcurrentHashMap(): MutableMap
diff --git a/fluxo-io-rad/src/nativeMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonNative.kt b/fluxo-io-rad/src/nativeMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonNative.kt
new file mode 100644
index 0000000..cdb2ce1
--- /dev/null
+++ b/fluxo-io-rad/src/nativeMain/kotlin/fluxo/io/util/ConcurrentHashMap.commonNative.kt
@@ -0,0 +1,6 @@
+@file:Suppress("FunctionName")
+
+package fluxo.io.util
+
+internal actual fun ConcurrentHashMap(): MutableMap =
+ co.touchlab.stately.collections.ConcurrentMutableMap()
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ba25c0e..9d906f9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -66,6 +66,8 @@ okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" }
kotlinx-io-bytestring = { module = "org.jetbrains.kotlinx:kotlinx-io-bytestring", version.ref = "kotlinx-io" }
+stately-concurrent-collections = { module = "co.touchlab:stately-concurrent-collections", version = "2.0.7" }
+
# https://developer.android.com/jetpack/androidx/releases/annotation
# https://mvnrepository.com/artifact/androidx.annotation/annotation
androidx-annotation = { module = "androidx.annotation:annotation", version = "1.8.0" }