Extracting 'safeAutoClose*' functions family to a separate library

This commit is contained in:
Andrew Golovashevich 2025-03-22 14:59:34 +03:00
parent 834d12c17b
commit 9538888b77
14 changed files with 38 additions and 130 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "libs/int-serializers"]
path = libs/int-serializers
url = https://git.landgrafhomyak.ru/xomrk/int-serializers.kt
[submodule "libs/highlevel-try-finally"]
path = libs/highlevel-try-finally
url = https://git.landgrafhomyak.ru/xomrk/highlevel-try-finally.kt

@ -0,0 +1 @@
Subproject commit ef1d72ca10c361d2eead443d99241a088e130174

View File

@ -8,9 +8,9 @@ import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import ru.landgrafhomyak.bgtu.networks0.low_level.sockets.IcmpSocket
import ru.landgrafhomyak.bgtu.networks0.utilities.compareAndExchange
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.IntSerializers
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ExperimentalUnsignedTypes::class)
@ -24,7 +24,7 @@ class Pinger1 : AutoCloseable {
internal constructor(rawSocket: IcmpSocket) {
safeAutoClose2(onAbort = rawSocket::close) {
safeAutoClose2(onError = rawSocket::close) {
this._state = atomic(State.READY)
this._rawSocket = rawSocket
}

View File

@ -1,8 +1,8 @@
package ru.landgrafhomyak.bgtu.networks0.low_level.multithreading
import ru.landgrafhomyak.bgtu.networks0.utilities.CloseableRefCounter
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
class CountDownLatch : AutoCloseable {
private val _refcnt: CloseableRefCounter
@ -14,7 +14,7 @@ class CountDownLatch : AutoCloseable {
this._refcnt = CloseableRefCounter("Latch was destroyed")
this._counter = initialCounterValue
this._mutex = Mutex()
safeAutoClose2(onAbort = this._mutex::close) {
safeAutoClose2(onError = this._mutex::close) {
this._condition = Condition()
}
}

View File

@ -3,7 +3,7 @@ package ru.landgrafhomyak.bgtu.networks0.low_level.multithreading
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
@OptIn(ExperimentalContracts::class)
inline fun <R> Mutex.withLock(synchronizedBlock: () -> R): R {

View File

@ -14,8 +14,8 @@ 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.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ExperimentalForeignApi::class)
@ -26,7 +26,7 @@ actual class Condition : AutoCloseable {
actual constructor() {
this._refcnt = CloseableRefCounter("Pthreads condition was destroyed")
this._descriptor = nativeHeap.alloc<pthread_cond_t>().ptr
safeAutoClose2(onAbort = { nativeHeap.free(this._descriptor) }) {
safeAutoClose2(onError = { nativeHeap.free(this._descriptor) }) {
var err = pthread_cond_init(this._descriptor, null)
if (err != 0)
PosixUtilities.throwErrno { d -> RuntimeException("Failed to initialize pthreads condition: $d") }

View File

@ -13,8 +13,8 @@ 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.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@OptIn(ExperimentalForeignApi::class, Mutex.RefcntAccess::class)
actual class Mutex : AutoCloseable {
@ -28,7 +28,7 @@ actual class Mutex : AutoCloseable {
actual constructor() {
this._refcnt = CloseableRefCounter("Pthreads mutex was destroyed")
this._descriptor = nativeHeap.alloc<pthread_mutex_t>().ptr
safeAutoClose2(onAbort = { nativeHeap.free(this._descriptor) }) {
safeAutoClose2(onError = { nativeHeap.free(this._descriptor) }) {
var err = pthread_mutex_init(this._descriptor, null)
if (err != 0)
PosixUtilities.throwErrno(err) { d -> RuntimeException("Failed to create pthreads mutex: $d") }

View File

@ -9,9 +9,9 @@ import kotlinx.cinterop.StableRef
import kotlinx.cinterop.asStableRef
import kotlinx.cinterop.staticCFunction
import ru.landgrafhomyak.bgtu.networks0.low_level.c_interop_utilities.PosixUtilities
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2e
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2e
import ru.landgrafhomyak.bgtu.networks0.utilities.compareAndExchange
@OptIn(ExperimentalForeignApi::class)
@ -38,9 +38,9 @@ actual class Thread : AutoCloseable {
actual constructor(routine: Routine) {
this._state = atomic(State.PENDING)
this._bootstrapArgRef = StableRef.create(ThreadBootstrapContext(routine))
safeAutoClose2(onAbort = this._bootstrapArgRef::dispose) {
safeAutoClose2(onError = this._bootstrapArgRef::dispose) {
this._threadBindings = _PthreadsThreadBindings()
safeAutoClose2(onAbort = this._threadBindings::free) {
safeAutoClose2(onError = this._threadBindings::free) {
var err = this._threadBindings.create(
null,
staticCFunction({ arg -> return@staticCFunction Thread._threadBootstrap(arg) }),
@ -60,7 +60,7 @@ actual class Thread : AutoCloseable {
State.STARTING, State.RUNNING -> throw IllegalStateException("Pthreads thread already running")
State.PENDING -> {}
}
safeAutoClose2(onAbort = { this._state.value = State.PENDING }) {
safeAutoClose2(onError = { this._state.value = State.PENDING }) {
this._bootstrapArgRef.get().startSignal.decrement()
}
}
@ -73,13 +73,13 @@ actual class Thread : AutoCloseable {
}
var err = this._threadBindings.join(null)
if (err != 0)
PosixUtilities.throwErrno(err) { d -> RuntimeException("Failed to create pthreads thread") }
PosixUtilities.throwErrno(err) { d -> RuntimeException("Failed to create pthreads thread: $d") }
return this._bootstrapArgRef.get().exitedWithError
}
override fun close() {
this._state.update { o ->
when (this._state.value) {
when (o) {
State.CLOSED -> throw IllegalStateException("Pthreads thread is destroyed")
State.STARTING, State.RUNNING -> throw IllegalStateException("Can't destroy pthreads thread while it running")
State.FINISHED, State.PENDING -> {}
@ -113,7 +113,7 @@ actual class Thread : AutoCloseable {
}
try {
safeAutoClose2e(
onAbort = { e ->
onError = { e ->
context.exitedWithError = e
context.threadState.value = State.FINISHED
},

View File

@ -30,8 +30,8 @@ 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.bgtu.networks0.utilities.safeAutoClose1
import ru.landgrafhomyak.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
import ru.landgrafhomyak.bgtu.networks0.low_level.multithreading.Mutex as TMutex
@OptIn(ExperimentalForeignApi::class)
@ -187,7 +187,7 @@ class EpollSocketEventLoop : SocketEventLoopScope.Closeable {
this.read = null
this.write = null
this.stableRef = StableRef.create(this)
safeAutoClose2(onAbort = this.stableRef::dispose) {
safeAutoClose2(onError = this.stableRef::dispose) {
this.sync = TMutex()
}
}
@ -205,13 +205,13 @@ class EpollSocketEventLoop : SocketEventLoopScope.Closeable {
private val __metadata: _SocketMetadata
constructor(ipv4: CValue<sockaddr_in>) : super(ipv4) {
safeAutoClose2(onAbort = { super.close() }) {
safeAutoClose2(onError = { super.close() }) {
this.__metadata = _SocketMetadata(this._socketFd)
}
}
constructor(ipv6: CValue<sockaddr_in6>) : super(ipv6) {
safeAutoClose2(onAbort = { super.close() }) {
safeAutoClose2(onError = { super.close() }) {
this@EpollSocketEventLoop._socketsCount.tryIncref {
this.__metadata = _SocketMetadata(this._socketFd)
}

View File

@ -35,7 +35,7 @@ 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.bgtu.networks0.utilities.safeAutoClose2
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose2
@Suppress("CanBeVal")
@OptIn(ExperimentalForeignApi::class)
@ -82,7 +82,7 @@ internal abstract class NonblockingIcmpSocketImpl : IcmpSocket {
else
PosixUtilities.throwErrno { d -> RuntimeException("Failed to create ICMP socket over ${protoName}: $d") }
}
safeAutoClose2(onAbort = { if (0 != close_lowlevel(this._socketFd)) PosixUtilities.throwErrno { d -> RuntimeException("Failed to close ICMP socket: $d") } }) {
safeAutoClose2(onError = { if (0 != close_lowlevel(this._socketFd)) PosixUtilities.throwErrno { d -> RuntimeException("Failed to close ICMP socket: $d") } }) {
var err = memScoped {
@Suppress("UNCHECKED_CAST")
return@memScoped connect(sock, addrValue as CValue<sockaddr>, addrSize)

View File

@ -22,6 +22,7 @@ kotlin {
commonMain {
dependencies {
implementation(Dependencies.kotlin_atomicfu)
api("ru.landgrafhomyak.utility:highlevel-try-finally:0.4")
}
}
}

View File

@ -8,6 +8,8 @@ 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)
@ -28,7 +30,7 @@ class CloseableRefCounter(private val _errMessage: String) {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
this.incref()
return safeAutoClose2(onAbort = this::decref, action = block)
return safeAutoClose2(onError = this::decref, action = block)
}
fun checkNotClosed() {

View File

@ -1,100 +0,0 @@
@file:OptIn(ExperimentalContracts::class)
package ru.landgrafhomyak.bgtu.networks0.utilities
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@Suppress("WRONG_INVOCATION_KIND")
inline fun <R> safeAutoClose1(
finally: () -> Unit,
action: () -> R
): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
callsInPlace(finally, InvocationKind.EXACTLY_ONCE)
}
return safeAutoClose3(onAbort = finally, onSuccess = finally, onCrossReturn = finally, action = action)
}
@Suppress("WRONG_INVOCATION_KIND")
inline fun <R> safeAutoClose2(
onAbort: () -> Unit = {},
onSuccess: () -> Unit = {},
action: () -> R
): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
callsInPlace(onAbort, InvocationKind.AT_MOST_ONCE)
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
}
return safeAutoClose3(onAbort = onAbort, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action)
}
@Suppress("WRONG_INVOCATION_KIND")
inline fun <R> safeAutoClose2e(
onAbort: (Throwable) -> Unit = { _ -> },
onSuccess: () -> Unit = {},
action: () -> R
): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
callsInPlace(onAbort, InvocationKind.AT_MOST_ONCE)
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
}
return safeAutoClose3e(onAbort = onAbort, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action)
}
inline fun <R> safeAutoClose3(
onAbort: () -> Unit = {},
onSuccess: () -> Unit = {},
onCrossReturn: () -> Unit = {},
action: () -> R
): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
callsInPlace(onAbort, InvocationKind.AT_MOST_ONCE)
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE)
}
return safeAutoClose3e(onAbort = { t -> onAbort() }, onSuccess = onSuccess, onCrossReturn = onCrossReturn, action = action)
}
inline fun <R> safeAutoClose3e(
onAbort: (Throwable) -> Unit = { _ -> },
onSuccess: () -> Unit = {},
onCrossReturn: () -> Unit = {},
action: () -> R
): R {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
callsInPlace(onAbort, InvocationKind.AT_MOST_ONCE)
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE)
}
val ret: R
var wasError = false
var crossReturned = true
try {
ret = action()
crossReturned = false
} catch (e1: Throwable) {
wasError = true
try {
onAbort(e1)
} catch (e2: Throwable) {
e1.addSuppressed(e2)
}
throw e1
} finally {
if (!wasError) {
if (crossReturned)
onCrossReturn()
else
onSuccess()
}
}
return ret
}

View File

@ -24,6 +24,7 @@ pluginManagement {
includeBuild("./libs/int-serializers")
includeBuild("./libs/highlevel-try-finally")
include(":modules:utilities")
include(":modules:low-level:c-interop-utilities")
include(":modules:low-level:multithreading")