From e9082af4ded6567cece0d01db8b43127a9af840c Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 26 Feb 2024 12:44:44 +0300 Subject: [PATCH] more KMP targets --- build.gradle.kts | 48 ++++++++++---- .../kotlin/synctools/AtomicCounterTest.kt | 1 - .../KVStorage.mingwX64.kt | 25 ++++++++ .../synctools/ProtectedOp.mingwX64.kt | 17 +++++ .../sergeych/synctools/WaitHandle.mingwX64.kt | 34 ++++++++++ .../net.sergeych.bintools/KVStorage.wasmJs.kt | 63 +++++++++++++++++++ .../sergeych/synctools/ProtectedOp.wasmJs.kt | 27 ++++++++ .../sergeych/synctools/WaitHandle.wasmJs.kt | 19 ++++++ 8 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 src/mingwX64Main/kotlin/net.sergeych.bintools/KVStorage.mingwX64.kt create mode 100644 src/mingwX64Main/kotlin/net/sergeych/synctools/ProtectedOp.mingwX64.kt create mode 100644 src/mingwX64Main/kotlin/net/sergeych/synctools/WaitHandle.mingwX64.kt create mode 100644 src/wasmJsMain/kotlin/net.sergeych.bintools/KVStorage.wasmJs.kt create mode 100644 src/wasmJsMain/kotlin/net/sergeych/synctools/ProtectedOp.wasmJs.kt create mode 100644 src/wasmJsMain/kotlin/net/sergeych/synctools/WaitHandle.wasmJs.kt diff --git a/build.gradle.kts b/build.gradle.kts index b4dbcc8..ecd867e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,7 @@ repositories { } kotlin { + jvmToolchain(17) jvm { compilations.all { kotlinOptions.jvmTarget = "1.8" @@ -48,15 +49,32 @@ kotlin { } } - val hostOs = System.getProperty("os.name") - val isMingwX64 = hostOs.startsWith("Windows") - val nativeTarget = when { - hostOs == "Mac OS X" -> macosX64("native") - hostOs == "Linux" -> linuxX64("native") - isMingwX64 -> mingwX64("native") - else -> throw GradleException("Host OS is not supported in Kotlin/Native.") +// val hostOs = System.getProperty("os.name") +// val isMingwX64 = hostOs.startsWith("Windows") +// val nativeTarget = when { +// hostOs == "Mac OS X" -> macosX64("native") +// hostOs == "Linux" -> linuxX64("native") +// isMingwX64 -> mingwX64("native") +// else -> throw GradleException("Host OS is not supported in Kotlin/Native.") +// } + + linuxX64("native") { + binaries.staticLib { + baseName = "mp_bintools" + } } + wasmJs { + browser() + binaries.executable() + } + + + mingwX64() { + binaries.staticLib { + baseName = "mp_bintools" + } + } sourceSets { all { @@ -66,29 +84,35 @@ kotlin { } val commonMain by getting { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") // this is actually a bug: we need only the core, but bare core causes strange errors - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") // api("net.sergeych:mp_stools:[1.3.3,)") - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") } } val commonTest by getting { dependencies { implementation(kotlin("test")) - implementation("net.sergeych:mp_stools:1.4.1") + implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT") } } val jvmMain by getting val jvmTest by getting val jsMain by getting { dependencies { - implementation("net.sergeych:mp_stools:1.4.3") + implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT") } } val jsTest by getting val nativeMain by getting val nativeTest by getting + val wasmJsMain by getting { + dependencies { + implementation("net.sergeych:mp_stools:1.4.4-SNAPSHOT") + } + } + val wasmJsTest by getting } publishing { diff --git a/src/commonTest/kotlin/synctools/AtomicCounterTest.kt b/src/commonTest/kotlin/synctools/AtomicCounterTest.kt index a840e18..ab1fc15 100644 --- a/src/commonTest/kotlin/synctools/AtomicCounterTest.kt +++ b/src/commonTest/kotlin/synctools/AtomicCounterTest.kt @@ -1,6 +1,5 @@ package net.sergeych.synctools -import net.sergeych.tools.AtomicCounter import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/mingwX64Main/kotlin/net.sergeych.bintools/KVStorage.mingwX64.kt b/src/mingwX64Main/kotlin/net.sergeych.bintools/KVStorage.mingwX64.kt new file mode 100644 index 0000000..8f28db7 --- /dev/null +++ b/src/mingwX64Main/kotlin/net.sergeych.bintools/KVStorage.mingwX64.kt @@ -0,0 +1,25 @@ +package net.sergeych.bintools + +/** + * Create per-platform default named storage. + * + * - In the browser, it uses the `Window.localStorage` prefixing items + * by a string containing the [name] + * + * - In the JVM environment it uses folder-based storage on the file system. The name + * is considered to be a folder name (the whole path which will be automatically created) + * using the following rules: + * - when the name starts with slash (`/`) it is treated as an absolute path to a folder + * - when the name contains slash, it is considered to be a relative folder to the + * `User.home` directory, like "`~/`" on unix systems. + * - otherwise, the folder will be created in "`~/.local_storage`" parent directory + * (which also will be created if needed). + * + * - For the native platorms it is not yet implemented (but will be soon). + * + * See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like, + * and `FileDataProvider` class on JVM target. + */ +actual fun defaultNamedStorage(name: String): KVStorage { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/src/mingwX64Main/kotlin/net/sergeych/synctools/ProtectedOp.mingwX64.kt b/src/mingwX64Main/kotlin/net/sergeych/synctools/ProtectedOp.mingwX64.kt new file mode 100644 index 0000000..6ce1e55 --- /dev/null +++ b/src/mingwX64Main/kotlin/net/sergeych/synctools/ProtectedOp.mingwX64.kt @@ -0,0 +1,17 @@ +package net.sergeych.synctools + +import kotlinx.atomicfu.locks.ReentrantLock + +/** + * Native implementation uses `ReentrantLock`] + */ +actual fun ProtectedOp(): ProtectedOpImplementation = object : ProtectedOpImplementation { + private val access = ReentrantLock() + override fun lock() { + access.lock() + } + + override fun unlock() { + access.unlock() + } +} \ No newline at end of file diff --git a/src/mingwX64Main/kotlin/net/sergeych/synctools/WaitHandle.mingwX64.kt b/src/mingwX64Main/kotlin/net/sergeych/synctools/WaitHandle.mingwX64.kt new file mode 100644 index 0000000..efb35d6 --- /dev/null +++ b/src/mingwX64Main/kotlin/net/sergeych/synctools/WaitHandle.mingwX64.kt @@ -0,0 +1,34 @@ +package net.sergeych.synctools + +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout + +@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") +actual class WaitHandle { + private val channel = Channel() + actual fun await(milliseconds: Long): Boolean { + return runBlocking { + try { + if( milliseconds > 0) { + withTimeout(milliseconds) { + channel.receive() + true + } + } + else { + channel.receive() + true + } + } + catch(_: TimeoutCancellationException) { + false + } + } + } + + actual fun wakeUp() { + runBlocking { channel.send(Unit) } + } +} \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/net.sergeych.bintools/KVStorage.wasmJs.kt b/src/wasmJsMain/kotlin/net.sergeych.bintools/KVStorage.wasmJs.kt new file mode 100644 index 0000000..51b1891 --- /dev/null +++ b/src/wasmJsMain/kotlin/net.sergeych.bintools/KVStorage.wasmJs.kt @@ -0,0 +1,63 @@ +package net.sergeych.bintools + +import kotlinx.browser.localStorage +import net.sergeych.mp_tools.decodeBase64Compact +import net.sergeych.mp_tools.encodeToBase64Compact +import org.w3c.dom.Storage +import org.w3c.dom.set + + +/** + * Create per-platform default named storage. + * + * - In the browser, it uses the `Window.localStorage` prefixing items + * by a string containing the [name] + * + * - In the JVM environment it uses folder-based storage on the file system. The name + * is considered to be a folder name (the whole path which will be automatically created) + * using the following rules: + * - when the name starts with slash (`/`) it is treated as an absolute path to a folder + * - when the name contains slash, it is considered to be a relative folder to the + * `User.home` directory, like "`~/`" on unix systems. + * - otherwise, the folder will be created in "`~/.local_storage`" parent directory + * (which also will be created if needed). + * + * - For the native platorms it is not yet implemented (but will be soon). + * + * See [DataKVStorage] and [DataProvider] to implement a KVStorage on filesystems and like, + * and `FileDataProvider` class on JVM target. + */ +actual fun defaultNamedStorage(name: String): KVStorage = BrowserKVStorage(name, localStorage) + +/** + * Default KV storage in browser. Use if with `localStorage` or `sessionStorage`. It uses + * prefix for storage values not to collide with other data, beware of it. + */ +class BrowserKVStorage(keyPrefix: String, private val bst: Storage) : KVStorage { + + private val prefix = "$keyPrefix:" + fun k(key: String) = "$prefix$key" + override fun get(key: String): ByteArray? { + return bst.getItem(k(key))?.decodeBase64Compact() + } + + override fun set(key: String, value: ByteArray?) { + val corrected = k(key) + if (value == null) + bst.removeItem(corrected) + else + bst.set(corrected, value.encodeToBase64Compact()) + } + + override val keys: Set + get() { + val kk = mutableListOf() + for (i in 0 until bst.length) { + val k = bst.key(i) ?: break + if( k.startsWith(prefix)) { + kk += k.substring(prefix.length) + } + } + return kk.toSet() + } +} \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/net/sergeych/synctools/ProtectedOp.wasmJs.kt b/src/wasmJsMain/kotlin/net/sergeych/synctools/ProtectedOp.wasmJs.kt new file mode 100644 index 0000000..7f1af9c --- /dev/null +++ b/src/wasmJsMain/kotlin/net/sergeych/synctools/ProtectedOp.wasmJs.kt @@ -0,0 +1,27 @@ +package net.sergeych.synctools + +import kotlinx.atomicfu.locks.ReentrantLock + +/** + * Get the platform-depended implementation of a mutex. It does nothing in the + * browser and use appropriate mechanics on JVM and native targets. See + * [ProtectedOpImplementation.invoke], [ProtectedOpImplementation.withLock] + * ```kotlin + * val op = ProtectedOp() + * //... + * op { + * // mutually exclusive execution + * println("sequential execution here") + * } + * ~~~ + */ +actual fun ProtectedOp(): ProtectedOpImplementation = object: ProtectedOpImplementation { + val rlock = ReentrantLock() + override fun lock() { + rlock.lock() + } + + override fun unlock() { + rlock.unlock() + } +} \ No newline at end of file diff --git a/src/wasmJsMain/kotlin/net/sergeych/synctools/WaitHandle.wasmJs.kt b/src/wasmJsMain/kotlin/net/sergeych/synctools/WaitHandle.wasmJs.kt new file mode 100644 index 0000000..6f21a1d --- /dev/null +++ b/src/wasmJsMain/kotlin/net/sergeych/synctools/WaitHandle.wasmJs.kt @@ -0,0 +1,19 @@ +package net.sergeych.synctools + +/** + * Platform-independent interface to thread wait/notify. Does nothing in JS/browser, + * and uses appropriate mechanics on other platforms. + * + * Wasm is effictively songle threaded at the moment + */ +@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") +actual class WaitHandle { + actual fun await(milliseconds: Long): Boolean { + // in JS we can't wait: no threads + return true + } + + actual fun wakeUp() { + // in JS we can't wait: no threads + } +} \ No newline at end of file