Thread implementation for windows
This commit is contained in:
parent
67079c1565
commit
b570518b03
@ -56,6 +56,16 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mingwX64().compilations["main"].cinterops {
|
||||||
|
create("windows") {
|
||||||
|
definitionFile = rootDir.resolve("src/windowsMain/c/windows.c2kt_def")
|
||||||
|
packageName = "ru.landgrafhomyak.multitasking_0._c"
|
||||||
|
includeDirs(rootDir.resolve("src/windowsMain/c/Include"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -72,10 +82,16 @@ kotlin {
|
|||||||
mingwX64().compilations["main"].defaultSourceSet {
|
mingwX64().compilations["main"].defaultSourceSet {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.6")
|
implementation("ru.landgrafhomyak.utility:highlevel-try-finally:0.6")
|
||||||
implementation("ru.landgrafhomyak.utility:closeable-state-1:1.2")
|
implementation("ru.landgrafhomyak.utility:closeable-state-1:1.3")
|
||||||
implementation("ru.landgrafhomyak.utility:kotlin-native-interop-utilities-0:0.1")
|
implementation("ru.landgrafhomyak.utility:kotlin-native-interop-utilities-0:0.1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mingwX64().compilations["test"].defaultSourceSet {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val notJvmMain by creating { dependsOn(commonMain) }
|
val notJvmMain by creating { dependsOn(commonMain) }
|
||||||
val notJvmTest by creating { dependsOn(commonTest) }
|
val notJvmTest by creating { dependsOn(commonTest) }
|
||||||
@ -109,6 +125,15 @@ kotlin {
|
|||||||
val windowsTest by creating { dependsOn(multithreadPlatformTest) }
|
val windowsTest by creating { dependsOn(multithreadPlatformTest) }
|
||||||
mingwX64().compilations["main"].defaultSourceSet.dependsOn(windowsMain)
|
mingwX64().compilations["main"].defaultSourceSet.dependsOn(windowsMain)
|
||||||
mingwX64().compilations["test"].defaultSourceSet.dependsOn(windowsTest)
|
mingwX64().compilations["test"].defaultSourceSet.dependsOn(windowsTest)
|
||||||
|
|
||||||
|
val x64Main by creating { dependsOn(commonMain) }
|
||||||
|
val x64Test by creating { dependsOn(commonTest) }
|
||||||
|
mingwX64().compilations["main"].defaultSourceSet.dependsOn(x64Main)
|
||||||
|
mingwX64().compilations["test"].defaultSourceSet.dependsOn(x64Test)
|
||||||
|
linuxX64().compilations["main"].defaultSourceSet.dependsOn(x64Main)
|
||||||
|
linuxX64().compilations["test"].defaultSourceSet.dependsOn(x64Test)
|
||||||
|
macosX64().compilations["main"].defaultSourceSet.dependsOn(x64Main)
|
||||||
|
macosX64().compilations["test"].defaultSourceSet.dependsOn(x64Test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
kotlin.native.ignoreDisabledTargets=true
|
kotlin.native.ignoreDisabledTargets=true
|
||||||
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
||||||
kotlin.native.enableKlibsCrossCompilation=true
|
kotlin.native.enableKlibsCrossCompilation=true
|
||||||
kotlin.stdlib.default.dependency=false
|
kotlin.stdlib.default.dependency=false
|
||||||
|
kotlin.mpp.enableCInteropCommonization=true
|
||||||
@ -7,7 +7,7 @@ import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException
|
|||||||
import ru.landgrafhomyak.multitasking_0.threads.sync.ResumeThreadCallback
|
import ru.landgrafhomyak.multitasking_0.threads.sync.ResumeThreadCallback
|
||||||
|
|
||||||
@Suppress("AMBIGUOUS_EXPECTS", "ACTUAL_WITHOUT_EXPECT")
|
@Suppress("AMBIGUOUS_EXPECTS", "ACTUAL_WITHOUT_EXPECT")
|
||||||
public actual class Thread(
|
public actual class Thread internal constructor(
|
||||||
@JvmField
|
@JvmField
|
||||||
internal val _nativeThread: jThread,
|
internal val _nativeThread: jThread,
|
||||||
) {
|
) {
|
||||||
@ -42,7 +42,7 @@ public actual class Thread(
|
|||||||
private class ThreadLocalMethodsImpl(thread: Thread) : _CommonThreadLocalMethods(thread), ThreadLocalMethods {
|
private class ThreadLocalMethodsImpl(thread: Thread) : _CommonThreadLocalMethods(thread), ThreadLocalMethods {
|
||||||
override fun _assertCurrentThread() {
|
override fun _assertCurrentThread() {
|
||||||
if (Thread._tl_currentThread.get() !== this.thread)
|
if (Thread._tl_currentThread.get() !== this.thread)
|
||||||
throw WrongCallerThreadException("Reference returned by 'Thread.current' must be used only in thread where it was produced")
|
throw WrongCallerThreadException("Object returned by 'Thread.current' must be used only in thread where it was produced")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun yield() {
|
override fun yield() {
|
||||||
|
|||||||
@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
# define ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_USE_FUTEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC {
|
||||||
|
CRITICAL_SECTION data__sync;
|
||||||
|
CONDITION_VARIABLE data__sync_notify;
|
||||||
|
CRITICAL_SECTION *sync;
|
||||||
|
CONDITION_VARIABLE *sync_notify;
|
||||||
|
#ifndef ru_landgrafhomyak_multitasking_0_threads_ExternalThreadDescriptorsGC_USE_FUTEX
|
||||||
|
CRITICAL_SECTION data__thread_ready_cs;
|
||||||
|
CONDITION_VARIABLE data__thread_ready_cv;
|
||||||
|
CRITICAL_SECTION *thread_ready_cs;
|
||||||
|
CONDITION_VARIABLE *thread_ready_cv;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_init(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC *self
|
||||||
|
) {
|
||||||
|
self->sync = &(self->data__sync);
|
||||||
|
self->sync_notify = &(self->data__sync_notify);
|
||||||
|
InitializeCriticalSection(self->sync);
|
||||||
|
#ifndef ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_USE_FUTEX
|
||||||
|
self->thread_ready_cs = &(self->data__thread_ready_cs);
|
||||||
|
self->thread_ready_cv = &(self->data__thread_ready_cv);
|
||||||
|
InitializeCriticalSection(self->thread_ready_cs);
|
||||||
|
InitializeConditionVariable(self->thread_ready_cv);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_deinit(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC *self
|
||||||
|
) {
|
||||||
|
DeleteCriticalSection(self->sync);
|
||||||
|
#ifndef ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_USE_FUTEX
|
||||||
|
DeleteCriticalSection(self->thread_ready_cs);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage {
|
||||||
|
SIZE_T slots_used;
|
||||||
|
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
|
||||||
|
void *wrappers[MAXIMUM_WAIT_OBJECTS - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_init(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *self,
|
||||||
|
HANDLE notifier
|
||||||
|
) {
|
||||||
|
self->slots_used = 0;
|
||||||
|
self->handles[0] = notifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _Bool ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_tryAdd(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *self,
|
||||||
|
HANDLE handle,
|
||||||
|
void *wrapper
|
||||||
|
) {
|
||||||
|
if (self->slots_used >= MAXIMUM_WAIT_OBJECTS - 1)
|
||||||
|
return 1;
|
||||||
|
self->wrappers[self->slots_used++] = wrapper;
|
||||||
|
self->handles[self->slots_used] = handle;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_wait(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *self,
|
||||||
|
SIZE_T slots_count_cached
|
||||||
|
) {
|
||||||
|
return WaitForMultipleObjects(
|
||||||
|
slots_count_cached + 1,
|
||||||
|
self->handles,
|
||||||
|
FALSE,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_removeSlotAndCloseHandle(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *self,
|
||||||
|
SIZE_T pos
|
||||||
|
) {
|
||||||
|
pos--;
|
||||||
|
void *wrapper = self->wrappers[pos];
|
||||||
|
if (0 == CloseHandle(self->handles[pos + 1]))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (SIZE_T i = pos + 1; i < self->slots_used; i++) {
|
||||||
|
self->handles[i] = self->handles[i + 1];
|
||||||
|
self->wrappers[i - 1] = self->wrappers[i];
|
||||||
|
}
|
||||||
|
self->slots_used--;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SIZE_T ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_shitToNextWorker(
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *self,
|
||||||
|
struct ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage *next
|
||||||
|
) {
|
||||||
|
if (next != NULL) {
|
||||||
|
while (next->slots_used < (MAXIMUM_WAIT_OBJECTS - 1) && self->slots_used > 0) {
|
||||||
|
self->slots_used--;
|
||||||
|
next->wrappers[next->slots_used++] = self->wrappers[self->slots_used];
|
||||||
|
next->handles[next->slots_used] = self->handles[self->slots_used + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self->slots_used;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static inline HANDLE ru_landgrafhomyak_multitasking_0_duplicateHandle(HANDLE handle) {
|
||||||
|
HANDLE new_handle = NULL;
|
||||||
|
DuplicateHandle(
|
||||||
|
GetCurrentProcess(), handle,
|
||||||
|
GetCurrentProcess(), &new_handle,
|
||||||
|
0, FALSE, DUPLICATE_SAME_ACCESS
|
||||||
|
);
|
||||||
|
return new_handle;
|
||||||
|
}
|
||||||
2
src/windowsMain/c/windows.c2kt_def
Normal file
2
src/windowsMain/c/windows.c2kt_def
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
headers = ru/landgrafhomyak/multitasking_0/utility.h ru/landgrafhomyak/multitasking_0/handle_and_pointer_background_garbage_collector.h
|
||||||
|
headerFilter = ru/landgrafhomyak/multitasking_0/**.h
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
package ru.landgrafhomyak.multitasking_0
|
||||||
|
|
||||||
|
import kotlinx.cinterop.COpaquePointer
|
||||||
|
import kotlinx.cinterop.CPointer
|
||||||
|
import kotlinx.cinterop.ExperimentalForeignApi
|
||||||
|
import kotlinx.cinterop.StableRef
|
||||||
|
import kotlinx.cinterop.reinterpret
|
||||||
|
import platform.windows.CRITICAL_SECTION
|
||||||
|
import platform.windows.CloseHandle
|
||||||
|
import platform.windows.CreateEventA
|
||||||
|
import platform.windows.EnterCriticalSection
|
||||||
|
import platform.windows.FALSE
|
||||||
|
import platform.windows.HANDLE
|
||||||
|
import platform.windows.HEAP_NO_SERIALIZE
|
||||||
|
import platform.windows.HeapAlloc
|
||||||
|
import platform.windows.HeapCreate
|
||||||
|
import platform.windows.HeapDestroy
|
||||||
|
import platform.windows.HeapFree
|
||||||
|
import platform.windows.InitializeCriticalSection
|
||||||
|
import platform.windows.LeaveCriticalSection
|
||||||
|
import platform.windows.MAXIMUM_WAIT_OBJECTS
|
||||||
|
import platform.windows.SetEvent
|
||||||
|
import platform.windows.WAIT_ABANDONED_0
|
||||||
|
import platform.windows.WAIT_FAILED
|
||||||
|
import platform.windows.WAIT_OBJECT_0
|
||||||
|
import platform.windows.WAIT_TIMEOUT
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_init
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_removeSlotAndCloseHandle
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_shitToNextWorker
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_tryAdd
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_wait
|
||||||
|
import ru.landgrafhomyak.multitasking_0.threads.Thread
|
||||||
|
import ru.landgrafhomyak.multitasking_0.threads.ThreadController
|
||||||
|
import ru.landgrafhomyak.multitasking_0.threads.ThreadRoutine
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.CloseableState
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.ManualStateManipulation
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.OwnedUsagesCounter
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.withUse
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.sizeOfUL
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.WindowsApiException
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.nullToWinApiErr
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.zeroToWinApiErr
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Suppress("JoinDeclarationAndAssignment")
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
internal abstract class HandleAndPointerBackgroundGarbageCollector {
|
||||||
|
protected abstract fun _destroyPtr(ptr: COpaquePointer)
|
||||||
|
protected abstract fun _generateThreadName(): String
|
||||||
|
|
||||||
|
private val _heap: HANDLE
|
||||||
|
private val _sync: CPointer<CRITICAL_SECTION>
|
||||||
|
private val _stableRef: StableRef<HandleAndPointerBackgroundGarbageCollector>
|
||||||
|
private var _lastWorker: WorkerData?
|
||||||
|
protected val _state: CloseableState.AllowsConcurrency
|
||||||
|
|
||||||
|
init {
|
||||||
|
this._state = OwnedUsagesCounter(this)
|
||||||
|
this._lastWorker = null
|
||||||
|
this._stableRef = StableRef.create(this)
|
||||||
|
safeAutoClose2(onError = { this._stableRef.dispose() }) {
|
||||||
|
this._heap = nullToWinApiErr { HeapCreate(HEAP_NO_SERIALIZE.toUInt(), sizeOfUL<ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC>(), 0u) }
|
||||||
|
safeAutoClose2(onError = { HeapDestroy(this._heap) }) {
|
||||||
|
this._sync = nullToWinApiErr { HeapAlloc(this._heap, 0u, sizeOfUL<CRITICAL_SECTION>()) }.reinterpret()
|
||||||
|
InitializeCriticalSection(this._sync)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun _register(handle: HANDLE, wrapper: COpaquePointer) = this._state.withUse {
|
||||||
|
EnterCriticalSection(this._sync)
|
||||||
|
safeAutoClose1(finally = { LeaveCriticalSection(this._sync) }) {
|
||||||
|
var worker = this._lastWorker ?: this._createNewWorker()
|
||||||
|
while (ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_tryAdd(worker.handlesStorage, handle, wrapper)) {
|
||||||
|
worker = this._createNewWorker()
|
||||||
|
}
|
||||||
|
zeroToWinApiErr { SetEvent(worker.notifier) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun _createNewWorker(): WorkerData {
|
||||||
|
val notifier = nullToWinApiErr { CreateEventA(null, FALSE, FALSE, null) }
|
||||||
|
safeAutoClose2(onError = { CloseHandle(notifier) }) {
|
||||||
|
val handlesStorage = HeapAlloc(this._heap, 0u, sizeOfUL<ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage>())
|
||||||
|
.nullToWinApiErr().reinterpret<ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage>()
|
||||||
|
safeAutoClose2(onError = { HeapFree(this._heap, 0u, handlesStorage) }) {
|
||||||
|
ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_init(handlesStorage, notifier)
|
||||||
|
val dataObj = WorkerData(this._lastWorker, handlesStorage, notifier)
|
||||||
|
this._lastWorker = dataObj
|
||||||
|
dataObj.threadController = Thread.create(this._generateThreadName(), true, this.ThreadRoutineImpl(dataObj))
|
||||||
|
dataObj.threadController.start()
|
||||||
|
return@_createNewWorker dataObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ManualStateManipulation::class)
|
||||||
|
open fun destroy() {
|
||||||
|
this._state.close()
|
||||||
|
while (true) {
|
||||||
|
val worker = this._lastWorker ?: break
|
||||||
|
EnterCriticalSection(this._sync)
|
||||||
|
safeAutoClose1(finally = { LeaveCriticalSection(this._sync) }) {
|
||||||
|
worker.interrupted = true
|
||||||
|
zeroToWinApiErr { SetEvent(worker.notifier) }
|
||||||
|
}
|
||||||
|
worker.threadController.join()
|
||||||
|
worker.threadController.releaseResources()
|
||||||
|
this._lastWorker = worker.next
|
||||||
|
zeroToWinApiErr { HeapFree(this._heap, 0u, worker.handlesStorage) }
|
||||||
|
zeroToWinApiErr { CloseHandle(worker.notifier) }
|
||||||
|
}
|
||||||
|
zeroToWinApiErr { HeapFree(this._heap, 0u, this._sync) }
|
||||||
|
zeroToWinApiErr { HeapDestroy(this._heap) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class WorkerData(
|
||||||
|
val next: WorkerData?,
|
||||||
|
val handlesStorage: CPointer<ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage>,
|
||||||
|
val notifier: HANDLE,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var _threadController: ThreadController? = null
|
||||||
|
var threadController: ThreadController
|
||||||
|
get() = this._threadController ?: throw IllegalStateException("Not initialized")
|
||||||
|
set(value) {
|
||||||
|
if (this._threadController != null)
|
||||||
|
throw IllegalStateException("Already initialized")
|
||||||
|
this._threadController = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var interrupted = false
|
||||||
|
set(value) {
|
||||||
|
if (field && !value)
|
||||||
|
throw IllegalStateException("Can't cancel interruption")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ThreadRoutineImpl(
|
||||||
|
private val _data: WorkerData,
|
||||||
|
) : ThreadRoutine {
|
||||||
|
override fun runThread(thread: Thread) {
|
||||||
|
try {
|
||||||
|
var usedSlotsCountCached = 1uL
|
||||||
|
mainloop@ while (true) {
|
||||||
|
when (val pos = ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_wait(this._data.handlesStorage, usedSlotsCountCached)) {
|
||||||
|
WAIT_FAILED -> WindowsApiException.throwFromLastWindowsErr()
|
||||||
|
WAIT_TIMEOUT.toUInt() -> continue
|
||||||
|
in WAIT_ABANDONED_0..<(WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS.toUInt()) -> TODO()
|
||||||
|
in WAIT_OBJECT_0..<(WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS.toUInt()) -> {
|
||||||
|
EnterCriticalSection(this@HandleAndPointerBackgroundGarbageCollector._sync)
|
||||||
|
safeAutoClose1(finally = { LeaveCriticalSection(this@HandleAndPointerBackgroundGarbageCollector._sync) }) {
|
||||||
|
if (pos != WAIT_OBJECT_0) {
|
||||||
|
val wrapper = nullToWinApiErr {
|
||||||
|
ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_removeSlotAndCloseHandle(
|
||||||
|
this._data.handlesStorage, (pos - WAIT_OBJECT_0).toULong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this@HandleAndPointerBackgroundGarbageCollector._destroyPtr(wrapper)
|
||||||
|
}
|
||||||
|
usedSlotsCountCached = ru_landgrafhomyak_multitasking_0_HandleAndPointerBackgroundGC_HandlesStorage_shitToNextWorker(
|
||||||
|
this._data.handlesStorage,
|
||||||
|
this._data.next?.handlesStorage
|
||||||
|
)
|
||||||
|
this._data.next?.notifier?.let { n -> SetEvent(n) }?.zeroToWinApiErr()
|
||||||
|
if (this._data.interrupted) return@runThread
|
||||||
|
continue@mainloop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package ru.landgrafhomyak.multitasking_0.threads
|
||||||
|
|
||||||
|
import kotlinx.cinterop.COpaquePointer
|
||||||
|
import kotlinx.cinterop.ExperimentalForeignApi
|
||||||
|
import kotlinx.cinterop.StableRef
|
||||||
|
import kotlinx.cinterop.asStableRef
|
||||||
|
import platform.windows.CloseHandle
|
||||||
|
import platform.windows.GetCurrentThread
|
||||||
|
import platform.windows.HANDLE
|
||||||
|
import platform.windows.SwitchToThread
|
||||||
|
import ru.landgrafhomyak.multitasking_0.HandleAndPointerBackgroundGarbageCollector
|
||||||
|
import ru.landgrafhomyak.multitasking_0.WrongCallerThreadException
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_duplicateHandle
|
||||||
|
import ru.landgrafhomyak.multitasking_0.threads.sync.ResumeThreadCallback
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.nullToWinApiErr
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.zeroToWinApiErr
|
||||||
|
|
||||||
|
//@Suppress("AMBIGUOUS_EXPECTS", "ACTUAL_WITHOUT_EXPECT")
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
public actual class Thread(
|
||||||
|
internal val _nativeThread: HANDLE,
|
||||||
|
public actual val name: String,
|
||||||
|
internal val isExternal: Boolean,
|
||||||
|
) {
|
||||||
|
internal val _stableRef: StableRef<Thread> = StableRef.create(this)
|
||||||
|
private val _threadLocalMethods = ThreadLocalMethodsImpl(this)
|
||||||
|
|
||||||
|
actual override fun toString(): String = "<platform native thread name='${this.name}'>"
|
||||||
|
|
||||||
|
private object ExternThreadsGC : HandleAndPointerBackgroundGarbageCollector() {
|
||||||
|
override fun _destroyPtr(ptr: COpaquePointer) {
|
||||||
|
ptr.asStableRef<Thread>().dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun _generateThreadName(): String {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerCurrentThread(): Thread {
|
||||||
|
val handle = nullToWinApiErr { ru_landgrafhomyak_multitasking_0_duplicateHandle(GetCurrentThread()) }
|
||||||
|
val wrapper: Thread
|
||||||
|
safeAutoClose2(onError = { zeroToWinApiErr { CloseHandle(handle) } }) {
|
||||||
|
wrapper = Thread(handle, ""/*todo GetThreadDescription starting from win10*/, isExternal = true)
|
||||||
|
safeAutoClose2(onError = { wrapper._stableRef.dispose() }) {
|
||||||
|
this._register(handle, wrapper._stableRef.asCPointer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CurrentThreadVariable {
|
||||||
|
private val _tls = ThreadLocalVariable()
|
||||||
|
fun get(): Thread? = this._tls.get()?.asStableRef<Thread>()?.get()
|
||||||
|
fun set(t: Thread?) {
|
||||||
|
this._tls.set(t?._stableRef?.asCPointer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual companion object {
|
||||||
|
internal val _tl_currentThread = CurrentThreadVariable()
|
||||||
|
|
||||||
|
|
||||||
|
public actual
|
||||||
|
val current: ThreadLocalMethods
|
||||||
|
get() {
|
||||||
|
val existing = this._tl_currentThread.get()
|
||||||
|
if (existing != null) return existing._threadLocalMethods
|
||||||
|
|
||||||
|
val created = ExternThreadsGC.registerCurrentThread()
|
||||||
|
this._tl_currentThread.set(created)
|
||||||
|
return created._threadLocalMethods
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual fun create(name: String, isDaemon: Boolean, routine: ThreadRoutine): ThreadController =
|
||||||
|
ThreadControllerImpl(name, isDaemon, routine)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class ThreadLocalMethodsImpl(thread: Thread) : _CommonThreadLocalMethods(thread), ThreadLocalMethods {
|
||||||
|
override fun _assertCurrentThread() {
|
||||||
|
if (Thread._tl_currentThread.get() != this.thread)
|
||||||
|
throw WrongCallerThreadException("Object returned by 'Thread.current' must be used only in thread where it was produced")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun yield() {
|
||||||
|
this._assertCurrentThread()
|
||||||
|
SwitchToThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun suspendThread(store: (ResumeThreadCallback) -> Unit) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun suspendThread(timeoutMillis: UInt, timeoutHandler: ResumeThreadCallback.TimeoutHandler, store: (ResumeThreadCallback) -> Unit) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
package ru.landgrafhomyak.multitasking_0.threads
|
||||||
|
|
||||||
|
import kotlin.concurrent.atomics.AtomicReference
|
||||||
|
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||||
|
import kotlinx.cinterop.ExperimentalForeignApi
|
||||||
|
import kotlinx.cinterop.StableRef
|
||||||
|
import kotlinx.cinterop.asStableRef
|
||||||
|
import kotlinx.cinterop.staticCFunction
|
||||||
|
import platform.windows.CREATE_SUSPENDED
|
||||||
|
import platform.windows.CloseHandle
|
||||||
|
import platform.windows.CreateThread
|
||||||
|
import platform.windows.INFINITE
|
||||||
|
import platform.windows.ResumeThread
|
||||||
|
import platform.windows.WAIT_FAILED
|
||||||
|
import platform.windows.WAIT_OBJECT_0
|
||||||
|
import platform.windows.WAIT_TIMEOUT
|
||||||
|
import platform.windows.WaitForSingleObject
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.CloseableState
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.Destructor
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.OwnedErrorOnConcurrentAccessState
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.withUse
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.withUseIfNotClosed
|
||||||
|
import ru.landgrafhomyak.utility.closeable_state_1.withUseThenClose
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.WindowsApiException
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.nullToWinApiErr
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.zeroToWinApiErr
|
||||||
|
|
||||||
|
@Suppress("JoinDeclarationAndAssignment")
|
||||||
|
@OptIn(ExperimentalForeignApi::class, ExperimentalAtomicApi::class)
|
||||||
|
public class ThreadControllerImpl : ThreadController {
|
||||||
|
public override val thread: Thread
|
||||||
|
private val _guard: CloseableState.ExternallySynchronized
|
||||||
|
private var _uncaughtException: AtomicReference<Throwable?>
|
||||||
|
private var _isStarted: Boolean
|
||||||
|
|
||||||
|
internal constructor(name: String, isDaemon: Boolean, routine: ThreadRoutine) {
|
||||||
|
this._uncaughtException = AtomicReference(null)
|
||||||
|
this._guard = OwnedErrorOnConcurrentAccessState(this)
|
||||||
|
this._isStarted = false
|
||||||
|
|
||||||
|
val kernel = this.Kernel(routine)
|
||||||
|
val kernelRef = StableRef.create(kernel)
|
||||||
|
safeAutoClose2(onError = { kernelRef.dispose() }) {
|
||||||
|
val handle = nullToWinApiErr {
|
||||||
|
CreateThread(
|
||||||
|
null,
|
||||||
|
0uL,
|
||||||
|
staticCFunction { kernelPtr ->
|
||||||
|
val kernelRef = kernelPtr!!.asStableRef<Kernel>()
|
||||||
|
val kernel = kernelRef.get()
|
||||||
|
kernelRef.dispose()
|
||||||
|
kernel.run()
|
||||||
|
return@staticCFunction 0u
|
||||||
|
},
|
||||||
|
kernelRef.asCPointer(),
|
||||||
|
CREATE_SUSPENDED.toUInt(),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
safeAutoClose2(onError = { zeroToWinApiErr { CloseHandle(handle) } }) {
|
||||||
|
this.thread = Thread(handle, name, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class Kernel(private val _routine: ThreadRoutine) {
|
||||||
|
fun run() {
|
||||||
|
try {
|
||||||
|
Thread._tl_currentThread.set(this@ThreadControllerImpl.thread)
|
||||||
|
this._routine.runThread(this@ThreadControllerImpl.thread)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
this@ThreadControllerImpl._uncaughtException.store(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "<controller of platform native thread '${this.thread.name}'>"
|
||||||
|
|
||||||
|
public override val isDaemon: Boolean
|
||||||
|
get() {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun _testFinished(): Boolean {
|
||||||
|
when (WaitForSingleObject(this.thread._nativeThread, 0u)) {
|
||||||
|
WAIT_FAILED -> WindowsApiException.throwFromLastWindowsErr()
|
||||||
|
WAIT_TIMEOUT.toUInt() -> return false
|
||||||
|
WAIT_OBJECT_0 -> return true
|
||||||
|
else -> throw RuntimeException("Unexpected return value of 'WaitForSingleObject'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override val state: ThreadController.State
|
||||||
|
get() = this._guard.withUseIfNotClosed(ThreadController.State.DESTROYED) {
|
||||||
|
if (!this._isStarted) return@withUseIfNotClosed ThreadController.State.NEW
|
||||||
|
|
||||||
|
if (!this._testFinished())
|
||||||
|
return@withUseIfNotClosed ThreadController.State.STARTED
|
||||||
|
else {
|
||||||
|
if (this._uncaughtException.load() != null)
|
||||||
|
return@withUseIfNotClosed ThreadController.State.FINISHED_WITH_ERROR
|
||||||
|
else
|
||||||
|
return@withUseIfNotClosed ThreadController.State.FINISHED_SUCCESSFULLY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override val uncaughtException: Throwable
|
||||||
|
get() = this._guard.withUse {
|
||||||
|
val e = this._uncaughtException.load()
|
||||||
|
if (e != null)
|
||||||
|
return e
|
||||||
|
|
||||||
|
if (!this._isStarted)
|
||||||
|
return@withUse throw IllegalStateException("Thread not finished yet")
|
||||||
|
|
||||||
|
if (this._testFinished())
|
||||||
|
throw IllegalStateException("Thread finished without uncaught exceptions")
|
||||||
|
else
|
||||||
|
throw IllegalStateException("Thread not finished yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override fun start(): Unit = this._guard.withUse {
|
||||||
|
if (this._isStarted) {
|
||||||
|
if (this._testFinished())
|
||||||
|
throw IllegalStateException("Thread already was started and finished")
|
||||||
|
else
|
||||||
|
throw IllegalStateException("Thread already was started")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0u > ResumeThread(this.thread._nativeThread)) WindowsApiException.throwFromLastWindowsErr()
|
||||||
|
this._isStarted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun join(): Unit = this._guard.withUse {
|
||||||
|
if (!this._isStarted)
|
||||||
|
throw IllegalStateException("Can't join on thread that isn't started")
|
||||||
|
|
||||||
|
|
||||||
|
waitloop@ while (true) {
|
||||||
|
when (WaitForSingleObject(this.thread._nativeThread, INFINITE)) {
|
||||||
|
WAIT_FAILED -> WindowsApiException.throwFromLastWindowsErr()
|
||||||
|
WAIT_TIMEOUT.toUInt() -> continue@waitloop
|
||||||
|
WAIT_OBJECT_0 -> break@waitloop
|
||||||
|
else -> throw RuntimeException("Unexpected return value of 'WaitForSingleObject'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destructor
|
||||||
|
public override fun releaseResources(): Unit = this._guard.withUseThenClose {
|
||||||
|
if (this._isStarted) {
|
||||||
|
if (!this._testFinished())
|
||||||
|
throw IllegalStateException("Can't destroy thread because it not finished yet")
|
||||||
|
}
|
||||||
|
zeroToWinApiErr { CloseHandle(this.thread._nativeThread) }
|
||||||
|
return@withUseThenClose true
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,259 @@
|
|||||||
|
package ru.landgrafhomyak.multitasking_0._tests
|
||||||
|
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlinx.cinterop.COpaquePointer
|
||||||
|
import kotlinx.cinterop.CPointer
|
||||||
|
import kotlinx.cinterop.ExperimentalForeignApi
|
||||||
|
import kotlinx.cinterop.alloc
|
||||||
|
import kotlinx.cinterop.invoke
|
||||||
|
import kotlinx.cinterop.memScoped
|
||||||
|
import kotlinx.cinterop.ptr
|
||||||
|
import kotlinx.cinterop.reinterpret
|
||||||
|
import kotlinx.cinterop.toCPointer
|
||||||
|
import kotlinx.cinterop.toLong
|
||||||
|
import platform.posix.printf
|
||||||
|
import platform.windows.CONDITION_VARIABLE
|
||||||
|
import platform.windows.CRITICAL_SECTION
|
||||||
|
import platform.windows.CloseHandle
|
||||||
|
import platform.windows.CreateEventA
|
||||||
|
import platform.windows.DeleteCriticalSection
|
||||||
|
import platform.windows.ERROR_INVALID_HANDLE
|
||||||
|
import platform.windows.EnterCriticalSection
|
||||||
|
import platform.windows.FALSE
|
||||||
|
import platform.windows.GetLastError
|
||||||
|
import platform.windows.GetModuleHandle
|
||||||
|
import platform.windows.GetModuleHandleA
|
||||||
|
import platform.windows.GetProcAddress
|
||||||
|
import platform.windows.HANDLE
|
||||||
|
import platform.windows.INFINITE
|
||||||
|
import platform.windows.InitializeConditionVariable
|
||||||
|
import platform.windows.InitializeCriticalSection
|
||||||
|
import platform.windows.LeaveCriticalSection
|
||||||
|
import platform.windows.SetEvent
|
||||||
|
import platform.windows.Sleep
|
||||||
|
import platform.windows.SleepConditionVariableCS
|
||||||
|
import platform.windows.TRUE
|
||||||
|
import platform.windows.WINBOOL
|
||||||
|
import platform.windows.WakeAllConditionVariable
|
||||||
|
import ru.landgrafhomyak.multitasking_0.HandleAndPointerBackgroundGarbageCollector
|
||||||
|
import ru.landgrafhomyak.multitasking_0._c.ru_landgrafhomyak_multitasking_0_duplicateHandle
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.WindowsApiException
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.nullToWinApiErr
|
||||||
|
import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.windows.zeroToWinApiErr
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
public class HandleAndPointerBackgroundGarbageCollectorTest {
|
||||||
|
private val CompareObjectHandles = GetProcAddress(
|
||||||
|
nullToWinApiErr { GetModuleHandleA("KernelBase.dll") },
|
||||||
|
"CompareObjectHandles"
|
||||||
|
).nullToWinApiErr().reinterpret<kotlinx.cinterop.CFunction<(HANDLE, HANDLE) -> WINBOOL>>()
|
||||||
|
|
||||||
|
|
||||||
|
private class TestableGarbageCollector(
|
||||||
|
private val _sync: CPointer<CRITICAL_SECTION>,
|
||||||
|
private val _notify: CPointer<CONDITION_VARIABLE>,
|
||||||
|
private val _dst: BooleanArray,
|
||||||
|
) : HandleAndPointerBackgroundGarbageCollector() {
|
||||||
|
override fun _destroyPtr(ptr: COpaquePointer) {
|
||||||
|
val i = ptr.toLong().toInt() - 1
|
||||||
|
EnterCriticalSection(this._sync)
|
||||||
|
this._dst[i] = true
|
||||||
|
WakeAllConditionVariable(this._notify)
|
||||||
|
LeaveCriticalSection(this._sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun _generateThreadName(): String = "abc"
|
||||||
|
|
||||||
|
public fun register(handle: HANDLE, wrapper: COpaquePointer) =
|
||||||
|
this._register(handle, wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
private fun _createEventsRec(countToAllocate: Int, scope: (Array<HANDLE>, Array<() -> Boolean>) -> Unit) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(scope, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
val marker = nullToWinApiErr { CreateEventA(null, FALSE, FALSE, null) }
|
||||||
|
safeAutoClose1(finally = { zeroToWinApiErr { CloseHandle(marker) } }) {
|
||||||
|
val event = nullToWinApiErr { ru_landgrafhomyak_multitasking_0_duplicateHandle(marker) }
|
||||||
|
safeAutoClose1(
|
||||||
|
action = {
|
||||||
|
val checkClosed = { CompareObjectHandles(marker, event) == FALSE }
|
||||||
|
if (countToAllocate > 1) {
|
||||||
|
this._createEventsRec(countToAllocate - 1) { arr, isc -> scope(arrayOf(event) + arr, arrayOf(checkClosed) + isc) }
|
||||||
|
} else {
|
||||||
|
scope(arrayOf(event), arrayOf(checkClosed))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
finally = {
|
||||||
|
if (CompareObjectHandles(marker, event) == TRUE) {
|
||||||
|
zeroToWinApiErr { CloseHandle(event) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
|
private fun _runTest(size: Int, body: (ctx: TestContext) -> Unit) {
|
||||||
|
val flags = BooleanArray(size) { false }
|
||||||
|
val isExpected = BooleanArray(flags.size) { false }
|
||||||
|
memScoped {
|
||||||
|
val cs = alloc<CRITICAL_SECTION>().ptr
|
||||||
|
val cv = alloc<CONDITION_VARIABLE>().ptr
|
||||||
|
InitializeCriticalSection(cs)
|
||||||
|
InitializeConditionVariable(cv)
|
||||||
|
safeAutoClose1(
|
||||||
|
action = {
|
||||||
|
this@HandleAndPointerBackgroundGarbageCollectorTest._createEventsRec(size) { events, isEventClosed ->
|
||||||
|
val gc = TestableGarbageCollector(cs, cv, flags)
|
||||||
|
safeAutoClose1(finally = { gc.destroy() }) {
|
||||||
|
body(TestContext(cs, cv, events, gc, isExpected))
|
||||||
|
EnterCriticalSection(cs)
|
||||||
|
var i = 0
|
||||||
|
for ((h, fe) in ((isEventClosed) zip (flags zip isExpected))) {
|
||||||
|
val (f, e) = fe
|
||||||
|
val c = h()
|
||||||
|
assertFalse(!e && f && c, "Resource $i not expected to be destructed, but both handle and wrapper are")
|
||||||
|
assertFalse(!e && f && !c, "Resource $i not expected to be destructed, but wrapper is")
|
||||||
|
assertFalse(!e && !f && c, "Resource $i not expected to be destructed, but handle is")
|
||||||
|
|
||||||
|
assertFalse(e && !f && !c, "Resource $i expected to be destructed, but both handle and wrapper are not")
|
||||||
|
assertFalse(e && f && !c, "Resource $i expected to be destructed, but handle isn't")
|
||||||
|
assertFalse(e && !f && c, "Resource $i expected to be destructed, but wrapper isn't")
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(cs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
finally = {
|
||||||
|
DeleteCriticalSection(cs)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestContext(
|
||||||
|
private val _sync: CPointer<CRITICAL_SECTION>,
|
||||||
|
private val _notify: CPointer<CONDITION_VARIABLE>,
|
||||||
|
private val _events: Array<HANDLE>,
|
||||||
|
private val _dst: TestableGarbageCollector,
|
||||||
|
private val _isExpected: BooleanArray,
|
||||||
|
) {
|
||||||
|
private val _isAdded = BooleanArray(this._events.size) { false }
|
||||||
|
|
||||||
|
fun add(i: Int) {
|
||||||
|
if (this._isAdded[i]) throw RuntimeException("Bad test: duplicated add($i)")
|
||||||
|
this._dst.register(this._events[i], (i + 1).toLong().toCPointer()!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release(i: Int) {
|
||||||
|
if (this._isExpected[i]) throw RuntimeException("Bad test: duplicated release($i)")
|
||||||
|
EnterCriticalSection(this._sync)
|
||||||
|
zeroToWinApiErr { SetEvent(this._events[i]) }
|
||||||
|
SleepConditionVariableCS(this._notify, this._sync, INFINITE)
|
||||||
|
this._isExpected[i] = true
|
||||||
|
LeaveCriticalSection(this._sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sleep(d: Duration) {
|
||||||
|
Sleep(d.inWholeMilliseconds.toUInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test empty`() = this._runTest(0) { ctx -> }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `_test events allocation 1`() = this._runTest(1) { ctx -> }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `_test events allocation 2`() = this._runTest(1) { ctx -> }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `_test events allocation 3`() = this._runTest(1) { ctx -> }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 1 alive`() = this._runTest(1) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 1 destruct instantly`() = this._runTest(1) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.release(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 1 destructed after delay`() = this._runTest(1) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.sleep(400.milliseconds)
|
||||||
|
ctx.release(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 2 destruct 1-st`() = this._runTest(2) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.add(1)
|
||||||
|
ctx.release(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 2 destruct 2-st`() = this._runTest(2) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.add(1)
|
||||||
|
ctx.release(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 2 destruct 12`() = this._runTest(2) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.add(1)
|
||||||
|
ctx.release(0)
|
||||||
|
ctx.release(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 2 destruct 21`() = this._runTest(2) { ctx ->
|
||||||
|
ctx.add(0)
|
||||||
|
ctx.add(1)
|
||||||
|
ctx.release(1)
|
||||||
|
ctx.release(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 80 alive`() = this._runTest(80) { ctx ->
|
||||||
|
(0..<80).forEach { i -> ctx.add(i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 80 shitting`() = this._runTest(80) { ctx ->
|
||||||
|
(0..<80).forEach { i -> ctx.add(i) }
|
||||||
|
(0..<20).forEach { i -> ctx.release(i) }
|
||||||
|
ctx.release(79)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test 80 destruct all`() = this._runTest(80) { ctx ->
|
||||||
|
(0..<80).forEach { i -> ctx.add(i) }
|
||||||
|
(0..<80).forEach { i -> ctx.release(i) }
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `test 80 destruct all reversed`() = this._runTest(80) { ctx ->
|
||||||
|
(0..<80).forEach { i -> ctx.add(i) }
|
||||||
|
(79 downTo 0).forEach { i -> ctx.release(i) }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user