I was just testing my Vapor based messaging server and hit it with 6300 concurrent web-socket connections. Got a segmentation fault crash which I believe must be due to some kind of async await racing issue in Swift actor. It seems to happen in Swift's dictionary getter. I can't see what would I be doing wrong in my code.
Linux version: Ubuntu 20.04.2 LTS
Swift version: Swift version 5.6.1 (swift-5.6.1-RELEASE)
Crash report looks like this:
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805cdb582, Backtrace.(printBacktrace in _B82A8C0ED7C904841114FDF244F9E58E)(signal: Swift.Int32) -> () at /home/username/live-messaging/.build/checkouts/swift-backtrace/Sources/Backtrace/Backtrace.swift:66
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805cdb817, closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install(signals: Swift.Array<Swift.Int32>) -> () at /home/username/live-messaging/.build/checkouts/swift-backtrace/Sources/Backtrace/Backtrace.swift:80
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805cdb817, @objc closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install(signals: Swift.Array<Swift.Int32>) -> () at /home/username/live-messaging/<compiler-generated>:79
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa77196f41f
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805ad8a3f, generic specialization <Swift.String, Swift.Dictionary<Swift.ObjectIdentifier, App.Connection>> of Swift.Dictionary.subscript.getter : (A) -> Swift.Optional<B> at /home/username/live-messaging/<compiler-generated>:0
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805ad8a3f, closure #2 (Swift.String, App.Conversation.Participant) -> App.ConversationInfo.Participant in App.MessagingCenter.onConnectionUpgraded(ws: WebSocketKit.WebSocket, user: App.User, conversationId: Swift.String) throws -> () at /home/username/live-messaging/Sources/App/MessagingCenter/MessagingCenter.swift:145
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805ad8a3f, reabstraction thunk helper from @callee_guaranteed (@guaranteed Swift.String, @guaranteed App.Conversation.Participant) -> (@owned App.ConversationInfo.Participant, @error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed (key: Swift.String, value: App.Conversation.Participant)) -> (@out App.ConversationInfo.Participant, @error @owned Swift.Error) at /home/username/live-messaging/<compiler-generated>:0
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805ad8a3f, generic specialization <Swift.Dictionary<Swift.String, App.Conversation.Participant>, App.ConversationInfo.Participant> of (extension in Swift):Swift.Collection.map<A>((A.Element) throws -> A1) throws -> Swift.Array<A1> at /home/username/live-messaging/<compiler-generated>:0
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805ad8a3f, App.MessagingCenter.onConnectionUpgraded(ws: WebSocketKit.WebSocket, user: App.User, conversationId: Swift.String) throws -> () at /home/username/live-messaging/Sources/App/MessagingCenter/MessagingCenter.swift:141
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x558805b05f6b, (1) suspend resume partial function for App.onUpgrade(req: Vapor.Request, ws: WebSocketKit.WebSocket) async -> () at /home/username/live-messaging/Sources/App/ws/upgrader.swift:19
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa77213dcb7
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa77213e398
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa7718e8414
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa7718e8272
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa7718f3361
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa771963608
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0x7fa770d18132
Sep 07 09:29:18 machine-1 runProduction[2371825]: 0xffffffffffffffff
Sep 07 09:29:18 machine-1 runProduction[2371821]: Segmentation fault (core dumped)
Sep 07 09:29:18 machine-1 systemd[1]: lmsg.service: Main process exited, code=exited, status=139/n/a
Sep 07 09:29:18 machine-1 systemd[1]: lmsg.service: Failed with result 'exit-code'.
And the point in my code where crash happens (when accessing dictionary getter):
import NIOCore
import Vapor
typealias LMID = String
typealias UserId = LMID
typealias ConnectionId = ObjectIdentifier
actor MessagingCenter {
...
var connectionsByUserId: [UserId: [ConnectionId: Connection]] = [:]
...
func onConnectionUpgraded(ws: WebSocket, user: User, conversationId: ConversationId) throws {
...
let convInfo = ConversationInfo(
conversationId: conversationId,
date: conversation.dateCreated,
participants: conversation.participants.map {
ConversationInfo.Participant(
id: $1.id,
isAdmin: $1.isAdmin,
isConnected: connectionsByUserId[$1.id] != nil // <- crash occured on this line
)
},
messages: conversation.messages.map {
ConversationInfo.Message(
id: $0.id,
senderId: $0.senderId,
date: $0.date,
payload: $0.payload
)
}
)
...
}