Skip to content

Commit d3cfab0

Browse files
authored
chore: fix more instances of flaky native reference handling (#176)
1 parent 8297b18 commit d3cfab0

File tree

6 files changed

+44
-25
lines changed

6 files changed

+44
-25
lines changed

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/auth/signing/AwsSignerNative.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
package aws.sdk.kotlin.crt.auth.signing
77

88
import aws.sdk.kotlin.crt.*
9-
import aws.sdk.kotlin.crt.Allocator
109
import aws.sdk.kotlin.crt.auth.credentials.Credentials
11-
import aws.sdk.kotlin.crt.awsAssertOpSuccess
1210
import aws.sdk.kotlin.crt.http.*
1311
import aws.sdk.kotlin.crt.util.asAwsByteCursor
1412
import aws.sdk.kotlin.crt.util.initFromCursor
@@ -23,7 +21,7 @@ import platform.posix.UINT64_MAX
2321
/**
2422
* Static class for a variety of AWS signing APIs.
2523
*/
26-
public actual object AwsSigner {
24+
public actual object AwsSigner : WithCrt() {
2725
public actual suspend fun signRequest(
2826
request: HttpRequest,
2927
config: AwsSigningConfig,

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/http/HttpClientConnectionManagerNative.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
package aws.sdk.kotlin.crt.http
77

88
import aws.sdk.kotlin.crt.*
9-
import aws.sdk.kotlin.crt.Allocator
10-
import aws.sdk.kotlin.crt.awsAssertOpSuccess
119
import aws.sdk.kotlin.crt.io.SocketDomain
1210
import aws.sdk.kotlin.crt.io.SocketOptions
1311
import aws.sdk.kotlin.crt.io.SocketType
1412
import aws.sdk.kotlin.crt.io.requiresTls
1513
import aws.sdk.kotlin.crt.util.*
1614
import cnames.structs.aws_http_connection_manager
15+
import kotlinx.atomicfu.atomic
1716
import kotlinx.cinterop.*
1817
import libcrt.*
1918
import kotlin.coroutines.Continuation
@@ -23,8 +22,12 @@ import kotlin.coroutines.suspendCoroutine
2322

2423
public actual class HttpClientConnectionManager actual constructor(
2524
public actual val options: HttpClientConnectionManagerOptions,
26-
) : Closeable,
25+
) : WithCrt(),
26+
Closeable,
2727
AsyncShutdown {
28+
29+
private val closed = atomic(false)
30+
2831
public actual val managerMetrics: HttpManagerMetrics
2932
get() = memScoped {
3033
val metrics = alloc<aws_http_manager_metrics>()
@@ -152,7 +155,9 @@ public actual class HttpClientConnectionManager actual constructor(
152155
}
153156

154157
actual override fun close() {
155-
aws_http_connection_manager_release(manager)
158+
if (closed.compareAndSet(false, true)) {
159+
aws_http_connection_manager_release(manager)
160+
}
156161
}
157162
}
158163

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/http/HttpClientConnectionNative.kt

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,31 @@
55
package aws.sdk.kotlin.crt.http
66

77
import aws.sdk.kotlin.crt.*
8-
import aws.sdk.kotlin.crt.Allocator
9-
import aws.sdk.kotlin.crt.NativeHandle
10-
import aws.sdk.kotlin.crt.awsAssertOpSuccess
118
import aws.sdk.kotlin.crt.io.Buffer
129
import aws.sdk.kotlin.crt.io.ByteCursorBuffer
1310
import aws.sdk.kotlin.crt.util.asAwsByteCursor
1411
import aws.sdk.kotlin.crt.util.initFromCursor
1512
import aws.sdk.kotlin.crt.util.toKString
1613
import aws.sdk.kotlin.crt.util.withAwsByteCursor
14+
import kotlinx.atomicfu.atomic
1715
import kotlinx.cinterop.*
1816
import libcrt.*
1917
import platform.posix.size_t
2018

2119
internal class HttpClientConnectionNative(
2220
private val manager: HttpClientConnectionManager,
2321
override val ptr: CPointer<cnames.structs.aws_http_connection>,
24-
) : Closeable,
22+
) : WithCrt(),
23+
Closeable,
2524
HttpClientConnection,
2625
NativeHandle<cnames.structs.aws_http_connection> {
2726

27+
private val closed = atomic(false)
28+
2829
override val id: String = ptr.rawValue.toString()
2930
override fun makeRequest(httpReq: HttpRequest, handler: HttpStreamResponseHandler): HttpStream {
3031
val nativeReq = httpReq.toNativeRequest()
31-
val cbData = HttpStreamContext(handler, nativeReq)
32+
val cbData = HttpStreamContext(null, handler, nativeReq)
3233
val stableRef = StableRef.create(cbData)
3334
val reqOptions = cValue<aws_http_make_request_options> {
3435
self_size = sizeOf<aws_http_make_request_options>().convert()
@@ -50,22 +51,30 @@ internal class HttpClientConnectionNative(
5051
throw CrtRuntimeException("aws_http_connection_make_request()")
5152
}
5253

53-
return HttpStreamNative(stream)
54+
return HttpStreamNative(stream).also { cbData.stream = it }
5455
}
5556

5657
override fun shutdown() {
5758
aws_http_connection_close(ptr)
5859
}
5960

6061
override fun close() {
61-
manager.releaseConnection(this)
62+
if (closed.compareAndSet(false, true)) {
63+
manager.releaseConnection(this)
64+
}
6265
}
6366
}
6467

6568
/**
6669
* Userdata passed through the native callbacks for HTTP responses
6770
*/
6871
private class HttpStreamContext(
72+
/**
73+
* The Kotlin stream object. This starts as null because the context is created before the stream itself. We need
74+
* the stream in callbacks so we set it lazily.
75+
*/
76+
var stream: HttpStreamNative? = null,
77+
6978
/**
7079
* The actual Kotlin handler for each callback
7180
*/
@@ -85,7 +94,7 @@ private fun onResponseHeaders(
8594
userdata: COpaquePointer?,
8695
): Int {
8796
val ctx = userdata?.asStableRef<HttpStreamContext>()?.get() ?: return aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE.toInt())
88-
val stream = nativeStream?.let { HttpStreamNative(it) } ?: return aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE.toInt())
97+
val stream = ctx.stream ?: return AWS_OP_ERR
8998

9099
val hdrCnt = numHeaders.toInt()
91100
val headers: List<HttpHeader>? = if (hdrCnt > 0 && headerArray != null) {
@@ -106,6 +115,7 @@ private fun onResponseHeaders(
106115
log(LogLevel.Error, "onResponseHeaders: $ex")
107116
return aws_raise_error(AWS_ERROR_HTTP_CALLBACK_FAILURE.toInt())
108117
}
118+
109119
return AWS_OP_SUCCESS
110120
}
111121

@@ -115,7 +125,8 @@ private fun onResponseHeaderBlockDone(
115125
userdata: COpaquePointer?,
116126
): Int {
117127
val ctx = userdata?.asStableRef<HttpStreamContext>()?.get() ?: return AWS_OP_ERR
118-
val stream = nativeStream?.let { HttpStreamNative(it) } ?: return AWS_OP_ERR
128+
val stream = ctx.stream ?: return AWS_OP_ERR
129+
119130
try {
120131
ctx.handler.onResponseHeadersDone(stream, blockType.value.toInt())
121132
} catch (ex: Exception) {
@@ -132,7 +143,7 @@ private fun onIncomingBody(
132143
userdata: COpaquePointer?,
133144
): Int {
134145
val ctx = userdata?.asStableRef<HttpStreamContext>()?.get() ?: return AWS_OP_ERR
135-
val stream = nativeStream?.let { HttpStreamNative(it) } ?: return AWS_OP_ERR
146+
val stream = ctx.stream ?: return AWS_OP_ERR
136147

137148
try {
138149
val body = if (data != null) ByteCursorBuffer(data) else Buffer.Empty
@@ -159,7 +170,8 @@ private fun onStreamComplete(
159170
) {
160171
val stableRef = userdata?.asStableRef<HttpStreamContext>() ?: return
161172
val ctx = stableRef.get()
162-
val stream = nativeStream?.let { HttpStreamNative(it) } ?: return
173+
val stream = ctx.stream ?: return
174+
163175
try {
164176
ctx.handler.onResponseComplete(stream, errorCode)
165177
} catch (ex: Exception) {

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/http/HttpStreamNative.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import aws.sdk.kotlin.crt.CrtRuntimeException
99
import aws.sdk.kotlin.crt.NativeHandle
1010
import aws.sdk.kotlin.crt.awsAssertOpSuccess
1111
import aws.sdk.kotlin.crt.util.asAwsByteCursor
12+
import kotlinx.atomicfu.atomic
1213
import kotlinx.cinterop.*
1314
import libcrt.*
1415
import kotlin.coroutines.Continuation
@@ -21,6 +22,8 @@ internal class HttpStreamNative(
2122
) : HttpStream,
2223
NativeHandle<cnames.structs.aws_http_stream> {
2324

25+
private val closed = atomic(false)
26+
2427
override val responseStatusCode: Int
2528
get() {
2629
return memScoped {
@@ -90,7 +93,9 @@ internal class HttpStreamNative(
9093
}
9194

9295
override fun close() {
93-
aws_http_stream_release(ptr)
96+
if (closed.compareAndSet(false, true)) {
97+
aws_http_stream_release(ptr)
98+
}
9499
}
95100
}
96101

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/io/HostResolverNative.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55

66
package aws.sdk.kotlin.crt.io
77

8-
import aws.sdk.kotlin.crt.Allocator
9-
import aws.sdk.kotlin.crt.AsyncShutdown
10-
import aws.sdk.kotlin.crt.Closeable
11-
import aws.sdk.kotlin.crt.NativeHandle
8+
import aws.sdk.kotlin.crt.*
129
import aws.sdk.kotlin.crt.util.ShutdownChannel
1310
import aws.sdk.kotlin.crt.util.shutdownChannel
1411
import kotlinx.cinterop.*
@@ -19,7 +16,8 @@ public actual class HostResolver private constructor(
1916
private val elg: EventLoopGroup,
2017
private val manageElg: Boolean,
2118
private val maxEntries: Int,
22-
) : NativeHandle<aws_host_resolver>,
19+
) : WithCrt(),
20+
NativeHandle<aws_host_resolver>,
2321
Closeable,
2422
AsyncShutdown {
2523

aws-crt-kotlin/native/src/aws/sdk/kotlin/crt/util/DigestNative.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
*/
55
package aws.sdk.kotlin.crt.util
66

7+
import aws.sdk.kotlin.crt.WithCrt
78
import aws.sdk.kotlin.crt.util.hashing.Sha256
89

910
/**
1011
* Utility object for various hash functions
1112
*/
12-
public actual object Digest {
13+
public actual object Digest : WithCrt() {
1314
/**
1415
* Calculate the SHA-256 hash of the input [buffer]
1516
*/

0 commit comments

Comments
 (0)