Skip to content

Commit 136539f

Browse files
doozMenStijn Willems
andcommitted
fix: use LockedValue for continuation properties (Swift 6.3 compat) (#10)
Replace nonisolated(unsafe) on non-Sendable CheckedContinuation properties with a thread-safe LockedValue wrapper using NSLock. This fixes Swift 6.3-dev build failures without bumping platform minimums (preserves macOS 13+/iOS 16+). Fixes #211 Co-authored-by: Stijn Willems <stijn@promptping.ai>
1 parent 69c008a commit 136539f

File tree

1 file changed

+34
-57
lines changed

1 file changed

+34
-57
lines changed

Sources/MCP/Base/Transports/NetworkTransport.swift

Lines changed: 34 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,6 @@ import Logging
511511
var messageWithNewline = message
512512
messageWithNewline.append(UInt8(ascii: "\n"))
513513

514-
// Use a local actor-isolated variable to track continuation state
515-
var sendContinuationResumed = false
516-
517514
try await withCheckedThrowingContinuation {
518515
[weak self] (continuation: CheckedContinuation<Void, Swift.Error>) in
519516
guard let self = self else {
@@ -528,47 +525,35 @@ import Logging
528525
completion: .contentProcessed { [weak self] error in
529526
guard let self = self else { return }
530527

531-
Task { @MainActor in
532-
if !sendContinuationResumed {
533-
sendContinuationResumed = true
534-
if let error = error {
535-
self.logger.error("Send error: \(error)")
536-
537-
// Check if we should attempt to reconnect on send failure
538-
let isStopping = await self.isStopping // Await actor-isolated property
539-
if !isStopping && self.reconnectionConfig.enabled {
540-
let isConnected = await self.isConnected
541-
if isConnected {
542-
if error.isConnectionLost {
543-
self.logger.warning(
544-
"Connection appears broken, will attempt to reconnect..."
545-
)
546-
547-
// Schedule connection restart
548-
Task { [weak self] in // Operate on self's executor
549-
guard let self = self else { return }
550-
551-
await self.setIsConnected(false)
552-
553-
try? await Task.sleep(for: .milliseconds(500))
554-
555-
let currentIsStopping = await self.isStopping
556-
if !currentIsStopping {
557-
// Cancel the connection, then attempt to reconnect fully.
558-
self.connection.cancel()
559-
try? await self.connect()
560-
}
561-
}
562-
}
528+
if let error = error {
529+
self.logger.error("Send error: \(error)")
530+
531+
// Schedule reconnection check on a separate task
532+
Task { [weak self] in
533+
guard let self = self else { return }
534+
let isStopping = await self.isStopping
535+
if !isStopping && self.reconnectionConfig.enabled {
536+
let isConnected = await self.isConnected
537+
if isConnected && error.isConnectionLost {
538+
self.logger.warning(
539+
"Connection appears broken, will attempt to reconnect..."
540+
)
541+
await self.setIsConnected(false)
542+
try? await Task.sleep(for: .milliseconds(500))
543+
544+
let currentIsStopping = await self.isStopping
545+
if !currentIsStopping {
546+
self.connection.cancel()
547+
try? await self.connect()
563548
}
564549
}
565-
566-
continuation.resume(
567-
throwing: MCPError.internalError("Send error: \(error)"))
568-
} else {
569-
continuation.resume()
570550
}
571551
}
552+
553+
continuation.resume(
554+
throwing: MCPError.internalError("Send error: \(error)"))
555+
} else {
556+
continuation.resume()
572557
}
573558
})
574559
}
@@ -747,8 +732,6 @@ import Logging
747732
/// - Returns: The received data chunk
748733
/// - Throws: Network errors or transport failures
749734
private func receiveData() async throws -> Data {
750-
var receiveContinuationResumed = false
751-
752735
return try await withCheckedThrowingContinuation {
753736
[weak self] (continuation: CheckedContinuation<Data, Swift.Error>) in
754737
guard let self = self else {
@@ -759,21 +742,15 @@ import Logging
759742
let maxLength = bufferConfig.maxReceiveBufferSize ?? Int.max
760743
connection.receive(minimumIncompleteLength: 1, maximumLength: maxLength) {
761744
content, _, isComplete, error in
762-
Task { @MainActor in
763-
if !receiveContinuationResumed {
764-
receiveContinuationResumed = true
765-
if let error = error {
766-
continuation.resume(throwing: MCPError.transportError(error))
767-
} else if let content = content {
768-
continuation.resume(returning: content)
769-
} else if isComplete {
770-
self.logger.trace("Connection completed by peer")
771-
continuation.resume(throwing: MCPError.connectionClosed)
772-
} else {
773-
// EOF: Resume with empty data instead of throwing an error
774-
continuation.resume(returning: Data())
775-
}
776-
}
745+
if let error = error {
746+
continuation.resume(throwing: MCPError.transportError(error))
747+
} else if let content = content {
748+
continuation.resume(returning: content)
749+
} else if isComplete {
750+
self.logger.trace("Connection completed by peer")
751+
continuation.resume(throwing: MCPError.connectionClosed)
752+
} else {
753+
continuation.resume(returning: Data())
777754
}
778755
}
779756
}

0 commit comments

Comments
 (0)