Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Work #52

Merged
merged 3 commits into from
Mar 29, 2024
Merged

Work #52

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ android.useAndroidX=true
org.gradle.jvmargs=-Xmx16g
org.jetbrains.compose.experimental.uikit.enabled=true
org.jetbrains.compose.experimental.macos.enabled=true
kotlin.mpp.enableCInteropCommonization=true
2 changes: 2 additions & 0 deletions library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ kotlin {
macosArm64(),
iosX64(),
iosArm64(),
iosSimulatorArm64(),
tvosSimulatorArm64(),
tvosX64(),
tvosArm64(),
Expand All @@ -21,6 +22,7 @@ kotlin {
binaryOption("bundleId", "com.tidal.networktime")
isStatic = true
}
it.compilations.configureEach { cinterops.create("NetworkFrameworkWorkaround") }
}

applyDefaultHierarchyTemplate()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
package com.tidal.networktime.internal

import kotlinx.cinterop.ByteVar
import com.tidal.networktime.internal.network_framework_workaround.nw_connection_send_with_default_context
import com.tidal.networktime.internal.network_framework_workaround.nw_parameters_create_secure_udp_workaround
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.addressOf
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.refTo
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.set
import kotlinx.cinterop.pin
import kotlinx.cinterop.usePinned
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.withTimeout
import platform.Network.NW_CONNECTION_FINAL_MESSAGE_CONTEXT
import platform.Network.NW_PARAMETERS_DEFAULT_CONFIGURATION
import platform.Network.NW_PARAMETERS_DISABLE_PROTOCOL
import platform.Network.nw_connection_create
import platform.Network.nw_connection_force_cancel
import platform.Network.nw_connection_receive
import platform.Network.nw_connection_send
import platform.Network.nw_connection_set_queue
import platform.Network.nw_connection_set_state_changed_handler
import platform.Network.nw_connection_start
Expand All @@ -27,8 +22,6 @@ import platform.Network.nw_connection_state_t
import platform.Network.nw_connection_t
import platform.Network.nw_endpoint_create_host
import platform.Network.nw_error_t
import platform.Network.nw_parameters_create_secure_udp
import platform.darwin._dispatch_data_destructor_free
import platform.darwin.dispatch_data_apply
import platform.darwin.dispatch_data_create
import platform.darwin.dispatch_data_t
Expand All @@ -38,67 +31,85 @@ import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.time.Duration

@OptIn(ExperimentalForeignApi::class)
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
internal actual class NTPUDPSocketOperations {
private var connection: nw_connection_t = null

actual suspend fun prepare(address: String, portNumber: Int, connectTimeout: Duration) {
val parameters = nw_parameters_create_secure_udp(
NW_PARAMETERS_DISABLE_PROTOCOL,
NW_PARAMETERS_DEFAULT_CONFIGURATION,
)
println("BEFORE CREATESECUREUDP")
val parameters = nw_parameters_create_secure_udp_workaround()
println("BEFORE CREATEHOST")
println("Endpoint is $address/$portNumber")
val endpoint = nw_endpoint_create_host(address, portNumber.toString())
println("BEFORE CONNCREATE")
connection = nw_connection_create(endpoint, parameters)
println("BEFORE SETQUEUE")
nw_connection_set_queue(connection, dispatch_get_current_queue())
val connectionStateDeferred = CompletableDeferred<nw_connection_state_t>()
println("BEFORE SETHANDLER")
nw_connection_set_state_changed_handler(connection) { state: nw_connection_state_t, _ ->
println("State is $state")
when (state) {
nw_connection_state_ready, nw_connection_state_failed, nw_connection_state_cancelled ->
connectionStateDeferred.complete(state)
}
}
println("Print test")
println("BEFORE START")
nw_connection_start(connection)
println("BEFORE WITHTIMEOUT")
withTimeout(connectTimeout) {
assertEquals(nw_connection_state_ready, connectionStateDeferred.await())
}
println("AFTER WITHTIMEOUT")
}

@OptIn(ExperimentalForeignApi::class)
actual suspend fun exchange(buffer: ByteArray, readTimeout: Duration) {
val toSendData = memScoped {
val cArray = allocArray<ByteVar>(buffer.size)
buffer.forEachIndexed { i, it ->
cArray[i] = it
}
cArray
println("EXCHANGE")
val data = buffer.pin().run {
dispatch_data_create(
addressOf(0),
buffer.size.convert(),
dispatch_get_current_queue(),
({ unpin() }),
)
}
nw_connection_send(
println("BEFORE SEND")
nw_connection_send_with_default_context(
connection,
dispatch_data_create(toSendData, buffer.size.convert(), null, _dispatch_data_destructor_free),
NW_CONNECTION_FINAL_MESSAGE_CONTEXT,
data,
true,
) {
println("SEND CB, ERROR IS $it")
assertNull(it)
}
val connectionReceptionDeferred = CompletableDeferred<dispatch_data_t>()
println("BEFORE RECEIVE")
nw_connection_receive(
connection,
1.convert(),
buffer.size.convert(),
) { content: dispatch_data_t, _, _, error: nw_error_t ->
println("RECEIVE CB, ERROR IS $error")
assertNull(error)
connectionReceptionDeferred.complete(content)
}
println("BEFORE RECEIVE TIMEOUT")
val receivedData = withTimeout(readTimeout) {
connectionReceptionDeferred.await()
}
dispatch_data_apply(receivedData) { _, _, regionPointer, _ ->
memcpy(buffer.refTo(0), regionPointer!!.reinterpret<ByteVar>(), buffer.size.convert())
false
println("BEFORE USEPINNED FOR RECEIVED")
buffer.usePinned {
dispatch_data_apply(receivedData) { _, offset, src, size ->
memcpy(it.addressOf(offset.toInt()), src, size)
true
}
}
}

actual fun tearDown() {
println("TEARDOWN")
nw_connection_force_cancel(connection)
connection = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import okio.Path

@OptIn(ExperimentalSerializationApi::class)
internal class SynchronizationResultProcessor(
val mutableState: MutableState,
val backupFilePath: Path?,
val fileSystem: FileSystem = FileSystemSupplier().system,
private val mutableState: MutableState,
private val backupFilePath: Path?,
private val fileSystem: FileSystem = FileSystemSupplier().system,
) {
var synchronizationResult: SynchronizationResult?
get() {
Expand Down
31 changes: 31 additions & 0 deletions library/src/nativeInterop/cinterop/NetworkFrameworkWorkaround.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package = com.tidal.networktime.internal.network_framework_workaround
language = Objective-C

---

#include <Network/Network.h>
#import <Network/connection.h>

// https://stackoverflow.com/a/63050804
NW_RETURNS_RETAINED nw_parameters_t nw_parameters_create_secure_udp_workaround() {
return nw_parameters_create_secure_udp(
NW_PARAMETERS_DISABLE_PROTOCOL,
NW_PARAMETERS_DEFAULT_CONFIGURATION
);
}

// https://youtrack.jetbrains.com/issue/KT-62102/
void nw_connection_send_with_default_context(
nw_connection_t connection,
_Nullable dispatch_data_t content,
bool is_complete,
nw_connection_send_completion_t completion
) {
nw_connection_send(
connection,
content,
NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT,
is_complete,
completion
);
}
1 change: 1 addition & 0 deletions samples/multiplatform-kotlin/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ kotlin {
macosArm64(),
iosX64(),
iosArm64(),
iosSimulatorArm64(),
).forEach {
it.binaries.framework {
baseName = project.name
Expand Down
Loading