From d0c557e872de0f26311570e94685fea9c4ba42a5 Mon Sep 17 00:00:00 2001 From: Jorge Antonio Diaz-Benito Soriano Date: Tue, 23 Jan 2024 21:45:02 +0100 Subject: [PATCH] Implement UDP exchange in Kotlin/Native --- .../networktime/internal/HostNameResolver.kt | 1 + .../internal/NTPUDPSocketOperations.kt | 112 ++++++------------ .../networktime/internal/NTPExchanger.kt | 4 +- .../internal/NTPUDPSocketOperations.kt | 4 +- .../internal/NTPUDPSocketOperations.kt | 4 +- .../UserInterfaceState.xcuserstate | Bin 30768 -> 33812 bytes 6 files changed, 42 insertions(+), 83 deletions(-) diff --git a/library/src/appleMain/kotlin/com/tidal/networktime/internal/HostNameResolver.kt b/library/src/appleMain/kotlin/com/tidal/networktime/internal/HostNameResolver.kt index 6951eb24..6e686371 100644 --- a/library/src/appleMain/kotlin/com/tidal/networktime/internal/HostNameResolver.kt +++ b/library/src/appleMain/kotlin/com/tidal/networktime/internal/HostNameResolver.kt @@ -130,6 +130,7 @@ internal actual class HostNameResolver { } private fun clear() { + cfHost?.let { CFRelease(it) } cfHost = null hostReference?.let { CFRelease(it) } hostReference = null diff --git a/library/src/appleMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt b/library/src/appleMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt index ce2f74b8..02a05715 100644 --- a/library/src/appleMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt +++ b/library/src/appleMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt @@ -2,36 +2,22 @@ package com.tidal.networktime.internal import com.tidal.networktime.ProtocolFamily import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.StableRef import kotlinx.cinterop.alloc -import kotlinx.cinterop.asStableRef import kotlinx.cinterop.memScoped -import kotlinx.cinterop.pointed import kotlinx.cinterop.ptr import kotlinx.cinterop.refTo -import kotlinx.cinterop.reinterpret import kotlinx.cinterop.sizeOf -import kotlinx.cinterop.staticCFunction import kotlinx.cinterop.toCPointer import kotlinx.cinterop.toLong -import kotlinx.cinterop.value -import kotlinx.coroutines.delay -import kotlinx.coroutines.withTimeout import platform.CoreFoundation.CFDataCreate -import platform.CoreFoundation.CFDataGetBytes -import platform.CoreFoundation.CFDataGetLength -import platform.CoreFoundation.CFDataRefVar -import platform.CoreFoundation.CFRangeMake -import platform.CoreFoundation.CFSocketCallBack +import platform.CoreFoundation.CFRelease import platform.CoreFoundation.CFSocketConnectToAddress -import platform.CoreFoundation.CFSocketContext import platform.CoreFoundation.CFSocketCreate import platform.CoreFoundation.CFSocketInvalidate import platform.CoreFoundation.CFSocketRef import platform.CoreFoundation.CFSocketSendData import platform.CoreFoundation.kCFAllocatorDefault import platform.CoreFoundation.kCFSocketDataCallBack -import platform.darwin.inet_aton import platform.darwin.inet_pton import platform.posix.AF_INET import platform.posix.AF_INET6 @@ -45,63 +31,39 @@ import platform.posix.signal import platform.posix.sockaddr_in import platform.posix.sockaddr_in6 import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds import kotlin.time.DurationUnit @OptIn(ExperimentalForeignApi::class) @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") internal actual class NTPUDPSocketOperations { private var cfSocket: CFSocketRef? = null - private var userDataRef: StableRef? = null - actual suspend fun prepareSocket( + actual suspend fun prepare( address: String, protocolFamily: ProtocolFamily, portNumber: Int, connectTimeout: Duration, ) { - userDataRef = StableRef.create(UserData()) - val callback: CFSocketCallBack = staticCFunction { _, callbackType, _, data, info -> - val userData = info!!.asStableRef().get() - if (callbackType != kCFSocketDataCallBack) { - return@staticCFunction - } - val reinterpretedData = data!!.reinterpret().pointed.value - val length = CFDataGetLength(reinterpretedData) - val range = CFRangeMake(0, length) - val bridgeBuffer = UByteArray(length.toInt()) - CFDataGetBytes(reinterpretedData, range, bridgeBuffer.refTo(0)) - userData.apply { - buffer = bridgeBuffer.toByteArray() - exchangeCompleted = true - } - } - signal(SIGPIPE, SIG_IGN) - cfSocket = memScoped { - val socketContext = alloc { - version = 0 - info = userDataRef!!.asCPointer() - retain = null - release = null - copyDescription = null - } - val socket = CFSocketCreate( - kCFAllocatorDefault, - when (protocolFamily) { - ProtocolFamily.INET -> PF_INET - ProtocolFamily.INET6 -> PF_INET6 - }, - SOCK_DGRAM, - IPPROTO_UDP, - kCFSocketDataCallBack, - callback, - socketContext.ptr, - ) - val addrCFDataRef = when (protocolFamily) { + val address = "127.0.0.1" + val portNumber = 31337 + cfSocket = CFSocketCreate( + kCFAllocatorDefault, + when (protocolFamily) { + ProtocolFamily.INET -> PF_INET + ProtocolFamily.INET6 -> PF_INET6 + }, + SOCK_DGRAM, + IPPROTO_UDP, + kCFSocketDataCallBack, + null, + null, + ) + val addrCfDataRef = memScoped { + when (protocolFamily) { ProtocolFamily.INET -> alloc { sin_family = AF_INET.toUByte() sin_port = portNumber.toUShort() - inet_aton(address, sin_addr.ptr) + inet_pton(AF_INET, address, sin_addr.ptr) }.run { CFDataCreate(kCFAllocatorDefault, ptr.toLong().toCPointer(), sizeOf()) } @@ -116,16 +78,16 @@ internal actual class NTPUDPSocketOperations { } } } - CFSocketConnectToAddress( - socket, - addrCFDataRef, - connectTimeout.toDouble(DurationUnit.MILLISECONDS), - ) - socket } + CFSocketConnectToAddress( + cfSocket, + addrCfDataRef, + connectTimeout.toDouble(DurationUnit.SECONDS) + ) } - actual suspend fun exchangeInPlace(buffer: ByteArray, readTimeout: Duration) { + actual suspend fun exchange(buffer: ByteArray, readTimeout: Duration) { + signal(SIGPIPE, SIG_IGN) val bufferCFDataRef = CFDataCreate( kCFAllocatorDefault, buffer.asUByteArray().refTo(0), @@ -137,22 +99,18 @@ internal actual class NTPUDPSocketOperations { bufferCFDataRef, Duration.INFINITE.toDouble(DurationUnit.MILLISECONDS), ) - withTimeout(readTimeout) { - while (!userDataRef!!.get().exchangeCompleted) { - delay(1.seconds) - } - } + /* val reinterpretedData = data!!.reinterpret().pointed.value + val length = CFDataGetLength(reinterpretedData) + val range = CFRangeMake(0, length) + val bridgeBuffer = UByteArray(length.toInt()) + CFDataGetBytes(reinterpretedData, range, bridgeBuffer.refTo(0)) */ } actual fun closeSocket() { - CFSocketInvalidate(cfSocket) + cfSocket?.let { + CFSocketInvalidate(it) + CFRelease(it) + } cfSocket = null - userDataRef?.dispose() - userDataRef = null - } - - private class UserData { - var exchangeCompleted = false - var buffer: ByteArray = byteArrayOf() } } diff --git a/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPExchanger.kt b/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPExchanger.kt index f37940be..482d747c 100644 --- a/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPExchanger.kt +++ b/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPExchanger.kt @@ -17,12 +17,12 @@ internal class NTPExchanger( ): NTPExchangeResult? { val ntpUdpSocketOperations = NTPUDPSocketOperations() return try { - ntpUdpSocketOperations.prepareSocket(address, protocolFamily, NTP_PORT_NUMBER, connectTimeout) + ntpUdpSocketOperations.prepare(address, protocolFamily, NTP_PORT_NUMBER, connectTimeout) val ntpPacket = NTPPacket(versionNumber = ntpVersion.toInt(), mode = NTP_MODE_CLIENT) val requestTime = referenceClock.referenceEpochTime ntpPacket.transmitEpochTimestamp = EpochTimestamp(requestTime).asNTPTimestamp val buffer = ntpPacketSerializer(ntpPacket) - ntpUdpSocketOperations.exchangeInPlace(buffer, queryReadTimeout) + ntpUdpSocketOperations.exchange(buffer, queryReadTimeout) val returnTime = referenceClock.referenceEpochTime ntpPacketDeserializer(buffer)?.let { NTPExchangeResult(returnTime, it) } } catch (_: Throwable) { diff --git a/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt b/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt index d31e8945..797b7ab0 100644 --- a/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt +++ b/library/src/commonMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt @@ -5,14 +5,14 @@ import kotlin.time.Duration @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") internal expect class NTPUDPSocketOperations() { - suspend fun prepareSocket( + suspend fun prepare( address: String, protocolFamily: ProtocolFamily, portNumber: Int, connectTimeout: Duration, ) - suspend fun exchangeInPlace(buffer: ByteArray, readTimeout: Duration) + suspend fun exchange(buffer: ByteArray, readTimeout: Duration) fun closeSocket() } diff --git a/library/src/jvmMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt b/library/src/jvmMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt index 9dfcdcfe..387b3927 100644 --- a/library/src/jvmMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt +++ b/library/src/jvmMain/kotlin/com/tidal/networktime/internal/NTPUDPSocketOperations.kt @@ -14,7 +14,7 @@ import kotlin.time.DurationUnit internal actual class NTPUDPSocketOperations { private lateinit var datagramSocket: DatagramSocket - actual suspend fun prepareSocket( + actual suspend fun prepare( address: String, protocolFamily: ProtocolFamily, portNumber: Int, @@ -24,7 +24,7 @@ internal actual class NTPUDPSocketOperations { datagramSocket.connect(InetAddress.getByName(address), portNumber) } - actual suspend fun exchangeInPlace(buffer: ByteArray, readTimeout: Duration) { + actual suspend fun exchange(buffer: ByteArray, readTimeout: Duration) { val exchangePacket = DatagramPacket(buffer, buffer.size) withContext(Dispatchers.IO) { datagramSocket.send(exchangePacket) diff --git a/samples/multiplatform-kotlin/iOS.xcodeproj/project.xcworkspace/xcuserdata/jantonio.xcuserdatad/UserInterfaceState.xcuserstate b/samples/multiplatform-kotlin/iOS.xcodeproj/project.xcworkspace/xcuserdata/jantonio.xcuserdatad/UserInterfaceState.xcuserstate index 1d9413c7cf0df7fdba14e2e3e274bc5c3caf8827..178ec82d37a7f0748621d7f7f4024fc561e7f742 100644 GIT binary patch delta 17926 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 Z%?qb~t(TM#wp{iQ{A>N+w%`4z{{w#TSn~h? delta 16433 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 DA5ztK