Skip to content

Commit 386848f

Browse files
committed
chore: upgrade Proto code to Swift 6
1 parent b41d364 commit 386848f

File tree

5 files changed

+69
-38
lines changed

5 files changed

+69
-38
lines changed

Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@
660660
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop";
661661
PRODUCT_NAME = "$(TARGET_NAME)";
662662
SWIFT_EMIT_LOC_STRINGS = YES;
663-
SWIFT_VERSION = 5.0;
663+
SWIFT_VERSION = 6.0;
664664
};
665665
name = Debug;
666666
};
@@ -690,7 +690,7 @@
690690
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop";
691691
PRODUCT_NAME = "$(TARGET_NAME)";
692692
SWIFT_EMIT_LOC_STRINGS = YES;
693-
SWIFT_VERSION = 5.0;
693+
SWIFT_VERSION = 6.0;
694694
};
695695
name = Release;
696696
};
@@ -835,7 +835,7 @@
835835
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests";
836836
PRODUCT_NAME = "$(TARGET_NAME)";
837837
SWIFT_EMIT_LOC_STRINGS = NO;
838-
SWIFT_VERSION = 5.0;
838+
SWIFT_VERSION = 6.0;
839839
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop";
840840
};
841841
name = Debug;
@@ -853,7 +853,7 @@
853853
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests";
854854
PRODUCT_NAME = "$(TARGET_NAME)";
855855
SWIFT_EMIT_LOC_STRINGS = NO;
856-
SWIFT_VERSION = 5.0;
856+
SWIFT_VERSION = 6.0;
857857
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop";
858858
};
859859
name = Release;

Coder Desktop/Coder Desktop.xcodeproj/xcshareddata/xcschemes/ProtoTests.xcscheme

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@
5454
savedToolIdentifier = ""
5555
useCustomWorkingDirectory = "NO"
5656
debugDocumentVersioning = "YES">
57+
<MacroExpansion>
58+
<BuildableReference
59+
BuildableIdentifier = "primary"
60+
BlueprintIdentifier = "961678FB2CFF100D00B2B6DF"
61+
BuildableName = "Coder Desktop.app"
62+
BlueprintName = "Coder Desktop"
63+
ReferencedContainer = "container:Coder Desktop.xcodeproj">
64+
</BuildableReference>
65+
</MacroExpansion>
5766
</ProfileAction>
5867
<AnalyzeAction
5968
buildConfiguration = "Debug">

