ThreadController interface extracted from Thread descriptor
This commit is contained in:
parent
650213fc4c
commit
34debfcb7b
@ -4,11 +4,12 @@ import ru.landgrafhomyak.multitasking_0.fibers.Fiber
|
||||
|
||||
@Suppress("EXPECT_ACTUAL_IR_INCOMPATIBILITY")
|
||||
public expect class Thread {
|
||||
override fun toString(): String
|
||||
|
||||
/**
|
||||
* Name of thread for debug purposes.
|
||||
*/
|
||||
public val name: String
|
||||
override fun toString(): String
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
|
||||
@ -2,8 +2,6 @@ package ru.landgrafhomyak.multitasking_0.threads
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
import java.lang.Thread as jThread
|
||||
import ru.landgrafhomyak.multitasking_0.fibers.Fiber
|
||||
import ru.landgrafhomyak.multitasking_0.SingleThreadEventLoop
|
||||
@ -11,129 +9,21 @@ import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException
|
||||
import ru.landgrafhomyak.multitasking_0.threads.sync.ResumeThreadCallback
|
||||
|
||||
@Suppress("AMBIGUOUS_EXPECTS", "ACTUAL_WITHOUT_EXPECT")
|
||||
public actual sealed class Thread {
|
||||
public actual class Thread(
|
||||
@JvmField
|
||||
internal val _nativeThread: jThread
|
||||
) {
|
||||
private val _threadLocalMethods = this.ThreadLocalMethodsImpl()
|
||||
|
||||
protected abstract val _nativeThread: jThread
|
||||
protected val _sync: ReentrantLock = ReentrantLock()
|
||||
protected var _isExplicitlyDestroyed: Boolean = false
|
||||
protected var _uncaughtException: Throwable? = null
|
||||
|
||||
|
||||
actual abstract override fun toString(): String
|
||||
actual override fun toString(): String = "<platform native thread name='${this._nativeThread.name}'>"
|
||||
|
||||
public actual val name: String
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread destroyed")
|
||||
}
|
||||
return this._nativeThread.name
|
||||
}
|
||||
get() = this._nativeThread.name
|
||||
|
||||
public actual val isDaemon: Boolean
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread destroyed")
|
||||
}
|
||||
return this._nativeThread.isDaemon
|
||||
}
|
||||
public actual 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.STARTED
|
||||
jThread.State.BLOCKED -> return State.STARTED
|
||||
jThread.State.WAITING -> return State.STARTED
|
||||
jThread.State.TIMED_WAITING -> return State.STARTED
|
||||
jThread.State.TERMINATED -> {
|
||||
if (this._uncaughtException == null)
|
||||
return State.FINISHED_SUCCESSFULLY
|
||||
else
|
||||
return State.FINISHED_WITH_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public actual val uncaughtException: Throwable
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread destroyed")
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public actual 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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires explicit synchronization
|
||||
*/
|
||||
protected fun _assertJoinAllowed() {
|
||||
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")
|
||||
}
|
||||
|
||||
public actual abstract fun join()
|
||||
|
||||
public actual 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
|
||||
}
|
||||
}
|
||||
|
||||
public actual enum class State {
|
||||
NEW,
|
||||
STARTED,
|
||||
FINISHED_SUCCESSFULLY,
|
||||
FINISHED_WITH_ERROR,
|
||||
DESTROYED
|
||||
}
|
||||
|
||||
private object CurrentThreadVariable : ThreadLocal<Thread?>() {
|
||||
override fun initialValue(): Thread? {
|
||||
return ExternalThread(jThread.currentThread())
|
||||
return Thread(jThread.currentThread())
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,8 +36,8 @@ public actual sealed class Thread {
|
||||
get() = this._tl_currentThread.get()?._threadLocalMethods ?: throw RuntimeException("This java thread can't be wrapped")
|
||||
|
||||
@JvmStatic
|
||||
public actual fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): Thread =
|
||||
DrivenPlatformThread(name, isDaemon, routine)
|
||||
public actual fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): ThreadController =
|
||||
ThreadControllerImpl(name, isDaemon, routine)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
package ru.landgrafhomyak.multitasking_0.threads
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
import java.lang.Thread as jThread
|
||||
|
||||
@Suppress("JoinDeclarationAndAssignment")
|
||||
public class ThreadControllerImpl : ThreadController {
|
||||
public override val thread: Thread
|
||||
private val _sync: ReentrantLock = ReentrantLock()
|
||||
private var _isExplicitlyDestroyed: Boolean = false
|
||||
private var _uncaughtException: Throwable? = null
|
||||
private val _threadFinishedWaiter: CountDownLatch
|
||||
|
||||
internal constructor(name: String, isDaemon: Boolean, routine: ThreadRoutine) {
|
||||
this._threadFinishedWaiter = CountDownLatch(1)
|
||||
|
||||
this.thread = Thread(jThread.ofPlatform().name(name).daemon(isDaemon).unstarted(this.Kernel(routine)))
|
||||
}
|
||||
|
||||
private inner class Kernel(private val _routine: ThreadRoutine) : Runnable {
|
||||
override fun run() {
|
||||
try {
|
||||
Thread._tl_currentThread.set(this@ThreadControllerImpl.thread)
|
||||
this._routine.runThread(this@ThreadControllerImpl.thread)
|
||||
} catch (t: Throwable) {
|
||||
this@ThreadControllerImpl._sync.withLock {
|
||||
this@ThreadControllerImpl._uncaughtException = t
|
||||
}
|
||||
} finally {
|
||||
this@ThreadControllerImpl._threadFinishedWaiter.countDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = "<controller of platform native thread '${this.thread._nativeThread.name}'>"
|
||||
|
||||
public override val isDaemon: Boolean
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread destroyed")
|
||||
}
|
||||
return this.thread._nativeThread.isDaemon
|
||||
}
|
||||
|
||||
public override val state: ThreadController.State
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
return ThreadController.State.DESTROYED
|
||||
|
||||
when (this.thread._nativeThread.state) {
|
||||
jThread.State.NEW -> return ThreadController.State.NEW
|
||||
jThread.State.RUNNABLE -> return ThreadController.State.STARTED
|
||||
jThread.State.BLOCKED -> return ThreadController.State.STARTED
|
||||
jThread.State.WAITING -> return ThreadController.State.STARTED
|
||||
jThread.State.TIMED_WAITING -> return ThreadController.State.STARTED
|
||||
jThread.State.TERMINATED -> {
|
||||
if (this._uncaughtException == null)
|
||||
return ThreadController.State.FINISHED_SUCCESSFULLY
|
||||
else
|
||||
return ThreadController.State.FINISHED_WITH_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override val uncaughtException: Throwable
|
||||
get() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread destroyed")
|
||||
|
||||
val e = this._uncaughtException
|
||||
if (e != null)
|
||||
return e
|
||||
|
||||
when (this.thread._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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public 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.thread._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.thread._nativeThread.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun join() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Can't join on destroyed thread")
|
||||
if (this.thread._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.thread._nativeThread.join()
|
||||
}
|
||||
|
||||
public override fun releaseResources() {
|
||||
this._sync.withLock {
|
||||
if (this._isExplicitlyDestroyed)
|
||||
throw IllegalStateException("Thread already destroyed")
|
||||
when (this.thread._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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,77 +8,12 @@ import ru.landgrafhomyak.multitasking_0.fibers.Fiber
|
||||
@ExpectRefinement
|
||||
@Suppress("EXPECT_ACTUAL_IR_INCOMPATIBILITY")
|
||||
public expect class Thread {
|
||||
/**
|
||||
* Name of thread for debug purposes.
|
||||
*
|
||||
* After [releaseResources()][Thread.releaseResources] become inaccessible.
|
||||
*/
|
||||
public val name: String
|
||||
override fun toString(): String
|
||||
|
||||
/**
|
||||
* Start execution of [routine][ThreadRoutine] passed to [create()][Thread.create].
|
||||
*
|
||||
* Doesn't define when any resources used by underlying functionality (e.g., stack) must be allocated.
|
||||
* Name of thread for debug purposes.
|
||||
*/
|
||||
public fun start()
|
||||
|
||||
/**
|
||||
* Blocks caller thread or fiber until [releaseResources()][Thread.releaseResources] call allowed.
|
||||
*
|
||||
* Calling this method before thread [started][Thread.start] is error.
|
||||
*/
|
||||
public fun join()
|
||||
|
||||
/**
|
||||
* Confirms that all resources used by underlying functionality are released.
|
||||
* Can be called only once.
|
||||
* Can be called before thread [started][Thread.start] or after thread [finishes][Thread.join].
|
||||
*/
|
||||
public fun releaseResources()
|
||||
|
||||
/**
|
||||
* State of thread. Always accessible.
|
||||
*/
|
||||
public val state: State
|
||||
|
||||
/**
|
||||
* Exception that wasn't uncaught by [routine][ThreadRoutine] and terminated thread.
|
||||
*
|
||||
* Accessible only in [FINISHED_WITH_ERROR][Thread.State.FINISHED_WITH_ERROR] state,
|
||||
* in other [states][Thread.state] will throw error, even after [releaseResources()][Thread.releaseResources].
|
||||
*/
|
||||
public val uncaughtException: Throwable
|
||||
|
||||
/**
|
||||
* Returns `false` if thread prevents process from being finished while thread is running.
|
||||
*
|
||||
* After [releaseResources()][Thread.releaseResources] become inaccessible.
|
||||
*/
|
||||
public val isDaemon: Boolean
|
||||
|
||||
public enum class State {
|
||||
/**
|
||||
* Thread [created][Thread.create] but not [started][Thread.start] yet.
|
||||
*/
|
||||
NEW,
|
||||
/**
|
||||
* Thread [started][Thread.start], but not finished yet.
|
||||
*/
|
||||
STARTED,
|
||||
/**
|
||||
* Thread finished ([routine][ThreadRoutine] returned) normally.
|
||||
*/
|
||||
FINISHED_SUCCESSFULLY,
|
||||
/**
|
||||
* Thread [finished with error][Thread.uncaughtException], that wasn't caught by [routine][ThreadRoutine].
|
||||
*/
|
||||
FINISHED_WITH_ERROR,
|
||||
/**
|
||||
* All resources allocated by this [Thread] object are released and reference to descriptor can be lost.
|
||||
*/
|
||||
DESTROYED
|
||||
}
|
||||
public val name: String
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
@ -101,7 +36,7 @@ public expect class Thread {
|
||||
* @param isDaemon should thread prevent process from being finished while thread is running.
|
||||
* @param routine
|
||||
*/
|
||||
public fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): Thread
|
||||
public fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): ThreadController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
package ru.landgrafhomyak.multitasking_0.threads
|
||||
|
||||
public interface ThreadController {
|
||||
/**
|
||||
* Thread controlled by this instance.
|
||||
*/
|
||||
public val thread: Thread
|
||||
|
||||
/**
|
||||
* Start execution of [routine][ThreadRoutine] passed to [create()][Thread.create].
|
||||
*
|
||||
* Doesn't define when any resources used by underlying functionality (e.g., stack) must be allocated.
|
||||
*/
|
||||
public fun start()
|
||||
|
||||
/**
|
||||
* Blocks caller thread or fiber until [releaseResources()][ThreadController.releaseResources] call allowed.
|
||||
*
|
||||
* Calling this method before thread [started][ThreadController.start] is error.
|
||||
*/
|
||||
public fun join()
|
||||
|
||||
/**
|
||||
* Confirms that all resources used by underlying functionality are released.
|
||||
* Can be called only once.
|
||||
* Can be called before thread [started][ThreadController.start] or after thread [finishes][ThreadController.join].
|
||||
*/
|
||||
public fun releaseResources()
|
||||
|
||||
/**
|
||||
* State of thread. Always accessible.
|
||||
*/
|
||||
public val state: State
|
||||
|
||||
/**
|
||||
* Exception that wasn't uncaught by [routine][ThreadRoutine] and terminated thread.
|
||||
*
|
||||
* Accessible only in [FINISHED_WITH_ERROR][ThreadController.State.FINISHED_WITH_ERROR] state,
|
||||
* in other [states][ThreadController.state] will throw error, even after [releaseResources()][ThreadController.releaseResources].
|
||||
*/
|
||||
public val uncaughtException: Throwable
|
||||
|
||||
/**
|
||||
* Returns `false` if thread prevents process from being finished while thread is running.
|
||||
*
|
||||
* After [releaseResources()][ThreadController.releaseResources] become inaccessible.
|
||||
*/
|
||||
public val isDaemon: Boolean
|
||||
|
||||
public enum class State {
|
||||
/**
|
||||
* Thread [created][Thread.create] but not [started][ThreadController.start] yet.
|
||||
*/
|
||||
NEW,
|
||||
/**
|
||||
* Thread [started][ThreadController.start], but not finished yet.
|
||||
*/
|
||||
STARTED,
|
||||
/**
|
||||
* Thread finished ([routine][ThreadRoutine] returned) normally.
|
||||
*/
|
||||
FINISHED_SUCCESSFULLY,
|
||||
/**
|
||||
* Thread [finished with error][ThreadController.uncaughtException], that wasn't caught by [routine][ThreadRoutine].
|
||||
*/
|
||||
FINISHED_WITH_ERROR,
|
||||
/**
|
||||
* All resources allocated by this [ThreadController] object are released and reference to descriptor can be lost.
|
||||
*/
|
||||
DESTROYED
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user