Skip to content

Commit 2e60ea1

Browse files
committed
chore: upgrade Proto code to Swift 6
1 parent b41d364 commit 2e60ea1

File tree

5 files changed

+71
-38
lines changed

5 files changed

+71
-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

+41-22
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,45 @@ 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+
{
17+
return try await withCheckedThrowingContinuation { continuation in
18+
msgHandler = continuation
19+
Task {
20+
await closure()
21+
}
22+
}
23+
}
24+
25+
private var rpcHandler: CheckedContinuation<RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>, Error>?
1426
override func handleRPC(_ req: RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>) {
1527
rpcHandler?.resume(returning: req)
1628
}
29+
30+
/// Runs the given closure asynchronously and return the next non-RPC message received
31+
func expectRPC(with closure:
32+
@escaping @Sendable () async -> Void) async throws ->
33+
RPCRequest<Vpn_TunnelMessage, Vpn_ManagerMessage>
34+
{
35+
return try await withCheckedThrowingContinuation { continuation in
36+
rpcHandler = continuation
37+
Task {
38+
await closure()
39+
}
40+
}
41+
}
1742
}
1843

1944
@Suite(.timeLimit(.minutes(1)))
20-
struct SpeakerTests {
45+
struct SpeakerTests: Sendable {
2146
let pipeMT = Pipe()
2247
let pipeTM = Pipe()
2348
let uut: TestTunnel
@@ -56,14 +81,11 @@ struct SpeakerTests {
5681
@Test func handleSingleMessage() async throws {
5782
async let readDone: () = try uut.readLoop()
5883

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-
}
84+
let got = try await uut.expectMessage {
85+
var s = Vpn_ManagerMessage()
86+
s.start = Vpn_StartRequest()
87+
await #expect(throws: Never.self) {
88+
try await sender.send(s)
6789
}
6890
}
6991
#expect(got.msg == .start(Vpn_StartRequest()))
@@ -74,16 +96,13 @@ struct SpeakerTests {
7496
@Test func handleRPC() async throws {
7597
async let readDone: () = try uut.readLoop()
7698

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-
}
99+
let got = try await uut.expectRPC {
100+
var s = Vpn_ManagerMessage()
101+
s.start = Vpn_StartRequest()
102+
s.rpc = Vpn_RPC()
103+
s.rpc.msgID = 33
104+
await #expect(throws: Never.self) {
105+
try await sender.send(s)
87106
}
88107
}
89108
#expect(got.msg.msg == .start(Vpn_StartRequest()))

0 commit comments

Comments
 (0)