diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/EventLoopClearer.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/EventLoopClearer.kt new file mode 100644 index 0000000..fc961a0 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/EventLoopClearer.kt @@ -0,0 +1,11 @@ +package ru.landgrafhomyak.multitasking_0.threads + +import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException +import ru.landgrafhomyak.multitasking_0.fibers.Fiber + +/** + * @see ThreadLocalMethods.setEventLoop + */ +public interface EventLoopClearer { + public fun clearEventLoop() +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/FiberExiter.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/FiberExiter.kt new file mode 100644 index 0000000..ef057c4 --- /dev/null +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/FiberExiter.kt @@ -0,0 +1,19 @@ +package ru.landgrafhomyak.multitasking_0.threads + +import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException +import ru.landgrafhomyak.multitasking_0.fibers.Fiber + +/** + * @see ThreadLocalMethods.enterFiber + */ +public interface FiberExiter { + public val callerFiber: Fiber? + /** + * Informs thread that execution flow was returned from [fiber][Fiber]. + * + * @throws IllegalStateException if [fiberToExit] doesn't match [current running fiber][ThreadLocalMethods.runningFiber] + * or this fiber isn't on top of fibers stack. + * @throws WrongCallerThreadException if called on thread different that caller of [Thread.current]. + */ + public fun exitFiber() +} \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt index 6a3460d..7455d4b 100644 --- a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt +++ b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt @@ -29,30 +29,13 @@ public expect sealed interface ThreadLocalMethods { * Informs thread that execution flow was switched to [fiber][Fiber]. * * @param fiberToEnter descriptor of fiber that is [going to run][Fiber.resume]. - * @param privateToken secret reference that should be used to call [exitFiber()][ThreadLocalMethods.exitFiber]. - * Need to avoid clearing [information about running fiber][ThreadLocalMethods.runningFiber] - * by anyone except implementation of this fiber. - * @return [fiber][Fiber] in which this function was called or `null` if it was called in thread. + * @return Callback object to rollback value of [Thread.current.runningFiber][ThreadLocalMethods.runningFiber] + * to state before this call. * * @throws WrongCallerThreadException if called on thread different that caller of [Thread.current]. */ - public fun enterFiber(fiberToEnter: Fiber, privateToken: Any): Fiber? - - /** - * Informs thread that execution flow was returned from [fiber][Fiber]. - * - * @param fiberToExit descriptor of fiber that is [going to return execution flow][Fiber.yield]. - * @param privateToken secret reference that was passed to [enterFiber()][ThreadLocalMethods.exitFiber]. - * Used to check that this function called by same caller as [enterFiber()][ThreadLocalMethods.exitFiber]. - * @param fiberToRestore return value of corresponding [enterFiber()][ThreadLocalMethods.exitFiber] call. - * - * @throws IllegalStateException if [fiberToExit] or [fiberToRestore] doesn't match. - * @throws IllegalArgumentException if [privateToken] doesn't match. - * @throws WrongCallerThreadException if called on thread different that caller of [Thread.current]. - */ - public fun exitFiber(fiberToExit: Fiber, privateToken: Any, fiberToRestore: Fiber?) + public fun enterFiber(fiberToEnter: Fiber): FiberExiter public val runningEventLoop: SingleThreadEventLoop? - public fun setEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) - public fun clearEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) + public fun setEventLoop(eventLoop: SingleThreadEventLoop): EventLoopClearer } \ No newline at end of file diff --git a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/_FibersStackNode.kt b/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/_FibersStackNode.kt deleted file mode 100644 index 473aa65..0000000 --- a/src/commonMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/_FibersStackNode.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ru.landgrafhomyak.multitasking_0.threads - -import kotlin.jvm.JvmField -import ru.landgrafhomyak.multitasking_0.fibers.Fiber - -internal class _FibersStackNode( - @JvmField - val caller: _FibersStackNode?, - @JvmField - val fiber: Fiber, - @JvmField - val privateToken: Any, -) { - fun assertExit(fiberToExit: Fiber, privateToken: Any, fiberToRestore: Fiber?) { - if (this.fiber !== fiberToExit || this.caller?.fiber !== fiberToRestore) - throw IllegalStateException("fiberToExit or fiberToRestore doesn't match") - - if (this.privateToken !== privateToken) - throw IllegalArgumentException("privateToken doesn't match") - } -} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/impl/java_virtual_threads/JavaVirtualThreadFiber.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/impl/java_virtual_threads/JavaVirtualThreadFiber.kt similarity index 94% rename from src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/impl/java_virtual_threads/JavaVirtualThreadFiber.kt rename to src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/impl/java_virtual_threads/JavaVirtualThreadFiber.kt index 3702761..d03daff 100644 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/impl/java_virtual_threads/JavaVirtualThreadFiber.kt +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/impl/java_virtual_threads/JavaVirtualThreadFiber.kt @@ -1,4 +1,4 @@ -package ru.landgrafhomyak.multitasking_0.threads.impl.java_virtual_threads +package ru.landgrafhomyak.multitasking_0.impl.java_virtual_threads import java.lang.Object as jObject import java.lang.Thread as jThread @@ -9,6 +9,7 @@ import ru.landgrafhomyak.multitasking_0.fibers.Fiber import ru.landgrafhomyak.multitasking_0.fibers.FiberRoutine import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException import ru.landgrafhomyak.multitasking_0.fibers.FiberInterruptedException +import ru.landgrafhomyak.multitasking_0.threads.FiberExiter import ru.landgrafhomyak.multitasking_0.threads.Thread as wThread public class JavaVirtualThreadFiber : Fiber { @@ -19,7 +20,7 @@ public class JavaVirtualThreadFiber : Fiber { private var _uncaughtException: Throwable? override val name: String private var _resumedOnThread: wThread? - private var _resumedOnFiber: Fiber? + private var _fiberExiter: FiberExiter? private var _interruptionData: InterruptionData? public constructor(name: String, routine: FiberRoutine) { @@ -29,7 +30,7 @@ public class JavaVirtualThreadFiber : Fiber { this._state = Fiber.State.CREATED this._uncaughtException = null this._resumedOnThread = null - this._resumedOnFiber = null + this._fiberExiter = null this._interruptionData = null this._syncLock.withLock { @@ -84,7 +85,7 @@ public class JavaVirtualThreadFiber : Fiber { get() = this._syncLock.withLock { if (this._state == Fiber.State.DESTROYED) throw IllegalStateException("Fiber destroyed") - return@withLock this._resumedOnFiber + return@withLock this._fiberExiter?.callerFiber } override fun yield() { @@ -103,7 +104,8 @@ public class JavaVirtualThreadFiber : Fiber { Fiber.State.DESTROYED -> throw IllegalStateException("Fiber destroyed") } - + this._fiberExiter!!.exitFiber() + this._fiberExiter = null wThread._tl_currentThread.set(null) this._syncCond.signal() this._syncCond.await() @@ -137,18 +139,15 @@ public class JavaVirtualThreadFiber : Fiber { Fiber.State.DESTROYED -> throw IllegalStateException("Fiber destroyed") } - val token = jObject() val currentThread = wThread.current this._resumedOnThread = currentThread.get() - this._resumedOnFiber = currentThread.enterFiber(this, token) + this._fiberExiter = currentThread.enterFiber(this) this._interruptionData = id this._syncCond.signal() this._syncCond.await() - currentThread.exitFiber(this, token, this.resumedOnFiber) this._resumedOnThread = null - this._resumedOnFiber = null } } diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt index 7e1a417..43f93fd 100644 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/Thread.kt @@ -1,5 +1,6 @@ package ru.landgrafhomyak.multitasking_0.threads +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock import java.lang.Thread as jThread @@ -159,7 +160,33 @@ public actual sealed class Thread { return this@Thread } - private var _fibersStack: _FibersStackNode? = null + private inner class FibersStackNode( + @JvmField + val caller: FibersStackNode?, + @JvmField + val fiber: Fiber, + ) : FiberExiter { + private val _isClosed = AtomicBoolean(false) + override val callerFiber: Fiber? + get() { + if (this._isClosed.get()) throw IllegalStateException("Fiber already exited and value in this property isn't actual") + return this.caller?.fiber + } + + override fun exitFiber() { + if (this._isClosed.compareAndExchange(false, true)) + throw IllegalStateException("Fiber already exited") + + if (this@ThreadLocalMethodsImpl._fibersStack !== this) { + this._isClosed.set(false) + throw IllegalStateException("Fiber being exited isn't on top of fibers stack") + } + + this@ThreadLocalMethodsImpl._fibersStack = this.caller + } + } + + private var _fibersStack: FibersStackNode? = null override val runningFiber: Fiber? get() { @@ -167,27 +194,14 @@ public actual sealed class Thread { return this._fibersStack?.fiber } - override fun enterFiber(fiberToEnter: Fiber, privateToken: Any): Fiber? { + override fun enterFiber(fiberToEnter: Fiber): FiberExiter { this._assertThread() - val caller = this._fibersStack - this._fibersStack = _FibersStackNode(caller, fiberToEnter, privateToken) - return caller?.fiber - } - - override fun exitFiber(fiberToExit: Fiber, privateToken: Any, fiberToRestore: Fiber?) { - this._assertThread() - - val top = this._fibersStack - if (top == null) - throw IllegalStateException("There is no running fiber to exit") - - top.assertExit(fiberToExit, privateToken, fiberToRestore) - - this._fibersStack = top.caller + val node = this.FibersStackNode(this._fibersStack, fiberToEnter) + this._fibersStack = node + return node } private var _runningEventLoop: SingleThreadEventLoop? = null - private var _runningEventLoopToken: Any? = null override val runningEventLoop: SingleThreadEventLoop? get() { @@ -196,23 +210,25 @@ public actual sealed class Thread { } - override fun setEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) { + private inner class EventLoopClearerImpl( + + ) : EventLoopClearer { + private val _isClosed = AtomicBoolean(false) + + override fun clearEventLoop() { + if (this._isClosed.compareAndExchange(false, true)) + throw IllegalStateException("Event loop already cleared") + + this@ThreadLocalMethodsImpl._runningEventLoop = null + } + } + + override fun setEventLoop(eventLoop: SingleThreadEventLoop): EventLoopClearer { this._assertThread() if (this._runningEventLoop != null) throw IllegalStateException("There is already a running event loop here") this._runningEventLoop = eventLoop - this._runningEventLoopToken = privateToken - } - - override fun clearEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) { - this._assertThread() - if (this._runningEventLoop !== eventLoop) - throw IllegalStateException("Currently another event loop is running") - if (this._runningEventLoopToken !== privateToken) - throw IllegalArgumentException("privateToken doesn't match") - - this._runningEventLoop = null - this._runningEventLoopToken = null + return this.EventLoopClearerImpl() } } } \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt index f376ed6..203c07a 100644 --- a/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt +++ b/src/jvmMain/kotlin/ru/landgrafhomyak/multitasking_0/threads/ThreadLocalMethods.kt @@ -7,10 +7,8 @@ public actual sealed interface ThreadLocalMethods { public actual fun get(): Thread public actual val runningFiber: Fiber? - public actual fun enterFiber(fiberToEnter: Fiber, privateToken: Any): Fiber? - public actual fun exitFiber(fiberToExit: Fiber, privateToken: Any, fiberToRestore: Fiber?) + public actual fun enterFiber(fiberToEnter: Fiber): FiberExiter public actual val runningEventLoop: SingleThreadEventLoop? - public actual fun setEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) - public actual fun clearEventLoop(eventLoop: SingleThreadEventLoop, privateToken: Any) + public actual fun setEventLoop(eventLoop: SingleThreadEventLoop): EventLoopClearer } \ No newline at end of file