13
13
//===----------------------------------------------------------------------===//
14
14
15
15
import CAsyncDNSResolver
16
+ import Foundation
16
17
17
18
/// ``DNSResolver`` implementation backed by c-ares C library.
18
19
@available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
19
- public class CAresDNSResolver : DNSResolver {
20
+ public final class CAresDNSResolver : DNSResolver , Sendable {
20
21
let options : Options
21
22
let ares : Ares
22
23
@@ -121,18 +122,15 @@ extension QueryType {
121
122
// MARK: - c-ares query wrapper
122
123
123
124
@available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
124
- class Ares {
125
+ final class Ares : Sendable {
125
126
typealias QueryCallback = @convention ( c) (
126
127
UnsafeMutableRawPointer ? , CInt , CInt , UnsafeMutablePointer < CUnsignedChar > ? , CInt
127
128
) -> Void
128
129
129
- let options : AresOptions
130
- let channel : AresChannel
131
-
130
+ private let channel : AresChannel
132
131
private let queryProcessor : QueryProcessor
133
132
134
133
init ( options: AresOptions ) throws {
135
- self . options = options
136
134
self . channel = try AresChannel ( options: options)
137
135
138
136
// Need to call `ares_process` or `ares_process_fd` for query callbacks to happen
@@ -145,7 +143,8 @@ class Ares {
145
143
name: String ,
146
144
replyParser: ReplyParser
147
145
) async throws -> ReplyParser . Reply {
148
- try await withTaskCancellationHandler (
146
+ let channel = self . channel
147
+ return try await withTaskCancellationHandler (
149
148
operation: {
150
149
try await withCheckedThrowingContinuation { continuation in
151
150
let handler = QueryReplyHandler ( parser: replyParser, continuation)
@@ -178,7 +177,7 @@ class Ares {
178
177
}
179
178
} ,
180
179
onCancel: {
181
- self . channel. withChannel { channel in
180
+ channel. withChannel { channel in
182
181
ares_cancel ( channel)
183
182
}
184
183
}
@@ -198,16 +197,18 @@ extension Ares {
198
197
// https://github.com/dimbleby/c-ares-resolver/blob/master/src/unix/eventloop.rs // ignore-unacceptable-language
199
198
// https://github.com/dimbleby/rust-c-ares/blob/master/src/channel.rs // ignore-unacceptable-language
200
199
// https://github.com/dimbleby/rust-c-ares/blob/master/examples/event-loop.rs // ignore-unacceptable-language
201
- class QueryProcessor {
200
+ final class QueryProcessor : @ unchecked Sendable {
202
201
static let defaultPollInterval : UInt64 = 10 * 1_000_000 // 10ms
203
202
204
203
private let channel : AresChannel
205
204
private let pollIntervalNanos : UInt64
206
205
207
- private var pollingTask : Task < Void , Error > ?
206
+ private let lock = NSLock ( )
207
+ private var locked_pollingTask : Task < Void , Error > ?
208
208
209
209
deinit {
210
- self . pollingTask? . cancel ( )
210
+ // No need to lock here as there can exist no more strong references to self.
211
+ self . locked_pollingTask? . cancel ( )
211
212
}
212
213
213
214
init ( channel: AresChannel , pollIntervalNanos: UInt64 = QueryProcessor . defaultPollInterval) {
@@ -218,7 +219,7 @@ extension Ares {
218
219
/// Asks c-ares for the set of socket descriptors we are waiting on for the `ares_channel`'s pending queries
219
220
/// then call `ares_process_fd` if any is ready for read and/or write.
220
221
/// c-ares returns up to `ARES_GETSOCK_MAXNUM` socket descriptors only. If more are in use (unlikely) they are not reported back.
221
- func poll( ) async {
222
+ func poll( ) {
222
223
var socks = [ ares_socket_t] ( repeating: ares_socket_t ( ) , count: Int ( ARES_GETSOCK_MAXNUM) )
223
224
224
225
self . channel. withChannel { channel in
@@ -249,12 +250,14 @@ extension Ares {
249
250
}
250
251
251
252
private func schedule( ) {
252
- self . pollingTask = Task { [ weak self] in
253
+ self . lock. lock ( )
254
+ defer { self . lock. unlock ( ) }
255
+ self . locked_pollingTask = Task { [ weak self] in
253
256
guard let s = self else {
254
257
return
255
258
}
256
259
try await Task . sleep ( nanoseconds: s. pollIntervalNanos)
257
- await s. poll ( )
260
+ s. poll ( )
258
261
}
259
262
}
260
263
}
@@ -291,7 +294,7 @@ extension Ares {
291
294
// MARK: - c-ares query reply parsers
292
295
293
296
protocol AresQueryReplyParser {
294
- associatedtype Reply
297
+ associatedtype Reply : Sendable
295
298
296
299
func parse( buffer: UnsafeMutablePointer < CUnsignedChar > ? , length: CInt ) throws -> Reply
297
300
}
0 commit comments