Skip to content

Commit a8f3ec8

Browse files
0xTimgwynne
andauthored
Sendable Take 2 (#136)
* Add dev containers to gitignore * Revert "Revert "Add Sendable conformances to WebsocketKit (#131)" (#135)" This reverts commit 0b43ce5. * Add @preconcurrency annotations to work around unsafe users * More preconcurrency annotations * Add API breakage allowlist * Add missing preconcurrency annotations * Last missing annotations * Update allowlist --------- Co-authored-by: Gwynne Raskind <gwynne@vapor.codes>
1 parent 0b43ce5 commit a8f3ec8

13 files changed

+233
-175
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
API breakage: func WebSocket.onText(_:) is now with @preconcurrency
2+
API breakage: func WebSocket.onBinary(_:) is now with @preconcurrency
3+
API breakage: func WebSocket.onPong(_:) is now with @preconcurrency
4+
API breakage: func WebSocket.onPing(_:) is now with @preconcurrency
5+
API breakage: func WebSocket.connect(to:headers:configuration:on:onUpgrade:) is now with @preconcurrency
6+
API breakage: func WebSocket.connect(scheme:host:port:path:query:headers:configuration:on:onUpgrade:) is now with @preconcurrency
7+
API breakage: func WebSocket.connect(scheme:host:port:path:query:headers:proxy:proxyPort:proxyHeaders:proxyConnectDeadline:configuration:on:onUpgrade:) is now with @preconcurrency
8+
API breakage: func WebSocket.connect(to:headers:proxy:proxyPort:proxyHeaders:proxyConnectDeadline:configuration:on:onUpgrade:) is now with @preconcurrency
9+
API breakage: func WebSocket.client(on:onUpgrade:) is now with @preconcurrency
10+
API breakage: func WebSocket.client(on:config:onUpgrade:) is now with @preconcurrency
11+
API breakage: func WebSocket.server(on:onUpgrade:) is now with @preconcurrency
12+
API breakage: func WebSocket.server(on:config:onUpgrade:) is now with @preconcurrency
13+
API breakage: func WebSocketClient.connect(scheme:host:port:path:query:headers:onUpgrade:) is now with @preconcurrency
14+
API breakage: func WebSocketClient.connect(scheme:host:port:path:query:headers:proxy:proxyPort:proxyHeaders:proxyConnectDeadline:onUpgrade:) is now with @preconcurrency

.github/workflows/test.yml

-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ concurrency:
55
on:
66
pull_request: { types: [opened, reopened, synchronize, ready_for_review] }
77
push: { branches: [ main ] }
8-
98
jobs:
10-
119
vapor-integration:
1210
if: ${{ !(github.event.pull_request.draft || false) }}
1311
runs-on: ubuntu-latest

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
DerivedData
66
.swiftpm
77
Package.resolved
8+
.devcontainer/

Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ let package = Package(
1616
.package(url: "https://github.com/apple/swift-nio.git", from: "2.53.0"),
1717
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.16.0"),
1818
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.24.0"),
19-
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.11.4"),
20-
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
19+
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.16.0"),
20+
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.1.0"),
2121
],
2222
targets: [
2323
.target(name: "WebSocketKit", dependencies: [

Sources/WebSocketKit/Concurrency/WebSocket+Concurrency.swift

+30-22
Original file line numberDiff line numberDiff line change
@@ -40,44 +40,52 @@ extension WebSocket {
4040
try await close(code: code).get()
4141
}
4242

43-
public func onText(_ callback: @escaping (WebSocket, String) async -> ()) {
44-
onText { socket, text in
45-
Task {
46-
await callback(socket, text)
43+
@preconcurrency public func onText(_ callback: @Sendable @escaping (WebSocket, String) async -> ()) {
44+
self.eventLoop.execute {
45+
self.onText { socket, text in
46+
Task {
47+
await callback(socket, text)
48+
}
4749
}
4850
}
4951
}
5052

51-
public func onBinary(_ callback: @escaping (WebSocket, ByteBuffer) async -> ()) {
52-
onBinary { socket, binary in
53-
Task {
54-
await callback(socket, binary)
53+
@preconcurrency public func onBinary(_ callback: @Sendable @escaping (WebSocket, ByteBuffer) async -> ()) {
54+
self.eventLoop.execute {
55+
self.onBinary { socket, binary in
56+
Task {
57+
await callback(socket, binary)
58+
}
5559
}
5660
}
5761
}
5862

59-
public func onPong(_ callback: @escaping (WebSocket) async -> ()) {
60-
onPong { socket in
61-
Task {
62-
await callback(socket)
63+
@preconcurrency public func onPong(_ callback: @Sendable @escaping (WebSocket) async -> ()) {
64+
self.eventLoop.execute {
65+
self.onPong { socket in
66+
Task {
67+
await callback(socket)
68+
}
6369
}
6470
}
6571
}
6672

67-
public func onPing(_ callback: @escaping (WebSocket) async -> ()) {
68-
onPing { socket in
69-
Task {
70-
await callback(socket)
73+
@preconcurrency public func onPing(_ callback: @Sendable @escaping (WebSocket) async -> ()) {
74+
self.eventLoop.execute {
75+
self.onPing { socket in
76+
Task {
77+
await callback(socket)
78+
}
7179
}
7280
}
7381
}
7482

75-
public static func connect(
83+
@preconcurrency public static func connect(
7684
to url: String,
7785
headers: HTTPHeaders = [:],
7886
configuration: WebSocketClient.Configuration = .init(),
7987
on eventLoopGroup: EventLoopGroup,
80-
onUpgrade: @escaping (WebSocket) async -> ()
88+
onUpgrade: @Sendable @escaping (WebSocket) async -> ()
8189
) async throws {
8290
return try await self.connect(
8391
to: url,
@@ -92,12 +100,12 @@ extension WebSocket {
92100
).get()
93101
}
94102

95-
public static func connect(
103+
@preconcurrency public static func connect(
96104
to url: URL,
97105
headers: HTTPHeaders = [:],
98106
configuration: WebSocketClient.Configuration = .init(),
99107
on eventLoopGroup: EventLoopGroup,
100-
onUpgrade: @escaping (WebSocket) async -> ()
108+
onUpgrade: @Sendable @escaping (WebSocket) async -> ()
101109
) async throws {
102110
return try await self.connect(
103111
to: url,
@@ -112,7 +120,7 @@ extension WebSocket {
112120
).get()
113121
}
114122

115-
public static func connect(
123+
@preconcurrency public static func connect(
116124
scheme: String = "ws",
117125
host: String,
118126
port: Int = 80,
@@ -121,7 +129,7 @@ extension WebSocket {
121129
headers: HTTPHeaders = [:],
122130
configuration: WebSocketClient.Configuration = .init(),
123131
on eventLoopGroup: EventLoopGroup,
124-
onUpgrade: @escaping (WebSocket) async -> ()
132+
onUpgrade: @Sendable @escaping (WebSocket) async -> ()
125133
) async throws {
126134
return try await self.connect(
127135
scheme: scheme,

Sources/WebSocketKit/Exports.swift

-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoopGroup
77
@_documentation(visibility: internal) @_exported import struct NIOCore.EventLoopPromise
88
@_documentation(visibility: internal) @_exported import class NIOCore.EventLoopFuture
9-
109
@_documentation(visibility: internal) @_exported import struct NIOHTTP1.HTTPHeaders
11-
1210
@_documentation(visibility: internal) @_exported import struct Foundation.URL
1311

1412
#else
@@ -19,9 +17,7 @@
1917
@_exported import protocol NIOCore.EventLoopGroup
2018
@_exported import struct NIOCore.EventLoopPromise
2119
@_exported import class NIOCore.EventLoopFuture
22-
2320
@_exported import struct NIOHTTP1.HTTPHeaders
24-
2521
@_exported import struct Foundation.URL
2622

2723
#endif

Sources/WebSocketKit/WebSocket+Connect.swift

+10-5
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ extension WebSocket {
1212
/// - eventLoopGroup: Event loop group to be used by the WebSocket client.
1313
/// - onUpgrade: An escaping closure to be executed after the upgrade is completed by `NIOWebSocketClientUpgrader`.
1414
/// - Returns: An future which completes when the connection to the WebSocket server is established.
15+
@preconcurrency
1516
public static func connect(
1617
to url: String,
1718
headers: HTTPHeaders = [:],
1819
configuration: WebSocketClient.Configuration = .init(),
1920
on eventLoopGroup: EventLoopGroup,
20-
onUpgrade: @escaping (WebSocket) -> ()
21+
onUpgrade: @Sendable @escaping (WebSocket) -> ()
2122
) -> EventLoopFuture<Void> {
2223
guard let url = URL(string: url) else {
2324
return eventLoopGroup.any().makeFailedFuture(WebSocketClient.Error.invalidURL)
@@ -40,12 +41,13 @@ extension WebSocket {
4041
/// - eventLoopGroup: Event loop group to be used by the WebSocket client.
4142
/// - onUpgrade: An escaping closure to be executed after the upgrade is completed by `NIOWebSocketClientUpgrader`.
4243
/// - Returns: An future which completes when the connection to the WebSocket server is established.
44+
@preconcurrency
4345
public static func connect(
4446
to url: URL,
4547
headers: HTTPHeaders = [:],
4648
configuration: WebSocketClient.Configuration = .init(),
4749
on eventLoopGroup: EventLoopGroup,
48-
onUpgrade: @escaping (WebSocket) -> ()
50+
onUpgrade: @Sendable @escaping (WebSocket) -> ()
4951
) -> EventLoopFuture<Void> {
5052
let scheme = url.scheme ?? "ws"
5153
return self.connect(
@@ -74,6 +76,7 @@ extension WebSocket {
7476
/// - eventLoopGroup: Event loop group to be used by the WebSocket client.
7577
/// - onUpgrade: An escaping closure to be executed after the upgrade is completed by `NIOWebSocketClientUpgrader`.
7678
/// - Returns: An future which completes when the connection to the WebSocket server is established.
79+
@preconcurrency
7780
public static func connect(
7881
scheme: String = "ws",
7982
host: String,
@@ -83,7 +86,7 @@ extension WebSocket {
8386
headers: HTTPHeaders = [:],
8487
configuration: WebSocketClient.Configuration = .init(),
8588
on eventLoopGroup: EventLoopGroup,
86-
onUpgrade: @escaping (WebSocket) -> ()
89+
onUpgrade: @Sendable @escaping (WebSocket) -> ()
8790
) -> EventLoopFuture<Void> {
8891
return WebSocketClient(
8992
eventLoopGroupProvider: .shared(eventLoopGroup),
@@ -116,6 +119,7 @@ extension WebSocket {
116119
/// - eventLoopGroup: Event loop group to be used by the WebSocket client.
117120
/// - onUpgrade: An escaping closure to be executed after the upgrade is completed by `NIOWebSocketClientUpgrader`.
118121
/// - Returns: An future which completes when the connection to the origin server is established.
122+
@preconcurrency
119123
public static func connect(
120124
scheme: String = "ws",
121125
host: String,
@@ -129,7 +133,7 @@ extension WebSocket {
129133
proxyConnectDeadline: NIODeadline = NIODeadline.distantFuture,
130134
configuration: WebSocketClient.Configuration = .init(),
131135
on eventLoopGroup: EventLoopGroup,
132-
onUpgrade: @escaping (WebSocket) -> ()
136+
onUpgrade: @Sendable @escaping (WebSocket) -> ()
133137
) -> EventLoopFuture<Void> {
134138
return WebSocketClient(
135139
eventLoopGroupProvider: .shared(eventLoopGroup),
@@ -162,6 +166,7 @@ extension WebSocket {
162166
/// - eventLoopGroup: Event loop group to be used by the WebSocket client.
163167
/// - onUpgrade: An escaping closure to be executed after the upgrade is completed by `NIOWebSocketClientUpgrader`.
164168
/// - Returns: An future which completes when the connection to the origin server is established.
169+
@preconcurrency
165170
public static func connect(
166171
to url: String,
167172
headers: HTTPHeaders = [:],
@@ -171,7 +176,7 @@ extension WebSocket {
171176
proxyConnectDeadline: NIODeadline = NIODeadline.distantFuture,
172177
configuration: WebSocketClient.Configuration = .init(),
173178
on eventLoopGroup: EventLoopGroup,
174-
onUpgrade: @escaping (WebSocket) -> ()
179+
onUpgrade: @Sendable @escaping (WebSocket) -> ()
175180
) -> EventLoopFuture<Void> {
176181
guard let url = URL(string: url) else {
177182
return eventLoopGroup.any().makeFailedFuture(WebSocketClient.Error.invalidURL)

0 commit comments

Comments
 (0)