Coder Desktop/Proto/Receiver.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ actor Receiver<RecvMsg: Message> {
2222
dispatch.read(offset: 0, length: 4, queue: queue) { done, data, error in
2323
guard error == 0 else {
2424
let errStrPtr = strerror(error)
25-
let errStr = String(validatingUTF8: errStrPtr!)!
25+
let errStr = String(validatingCString: errStrPtr!)!
2626
continuation.resume(throwing: ReceiveError.readError(errStr))
2727
return
2828
}
@@ -42,7 +42,7 @@ actor Receiver<RecvMsg: Message> {
4242
dispatch.read(offset: 0, length: Int(length), queue: queue) { done, data, error in
4343
guard error == 0 else {
4444
let errStrPtr = strerror(error)
45-
let errStr = String(validatingUTF8: errStrPtr!)!
45+
let errStr = String(validatingCString: errStrPtr!)!
4646
continuation.resume(throwing: ReceiveError.readError(errStr))
4747
return
4848
}

Coder Desktop/Proto/Speaker.swift

+15-10
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,25 @@ class Speaker<SendMsg: RPCMessage & Message, RecvMsg: RPCMessage & Message> {
133133
/// Send a unary RPC message and handle the response
134134
func unaryRPC(_ req: SendMsg) async throws -> RecvMsg {
135135
return try await withCheckedThrowingContinuation { continuation in
136+
// capture sendable objects that will be part of the closure in local variables
137+
// so that Swift 6 doesn't complain that our Task accesses non-sendable state.
138+
let sender_ = self.sender
139+
let secretary_ = self.secretary
140+
let logger_ = self.logger
136141
Task {
137-
let msgID = await self.secretary.record(continuation: continuation)
142+
let msgID = await secretary_.record(continuation: continuation)
138143
var req = req
139144
req.rpc = Vpn_RPC()
140145
req.rpc.msgID = msgID
141146
do {
142-
self.logger.debug("sending RPC with msgID: \(msgID)")
143-
try await self.sender.send(req)
147+
logger_.debug("sending RPC with msgID: \(msgID)")
148+
try await sender_.send(req)
144149
} catch {
145-
self.logger.warning("failed to send RPC with msgID: \(msgID): \(error)")
146-
await self.secretary.erase(id: req.rpc.msgID)
150+
logger_.warning("failed to send RPC with msgID: \(msgID): \(error)")
151+
await secretary_.erase(id: req.rpc.msgID)
147152
continuation.resume(throwing: error)
148153
}
149-
self.logger.debug("sent RPC with msgID: \(msgID)")
154+
logger_.debug("sent RPC with msgID: \(msgID)")
150155
}
151156
}
152157
}
@@ -169,7 +174,7 @@ class Speaker<SendMsg: RPCMessage & Message, RecvMsg: RPCMessage & Message> {
169174
}
170175

171176
/// A class that performs the initial VPN protocol handshake and version negotiation.
172-
class Handshaker {
177+
class Handshaker: @unchecked Sendable {
173178
private let writeFD: FileHandle
174179
private let dispatch: DispatchIO
175180
private var theirData: Data = .init()
@@ -219,7 +224,7 @@ class Handshaker {
219224
private func handleRead(_: Bool, _ data: DispatchData?, _ error: Int32) {
220225
guard error == 0 else {
221226
let errStrPtr = strerror(error)
222-
let errStr = String(validatingUTF8: errStrPtr!)!
227+
let errStr = String(validatingCString: errStrPtr!)!
223228
continuation?.resume(throwing: HandshakeError.readError(errStr))
224229
return
225230
}
@@ -277,7 +282,7 @@ enum HandshakeError: Error {
277282
case unsupportedVersion([ProtoVersion])
278283
}
279284

280-
struct RPCRequest<SendMsg: RPCMessage & Message, RecvMsg: RPCMessage> {
285+
struct RPCRequest<SendMsg: RPCMessage & Message, RecvMsg: RPCMessage & Sendable>: Sendable {
281286
let msg: RecvMsg
282287
private let sender: Sender<SendMsg>
283288

@@ -302,7 +307,7 @@ enum RPCError: Error {
302307
}
303308

304309
/// An actor to record outgoing RPCs and route their replies to the original sender
305-
actor RPCSecretary<RecvMsg: RPCMessage> {
310+
actor RPCSecretary<RecvMsg: RPCMessage & Sendable> {
306311
private var continuations: [UInt64: CheckedContinuation<RecvMsg, Error>] = [:]
307312
private var nextMsgID: UInt64 = 1
308313

Coder Desktop/ProtoTests/SpeakerTests.swift

+39-22
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,43 @@ import Testing
44

55
/// A concrete, test class for the abstract Speaker, which overrides the handlers to send things to
66
/// continuations we set in the test.
7-
class TestTunnel: Speaker<Vpn_TunnelMessage, Vpn_ManagerMessage> {
8-
var msgHandler: CheckedContinuation<Vpn_ManagerMessage, Error>?
7+
class TestTunnel: Speaker<Vpn_TunnelMessage, Vpn_ManagerMessage>, @unchecked Sendable {
8+
private var msgHandler: CheckedContinuation<Vpn_ManagerMessage, Error>?
99
override func handleMessage(_ msg: Vpn_ManagerMessage) {
1010
msgHandler?.resume(returning: msg)
1111
}
1212

13-
var rpcHandler: CheckedContinuation<RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>, Error>?
13+
/// Runs the given closure asynchronously and returns the next non-RPC message received.
14+
func expectMessage(with closure:
15+
@escaping @Sendable () async -> Void) async throws -> Vpn_ManagerMessage {
16+
return try await withCheckedThrowingContinuation { continuation in
17+
msgHandler = continuation
18+
Task {
19+
await closure()
20+
}
21+
}
22+
}
23+
24+
private var rpcHandler: CheckedContinuation<RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>, Error>?
1425
override func handleRPC(_ req: RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>) {
1526
rpcHandler?.resume(returning: req)
1627
}
28+
29+
/// Runs the given closure asynchronously and return the next non-RPC message received
30+
func expectRPC(with closure:
31+
@escaping @Sendable () async -> Void) async throws ->
32+
RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage> {
33+
return try await withCheckedThrowingContinuation { continuation in
34+
rpcHandler = continuation
35+
Task {
36+
await closure()
37+
}
38+
}
39+
}
1740
}
1841

1942
@Suite(.timeLimit(.minutes(1)))
20-
struct SpeakerTests {
43+
struct SpeakerTests: Sendable {
2144
let pipeMT = Pipe()
2245
let pipeTM = Pipe()
2346
let uut: TestTunnel
@@ -56,14 +79,11 @@ struct SpeakerTests {
5679
@Test func handleSingleMessage() async throws {
5780
async let readDone: () = try uut.readLoop()
5881

59-
let got = try await withCheckedThrowingContinuation { continuation in
60-
uut.msgHandler = continuation
61-
Task {
62-
var s = Vpn_ManagerMessage()
63-
s.start = Vpn_StartRequest()
64-
await #expect(throws: Never.self) {
65-
try await sender.send(s)
66-
}
82+
let got = try await uut.expectMessage {
83+
var s = Vpn_ManagerMessage()
84+
s.start = Vpn_StartRequest()
85+
await #expect(throws: Never.self) {
86+
try await sender.send(s)
6787
}
6888
}
6989
#expect(got.msg == .start(Vpn_StartRequest()))
@@ -74,16 +94,13 @@ struct SpeakerTests {
7494
@Test func handleRPC() async throws {
7595
async let readDone: () = try uut.readLoop()
7696

77-
let got = try await withCheckedThrowingContinuation { continuation in
78-
uut.rpcHandler = continuation
79-
Task {
80-
var s = Vpn_ManagerMessage()
81-
s.start = Vpn_StartRequest()
82-
s.rpc = Vpn_RPC()
83-
s.rpc.msgID = 33
84-
await #expect(throws: Never.self) {
85-
try await sender.send(s)
86-
}
97+
let got = try await uut.expectRPC {
98+
var s = Vpn_ManagerMessage()
99+
s.start = Vpn_StartRequest()
100+
s.rpc = Vpn_RPC()
101+
s.rpc.msgID = 33
102+
await #expect(throws: Never.self) {
103+
try await sender.send(s)
87104
}
88105
}
89106
#expect(got.msg.msg == .start(Vpn_StartRequest()))

0 commit comments

Comments
 (0)