From af2f50a3b864739ec26bed3c17e68169c05901ad Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Sat, 22 Mar 2025 21:07:49 +0300 Subject: [PATCH] Extracted RefCounter to a separate library and fixed bug with refcnt in epoll event-loop --- .gitmodules | 3 + libs/reference-counter | 1 + .../multithreading/CountDownLatch.kt | 7 +- .../low_level/multithreading/Condition.kt | 6 +- .../low_level/multithreading/Mutex.kt | 7 +- .../low_level/sockets/EpollSocketEventLoop.kt | 10 +-- .../sockets/NonblockingIcmpSocketImpl.kt | 8 +-- modules/utilities/build.gradle.kts | 1 + .../utilities/CloseableRefCounter.kt | 72 ------------------- settings.gradle.kts | 1 + 10 files changed, 25 insertions(+), 91 deletions(-) create mode 160000 libs/reference-counter delete mode 100644 modules/utilities/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/utilities/CloseableRefCounter.kt diff --git a/.gitmodules b/.gitmodules index 9a0d92f..4a987a2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "libs/highlevel-try-finally"] path = libs/highlevel-try-finally url = https://git.landgrafhomyak.ru/xomrk/highlevel-try-finally.kt +[submodule "libs/reference-counter"] + path = libs/reference-counter + url = https://git.landgrafhomyak.ru/xomrk/reference-counter.kt diff --git a/libs/reference-counter b/libs/reference-counter new file mode 160000 index 0000000..0e2aea0 --- /dev/null +++ b/libs/reference-counter @@ -0,0 +1 @@ +Subproject commit 0e2aea0bff433b3917f89e2b61ab5482154cec16 diff --git a/modules/low-level/multithreading/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/CountDownLatch.kt b/modules/low-level/multithreading/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/CountDownLatch.kt index 4120750..543a2b3 100644 --- a/modules/low-level/multithreading/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/CountDownLatch.kt +++ b/modules/low-level/multithreading/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/CountDownLatch.kt @@ -1,18 +1,17 @@ package ru.landgrafhomyak.bgtu.networks0.low_level.multithreading -import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter -import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 import ru.landgrafhomyak.utility.highlevel_try_finally.tryFinallyChain +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter class CountDownLatch : AutoCloseable { - private val _refcnt: CloseableRefCounter + private val _refcnt: CloseableReferenceCounter private var _counter: ULong private val _mutex: Mutex private val _condition: Condition constructor(initialCounterValue: ULong) { - this._refcnt = CloseableRefCounter("Latch was destroyed") + this._refcnt = CloseableReferenceCounter("Latch was destroyed") this._counter = initialCounterValue this._mutex = Mutex() safeAutoClose2(onError = this._mutex::close) { diff --git a/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Condition.kt b/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Condition.kt index ac0001c..82348e7 100644 --- a/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Condition.kt +++ b/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Condition.kt @@ -13,19 +13,19 @@ import platform.posix.pthread_cond_wait import platform.posix.pthread_cond_broadcast import platform.posix.pthread_cond_signal import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.PosixUtilities -import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 import ru.landgrafhomyak.utility.highlevel_try_finally.tryFinallyChain +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter @OptIn(ExperimentalForeignApi::class) actual class Condition : AutoCloseable { - private val _refcnt: CloseableRefCounter + private val _refcnt: CloseableReferenceCounter val _descriptor: CPointer actual constructor() { - this._refcnt = CloseableRefCounter("Pthreads condition was destroyed") + this._refcnt = CloseableReferenceCounter("Pthreads condition was destroyed") this._descriptor = nativeHeap.alloc().ptr safeAutoClose2(onError = { nativeHeap.free(this._descriptor) }) { var err = pthread_cond_init(this._descriptor, null) diff --git a/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Mutex.kt b/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Mutex.kt index f61b84d..b335659 100644 --- a/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Mutex.kt +++ b/modules/low-level/multithreading/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/multithreading/Mutex.kt @@ -12,10 +12,9 @@ import platform.posix.pthread_mutex_lock import platform.posix.pthread_mutex_t import platform.posix.pthread_mutex_unlock import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.PosixUtilities -import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter -import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 import ru.landgrafhomyak.utility.highlevel_try_finally.tryFinallyChain +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter @OptIn(ExperimentalForeignApi::class, Mutex.RefcntAccess::class) actual class Mutex : AutoCloseable { @@ -23,11 +22,11 @@ actual class Mutex : AutoCloseable { internal annotation class RefcntAccess @RefcntAccess - internal val _refcnt: CloseableRefCounter + internal val _refcnt: CloseableReferenceCounter internal val _descriptor: CPointer actual constructor() { - this._refcnt = CloseableRefCounter("Pthreads mutex was destroyed") + this._refcnt = CloseableReferenceCounter("Pthreads mutex was destroyed") this._descriptor = nativeHeap.alloc().ptr safeAutoClose2(onError = { nativeHeap.free(this._descriptor) }) { var err = pthread_mutex_init(this._descriptor, null) diff --git a/modules/low-level/sockets/src/linuxMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/EpollSocketEventLoop.kt b/modules/low-level/sockets/src/linuxMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/EpollSocketEventLoop.kt index a3a4735..a7a732e 100644 --- a/modules/low-level/sockets/src/linuxMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/EpollSocketEventLoop.kt +++ b/modules/low-level/sockets/src/linuxMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/EpollSocketEventLoop.kt @@ -29,20 +29,20 @@ import platform.posix.sockaddr_in import platform.posix.sockaddr_in6 import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.PosixUtilities import ru.landgrafhomyak.bgtu.networks0.low_level.multithreading.withLock -import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 import ru.landgrafhomyak.utility.highlevel_try_finally.tryFinallyChain +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter import ru.landgrafhomyak.bgtu.networks0.low_level.multithreading.Mutex as TMutex @OptIn(ExperimentalForeignApi::class) @Suppress("JoinDeclarationAndAssignment", "ConvertSecondaryConstructorToPrimary", "FunctionName") class EpollSocketEventLoop : SocketEventLoopScope.Closeable { private val _epollDescriptor: Int - private val _socketsCount: CloseableRefCounter + private val _socketsCount: CloseableReferenceCounter constructor() { - this._socketsCount = CloseableRefCounter("Epoll event-loop is closed") + this._socketsCount = CloseableReferenceCounter("Epoll event-loop is closed") this._epollDescriptor = epoll_create(1) if (this._epollDescriptor < 0) PosixUtilities.throwErrno { d -> RuntimeException("Failed to create epoll descriptor: $d") } @@ -207,7 +207,9 @@ class EpollSocketEventLoop : SocketEventLoopScope.Closeable { constructor(ipv4: CValue) : super(ipv4) { safeAutoClose2(onError = { super.close() }) { - this.__metadata = _SocketMetadata(this._socketFd) + this@EpollSocketEventLoop._socketsCount.tryIncref { + this.__metadata = _SocketMetadata(this._socketFd) + } } } diff --git a/modules/low-level/sockets/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/NonblockingIcmpSocketImpl.kt b/modules/low-level/sockets/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/NonblockingIcmpSocketImpl.kt index 45569fe..841de61 100644 --- a/modules/low-level/sockets/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/NonblockingIcmpSocketImpl.kt +++ b/modules/low-level/sockets/src/posixMain/kotlin/ru/landgrafhomyak/bgtu/networks0/low_level/sockets/NonblockingIcmpSocketImpl.kt @@ -34,8 +34,8 @@ import platform.posix.recv as recv_lowlevel import platform.posix.close as close_lowlevel import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.CUtilities import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.PosixUtilities -import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter @Suppress("CanBeVal") @OptIn(ExperimentalForeignApi::class) @@ -43,7 +43,7 @@ internal abstract class NonblockingIcmpSocketImpl : IcmpSocket { protected val _socketFd: Int private val __rSync: CMutex private val __wSync: CMutex - private val __refcnt: CloseableRefCounter + private val __refcnt: CloseableReferenceCounter private val __parentProtocol: ParentProtocol private enum class ParentProtocol { @@ -61,7 +61,7 @@ internal abstract class NonblockingIcmpSocketImpl : IcmpSocket { this.__rSync = CMutex() this.__wSync = CMutex() this.__parentProtocol = ParentProtocol.IPv4 - this.__refcnt = CloseableRefCounter("ICMP socket was closed") + this.__refcnt = CloseableReferenceCounter("ICMP socket was closed") this._socketFd = this.__createSocket(AF_INET, IPPROTO_ICMP, ipv4, CUtilities.sizeOfUI(), "IPv4") } @@ -69,7 +69,7 @@ internal abstract class NonblockingIcmpSocketImpl : IcmpSocket { this.__rSync = CMutex() this.__wSync = CMutex() this.__parentProtocol = ParentProtocol.IPv6 - this.__refcnt = CloseableRefCounter("ICMP socket was closed") + this.__refcnt = CloseableReferenceCounter("ICMP socket was closed") this._socketFd = this.__createSocket(AF_INET6, IPPROTO_ICMPV6, ipv6, CUtilities.sizeOfUI(), "IPv6") } diff --git a/modules/utilities/build.gradle.kts b/modules/utilities/build.gradle.kts index e96344a..fc3945d 100644 --- a/modules/utilities/build.gradle.kts +++ b/modules/utilities/build.gradle.kts @@ -23,6 +23,7 @@ kotlin { dependencies { implementation(Dependencies.kotlin_atomicfu) api("ru.landgrafhomyak.utility:highlevel-try-finally:0.4") + api("ru.landgrafhomyak.utility:reference-counter:0.1") } } } diff --git a/modules/utilities/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/utilities/CloseableRefCounter.kt b/modules/utilities/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/utilities/CloseableRefCounter.kt deleted file mode 100644 index 7cdb08a..0000000 --- a/modules/utilities/src/commonMain/kotlin/ru/landgrafhomyak/bgtu/networks0/utilities/CloseableRefCounter.kt +++ /dev/null @@ -1,72 +0,0 @@ -@file:OptIn(ExperimentalContracts::class) - -package ru.landgrafhomyak.bgtu.networks0.utilities - -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlinx.atomicfu.AtomicLong -import kotlinx.atomicfu.atomic -import kotlinx.atomicfu.update -import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 -import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2 - -class CloseableRefCounter(private val _errMessage: String) { - private val _value: AtomicLong = atomic(0L) - - fun throwClosed() { - throw IllegalStateException(this._errMessage) - } - - fun incref() { - this._value.update { o -> - if (o < 0) this.throwClosed() - return@update o + 1 - } - } - - inline fun tryIncref(block: () -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - this.incref() - return safeAutoClose2(onError = this::decref, action = block) - } - - fun checkNotClosed() { - if (this._value.value < 0) this.throwClosed() - } - - fun decref() { - this._value.update(Long::dec) - } - - inline fun tryDecref(block: () -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - this.checkNotClosed() - return safeAutoClose2(onSuccess = this::decref, action = block) - } - - fun close(errExistRefs: String) { - val state = this._value.compareAndExchange(0, -1) - when { - state > 0 -> throw IllegalStateException(errExistRefs) - state < 0 -> this.throwClosed() - } - } - - inline fun withRef(protected: () -> R): R { - this.incref() - return safeAutoClose1(finally = this::decref, action = protected) - } - - override fun toString(): String { - val refcntCached = this._value.value - if (refcntCached < 0) - return "" - else - return "" - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index a952c27..c430115 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,6 +25,7 @@ pluginManagement { includeBuild("./libs/int-serializers") includeBuild("./libs/highlevel-try-finally") +includeBuild("./libs/reference-counter") include(":modules:utilities") include(":modules:low-level:c-interop-utilities") include(":modules:low-level:multithreading")