@@ -19,7 +19,9 @@ import NIOHTTP1
1919import NIOSSL
2020
2121@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
22- @usableFromInline final class Transaction : @unchecked Sendable {
22+ @usableFromInline final class Transaction :
23+ // until NIOLockedValueBox learns `sending` because StateMachine cannot be Sendable
24+ @unchecked Sendable {
2325 let logger : Logger
2426
2527 let request : HTTPClientRequest . Prepared
@@ -28,8 +30,7 @@ import NIOSSL
2830 let preferredEventLoop : EventLoop
2931 let requestOptions : RequestOptions
3032
31- private let stateLock = NIOLock ( )
32- private var state : StateMachine
33+ private let state : NIOLockedValueBox < StateMachine >
3334
3435 init (
3536 request: HTTPClientRequest . Prepared ,
@@ -44,7 +45,7 @@ import NIOSSL
4445 self . logger = logger
4546 self . connectionDeadline = connectionDeadline
4647 self . preferredEventLoop = preferredEventLoop
47- self . state = StateMachine ( responseContinuation)
48+ self . state = NIOLockedValueBox ( StateMachine ( responseContinuation) )
4849 }
4950
5051 func cancel( ) {
@@ -56,8 +57,8 @@ import NIOSSL
5657 private func writeOnceAndOneTimeOnly( byteBuffer: ByteBuffer ) {
5758 // This method is synchronously invoked after sending the request head. For this reason we
5859 // can make a number of assumptions, how the state machine will react.
59- let writeAction = self . stateLock . withLock {
60- self . state. writeNextRequestPart ( )
60+ let writeAction = self . state . withLockedValue { state in
61+ state. writeNextRequestPart ( )
6162 }
6263
6364 switch writeAction {
@@ -99,30 +100,33 @@ import NIOSSL
99100
100101 struct BreakTheWriteLoopError : Swift . Error { }
101102
103+ // FIXME: Refactor this to not use `self.state.unsafe`.
102104 private func writeRequestBodyPart( _ part: ByteBuffer ) async throws {
103- self . stateLock . lock ( )
104- switch self . state. writeNextRequestPart ( ) {
105+ self . state . unsafe . lock ( )
106+ switch self . state. unsafe . withValueAssumingLockIsAcquired ( { state in state . writeNextRequestPart ( ) } ) {
105107 case . writeAndContinue( let executor) :
106- self . stateLock . unlock ( )
108+ self . state . unsafe . unlock ( )
107109 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
108110
109111 case . writeAndWait( let executor) :
110112 try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < Void , Error > ) in
111- self . state. waitForRequestBodyDemand ( continuation: continuation)
112- self . stateLock. unlock ( )
113+ self . state. unsafe. withValueAssumingLockIsAcquired ( { state in
114+ state. waitForRequestBodyDemand ( continuation: continuation)
115+ } )
116+ self . state. unsafe. unlock ( )
113117
114118 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
115119 }
116120
117121 case . fail:
118- self . stateLock . unlock ( )
122+ self . state . unsafe . unlock ( )
119123 throw BreakTheWriteLoopError ( )
120124 }
121125 }
122126
123127 private func requestBodyStreamFinished( ) {
124- let finishAction = self . stateLock . withLock {
125- self . state. finishRequestBodyStream ( )
128+ let finishAction = self . state . withLockedValue { state in
129+ state. finishRequestBodyStream ( )
126130 }
127131
128132 switch finishAction {
@@ -150,8 +154,8 @@ extension Transaction: HTTPSchedulableRequest {
150154 var requiredEventLoop : EventLoop ? { nil }
151155
152156 func requestWasQueued( _ scheduler: HTTPRequestScheduler ) {
153- self . stateLock . withLock {
154- self . state. requestWasQueued ( scheduler)
157+ self . state . withLockedValue { state in
158+ state. requestWasQueued ( scheduler)
155159 }
156160 }
157161}
@@ -165,8 +169,8 @@ extension Transaction: HTTPExecutableRequest {
165169 // MARK: Request
166170
167171 func willExecuteRequest( _ executor: HTTPRequestExecutor ) {
168- let action = self . stateLock . withLock {
169- self . state. willExecuteRequest ( executor)
172+ let action = self . state . withLockedValue { state in
173+ state. willExecuteRequest ( executor)
170174 }
171175
172176 switch action {
@@ -183,8 +187,8 @@ extension Transaction: HTTPExecutableRequest {
183187 func requestHeadSent( ) { }
184188
185189 func resumeRequestBodyStream( ) {
186- let action = self . stateLock . withLock {
187- self . state. resumeRequestBodyStream ( )
190+ let action = self . state . withLockedValue { state in
191+ state. resumeRequestBodyStream ( )
188192 }
189193
190194 switch action {
@@ -214,16 +218,16 @@ extension Transaction: HTTPExecutableRequest {
214218 }
215219
216220 func pauseRequestBodyStream( ) {
217- self . stateLock . withLock {
218- self . state. pauseRequestBodyStream ( )
221+ self . state . withLockedValue { state in
222+ state. pauseRequestBodyStream ( )
219223 }
220224 }
221225
222226 // MARK: Response
223227
224228 func receiveResponseHead( _ head: HTTPResponseHead ) {
225- let action = self . stateLock . withLock {
226- self . state. receiveResponseHead ( head, delegate: self )
229+ let action = self . state . withLockedValue { state in
230+ state. receiveResponseHead ( head, delegate: self )
227231 }
228232
229233 switch action {
@@ -243,8 +247,8 @@ extension Transaction: HTTPExecutableRequest {
243247 }
244248
245249 func receiveResponseBodyParts( _ buffer: CircularBuffer < ByteBuffer > ) {
246- let action = self . stateLock . withLock {
247- self . state. receiveResponseBodyParts ( buffer)
250+ let action = self . state . withLockedValue { state in
251+ state. receiveResponseBodyParts ( buffer)
248252 }
249253 switch action {
250254 case . none:
@@ -260,8 +264,8 @@ extension Transaction: HTTPExecutableRequest {
260264 }
261265
262266 func succeedRequest( _ buffer: CircularBuffer < ByteBuffer > ? ) {
263- let succeedAction = self . stateLock . withLock {
264- self . state. succeedRequest ( buffer)
267+ let succeedAction = self . state . withLockedValue { state in
268+ state. succeedRequest ( buffer)
265269 }
266270 switch succeedAction {
267271 case . finishResponseStream( let source, let finalResponse) :
@@ -276,8 +280,8 @@ extension Transaction: HTTPExecutableRequest {
276280 }
277281
278282 func fail( _ error: Error ) {
279- let action = self . stateLock . withLock {
280- self . state. fail ( error)
283+ let action = self . state . withLockedValue { state in
284+ state. fail ( error)
281285 }
282286 self . performFailAction ( action)
283287 }
@@ -304,8 +308,8 @@ extension Transaction: HTTPExecutableRequest {
304308 }
305309
306310 func deadlineExceeded( ) {
307- let action = self . stateLock . withLock {
308- self . state. deadlineExceeded ( )
311+ let action = self . state . withLockedValue { state in
312+ state. deadlineExceeded ( )
309313 }
310314 self . performDeadlineExceededAction ( action)
311315 }
@@ -329,8 +333,8 @@ extension Transaction: HTTPExecutableRequest {
329333extension Transaction : NIOAsyncSequenceProducerDelegate {
330334 @usableFromInline
331335 func produceMore( ) {
332- let action = self . stateLock . withLock {
333- self . state. produceMore ( )
336+ let action = self . state . withLockedValue { state in
337+ state. produceMore ( )
334338 }
335339 switch action {
336340 case . none:
0 commit comments