diff --git a/build.gradle.kts b/build.gradle.kts index 7070fb4..279b194 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,4 @@ -import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.configureAllCompilationsOnAllTargets import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.configureAllCompilersOptionsOnAllTargets -import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.defineAllMultiplatformTargets import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.defineXomrkGiteaMavenRepo buildscript { @@ -15,7 +13,7 @@ buildscript { } plugins { - kotlin("multiplatform") version "2.2.10" + kotlin("multiplatform") version "2.2.20-Beta1" `maven-publish` } @@ -42,7 +40,7 @@ kotlin { configureAllCompilersOptionsOnAllTargets { - freeCompilerArgs.addAll("-Xexpect-actual-classes") + freeCompilerArgs.addAll("-Xexpect-actual-classes", "-Xrender-internal-diagnostic-names", "-XXLanguage:+ExpectRefinement") } jvm().compilations.configureEach { diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Fiber.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Fiber.kt index 2a1b0c6..83452e0 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Fiber.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Fiber.kt @@ -1,11 +1,13 @@ package ru.landgrafhomyak.multitasking_0 +import ru.landgrafhomyak.multitasking_0.threads.Thread + /** * Concurrency primitive with call stack. */ public interface Fiber { /** - * Returns [thread][Thread] to which fiber is bound if there are restrictions to threads where fiber can be [resumed][Fiber.resume]. + * Returns [thread][ru.landgrafhomyak.multitasking_0.threads.Thread] to which fiber is bound if there are restrictions to threads where fiber can be [resumed][Fiber.resume]. */ public val ownerThread: Thread? @@ -59,9 +61,13 @@ public interface Fiber { */ public fun resume() + public fun interrupt() + /** * Returns uncaught exception that terminated this fiber. * If current [state][Fiber.state] isn't [FINISHED_WITH_ERROR][ExecutionState.FINISHED_WITH_ERROR] will throw [IllegalStateException]. */ public val uncaughtException: Throwable + + public fun releaseResources() } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/SingleThreadEventLoop.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/SingleThreadEventLoop.kt index 20920ce..2cc92a9 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/SingleThreadEventLoop.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/SingleThreadEventLoop.kt @@ -1,5 +1,7 @@ package ru.landgrafhomyak.multitasking_0 +import ru.landgrafhomyak.multitasking_0.threads.Thread + public interface SingleThreadEventLoop { /** * Returns thread on which this event loop runs. diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt similarity index 53% rename from src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt rename to src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt index a83282e..8b1c5f1 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt @@ -1,4 +1,6 @@ -package ru.landgrafhomyak.multitasking_0 +package ru.landgrafhomyak.multitasking_0.threads + +import ru.landgrafhomyak.multitasking_0.Fiber public expect class Thread { public val name: String @@ -7,6 +9,6 @@ public expect class Thread { public var runningFiber: Fiber? public companion object { - public fun getCurrentThread(): Thread + public fun currentThread(): Thread } -} \ No newline at end of file +} diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt deleted file mode 100644 index 403a880..0000000 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/Thread.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.landgrafhomyak.multitasking_0 - -public actual class Thread( - public actual val name: String, -) { - actual override fun toString(): String = TODO() - - public actual var runningFiber: Fiber? = null - - public actual companion object { - public actual fun getCurrentThread(): Thread = TODO() - } -} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/DrivenPlatformThread.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/DrivenPlatformThread.kt new file mode 100644 index 0000000..42d98bb --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/DrivenPlatformThread.kt @@ -0,0 +1,122 @@ +package ru.landgrafhomyak.multitasking_0.threads + +import java.util.concurrent.CountDownLatch +import java.lang.Thread as jThread +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +@Suppress("JoinDeclarationAndAssignment") +internal class DrivenPlatformThread : Thread { + override fun toString(): String = TODO() + + override val _nativeThread: jThread + + private val _sync: ReentrantLock + private var _uncaughtException: Throwable? + private var _isExplicitlyDestroyed: Boolean + private val _threadFinishedWaiter: CountDownLatch + + internal constructor(name: String, isDaemon: Boolean, routine: ThreadRoutine) : super() { + this._sync = ReentrantLock() + this._uncaughtException = null + this._isExplicitlyDestroyed = false + this._threadFinishedWaiter = CountDownLatch(1) + + this._nativeThread = jThread.ofPlatform().name(name).daemon(isDaemon).unstarted(this.Kernel(routine)) + } + + private inner class Kernel(private val _routine: ThreadRoutine) : Runnable { + override fun run() { + try { + this._routine.runThread(this@DrivenPlatformThread) + } catch (t: Throwable) { + this@DrivenPlatformThread._sync.withLock { + this@DrivenPlatformThread._uncaughtException = t + } + } finally { + this@DrivenPlatformThread._threadFinishedWaiter.countDown() + } + } + } + + override val state: State + get() { + this._sync.withLock { + if (this._isExplicitlyDestroyed) + return State.DESTROYED + + when (this._nativeThread.state) { + jThread.State.NEW -> return State.NEW + jThread.State.RUNNABLE -> return State.NOT_FINISHED + jThread.State.BLOCKED -> return State.NOT_FINISHED + jThread.State.WAITING -> return State.NOT_FINISHED + jThread.State.TIMED_WAITING -> return State.NOT_FINISHED + jThread.State.TERMINATED -> { + if (this._uncaughtException == null) + return State.FINISHED_SUCCESSFULLY + else + return State.FINISHED_WITH_ERROR + } + } + } + } + + override val uncaughtException: Throwable + get() { + this._sync.withLock { + val e = this._uncaughtException + if (e != null) + return e + + when (this._nativeThread.state) { + jThread.State.NEW, jThread.State.RUNNABLE, jThread.State.BLOCKED, jThread.State.WAITING, jThread.State.TIMED_WAITING -> + throw IllegalStateException("Thread not finished yet") + + jThread.State.TERMINATED -> throw IllegalStateException("Thread finished without uncaught exceptions") + } + } + } + + override fun start() { + this._sync.withLock { + if (this._isExplicitlyDestroyed) + throw IllegalStateException("Thread already was started, finished and destroyed") + // stdlib's errors aren't verbose + when (this._nativeThread.state) { + jThread.State.NEW -> {} + jThread.State.TERMINATED -> throw IllegalStateException("Thread already was started and finished") + jThread.State.RUNNABLE, jThread.State.BLOCKED, jThread.State.WAITING, jThread.State.TIMED_WAITING -> + throw IllegalStateException("Thread already was started") + } + + this._nativeThread.start() + } + } + + override fun join() { + this._sync.withLock { + if (this._isExplicitlyDestroyed) + throw IllegalStateException("Can't join on destroyed thread") + if (this._nativeThread.state == jThread.State.NEW) + throw IllegalStateException("Can't join on thread that isn't started") + } + + // stdlib's join uses Object.wait which may block caller virtual thread, so let wait in way virtual threads support + this._threadFinishedWaiter.await() + this._nativeThread.join() + } + + override fun releaseResources() { + this._sync.withLock { + if (this._isExplicitlyDestroyed) + throw IllegalStateException("Thread already destroyed") + when (this._nativeThread.state) { + jThread.State.NEW -> {} + jThread.State.TERMINATED -> {} + jThread.State.RUNNABLE, jThread.State.BLOCKED, jThread.State.WAITING, jThread.State.TIMED_WAITING -> + throw IllegalStateException("Thread already was started") + } + this._isExplicitlyDestroyed = true + } + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt new file mode 100644 index 0000000..686b2a1 --- /dev/null +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt @@ -0,0 +1,42 @@ +package ru.landgrafhomyak.multitasking_0.threads + +import java.lang.Thread as jThread +import ru.landgrafhomyak.multitasking_0.Fiber + +@Suppress("AMBIGUOUS_EXPECTS", "ACTUAL_WITHOUT_EXPECT") +public actual sealed class Thread { + actual abstract override fun toString(): String + public actual var runningFiber: Fiber? = null + + protected abstract val _nativeThread: jThread + + public actual val name: String get() = this._nativeThread.name + public actual val isDaemon: Boolean get() = this._nativeThread.isDaemon + public actual abstract val state: State + public actual abstract val uncaughtException: Throwable + public actual abstract fun releaseResources() + + public actual abstract fun start() + + public actual abstract fun join() + + public actual enum class State { + NEW, + NOT_FINISHED, + FINISHED_SUCCESSFULLY, + FINISHED_WITH_ERROR, + DESTROYED + } + + public actual companion object { + @JvmStatic + public actual fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): Thread { + TODO() + } + + @JvmStatic + public actual fun currentThread(): Thread { + TODO() + } + } +} \ No newline at end of file diff --git a/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt b/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt new file mode 100644 index 0000000..fcd2542 --- /dev/null +++ b/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt @@ -0,0 +1,35 @@ +package ru.landgrafhomyak.multitasking_0.threads + +import kotlin.experimental.ExpectRefinement +import ru.landgrafhomyak.multitasking_0.Fiber + + +// didn't work because AMBIGUOUS_EXPECTS +@OptIn(ExperimentalMultiplatform::class) +@ExpectRefinement +public expect class Thread { + public val name: String + override fun toString(): String + public var runningFiber: Fiber? + + public fun start() + public fun join() + public val state: State + public val uncaughtException: Throwable + public fun releaseResources() + public val isDaemon: Boolean + + public enum class State { + NEW, + NOT_FINISHED, + FINISHED_SUCCESSFULLY, + FINISHED_WITH_ERROR, + DESTROYED + } + + public companion object { + public fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): Thread + public fun currentThread(): Thread + } +} + diff --git a/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadRoutine.kt b/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadRoutine.kt new file mode 100644 index 0000000..c209f94 --- /dev/null +++ b/src/multithreadPlatformMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadRoutine.kt @@ -0,0 +1,5 @@ +package ru.landgrafhomyak.multitasking_0.threads + +public fun interface ThreadRoutine { + public fun runThread(thread: Thread) +} \ No newline at end of file