@@ -19,7 +19,11 @@ 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
23+ final class Transaction :
24+ // until NIOLockedValueBox learns `sending` because StateMachine cannot be Sendable
25+ @unchecked Sendable
26+ {
2327 let logger : Logger
2428
2529 let request : HTTPClientRequest . Prepared
@@ -28,8 +32,7 @@ import NIOSSL
2832 let preferredEventLoop : EventLoop
2933 let requestOptions : RequestOptions
3034
31- private let stateLock = NIOLock ( )
32- private var state : StateMachine
35+ private let state : NIOLockedValueBox < StateMachine >
3336
3437 init (
3538 request: HTTPClientRequest . Prepared ,
@@ -44,7 +47,7 @@ import NIOSSL
4447 self . logger = logger
4548 self . connectionDeadline = connectionDeadline
4649 self . preferredEventLoop = preferredEventLoop
47- self . state = StateMachine ( responseContinuation)
50+ self . state = NIOLockedValueBox ( StateMachine ( responseContinuation) )
4851 }
4952
5053 func cancel( ) {
@@ -56,8 +59,8 @@ import NIOSSL
5659 private func writeOnceAndOneTimeOnly( byteBuffer: ByteBuffer ) {
5760 // This method is synchronously invoked after sending the request head. For this reason we
5861 // can make a number of assumptions, how the state machine will react.
59- let writeAction = self . stateLock . withLock {
60- self . state. writeNextRequestPart ( )
62+ let writeAction = self . state . withLockedValue { state in
63+ state. writeNextRequestPart ( )
6164 }
6265
6366 switch writeAction {
@@ -99,30 +102,33 @@ import NIOSSL
99102
100103 struct BreakTheWriteLoopError : Swift . Error { }
101104
105+ // FIXME: Refactor this to not use `self.state.unsafe`.
102106 private func writeRequestBodyPart( _ part: ByteBuffer ) async throws {
103- self . stateLock . lock ( )
104- switch self . state. writeNextRequestPart ( ) {
107+ self . state . unsafe . lock ( )
108+ switch self . state. unsafe . withValueAssumingLockIsAcquired ( { state in state . writeNextRequestPart ( ) } ) {
105109 case . writeAndContinue( let executor) :
106- self . stateLock . unlock ( )
110+ self . state . unsafe . unlock ( )
107111 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
108112
109113 case . writeAndWait( let executor) :
110114 try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < Void , Error > ) in
111- self . state. waitForRequestBodyDemand ( continuation: continuation)
112- self . stateLock. unlock ( )
115+ self . state. unsafe. withValueAssumingLockIsAcquired ( { state in
116+ state. waitForRequestBodyDemand ( continuation: continuation)
117+ } )
118+ self . state. unsafe. unlock ( )
113119
114120 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
115121 }
116122
117123 case . fail:
118- self . stateLock . unlock ( )
124+ self . state . unsafe . unlock ( )
119125 throw BreakTheWriteLoopError ( )
120126 }
121127 }
122128
123129 private func requestBodyStreamFinished( ) {
124- let finishAction = self . stateLock . withLock {
125- self . state. finishRequestBodyStream ( )
130+ let finishAction = self . state . withLockedValue { state in
131+ state. finishRequestBodyStream ( )
126132 }
127133
128134 switch finishAction {
@@ -150,8 +156,8 @@ extension Transaction: HTTPSchedulableRequest {
150156 var requiredEventLoop : EventLoop ? { nil }
151157
152158 func requestWasQueued( _ scheduler: HTTPRequestScheduler ) {
153- self . stateLock . withLock {
154- self . state. requestWasQueued ( scheduler)
159+ self . state . withLockedValue { state in
160+ state. requestWasQueued ( scheduler)
155161 }
156162 }
157163}
@@ -165,8 +171,8 @@ extension Transaction: HTTPExecutableRequest {
165171 // MARK: Request
166172
167173 func willExecuteRequest( _ executor: HTTPRequestExecutor ) {
168- let action = self . stateLock . withLock {
169- self . state. willExecuteRequest ( executor)
174+ let action = self . state . withLockedValue { state in
175+ state. willExecuteRequest ( executor)
170176 }
171177
172178 switch action {
@@ -183,8 +189,8 @@ extension Transaction: HTTPExecutableRequest {
183189 func requestHeadSent( ) { }
184190
185191 func resumeRequestBodyStream( ) {
186- let action = self . stateLock . withLock {
187- self . state. resumeRequestBodyStream ( )
192+ let action = self . state . withLockedValue { state in
193+ state. resumeRequestBodyStream ( )
188194 }
189195
190196 switch action {
@@ -214,16 +220,16 @@ extension Transaction: HTTPExecutableRequest {
214220 }
215221
216222 func pauseRequestBodyStream( ) {
217- self . stateLock . withLock {
218- self . state. pauseRequestBodyStream ( )
223+ self . state . withLockedValue { state in
224+ state. pauseRequestBodyStream ( )
219225 }
220226 }
221227
222228 // MARK: Response
223229
224230 func receiveResponseHead( _ head: HTTPResponseHead ) {
225- let action = self . stateLock . withLock {
226- self . state. receiveResponseHead ( head, delegate: self )
231+ let action = self . state . withLockedValue { state in
232+ state. receiveResponseHead ( head, delegate: self )
227233 }
228234
229235 switch action {
@@ -243,8 +249,8 @@ extension Transaction: HTTPExecutableRequest {
243249 }
244250
245251 func receiveResponseBodyParts( _ buffer: CircularBuffer < ByteBuffer > ) {
246- let action = self . stateLock . withLock {
247- self . state. receiveResponseBodyParts ( buffer)
252+ let action = self . state . withLockedValue { state in
253+ state. receiveResponseBodyParts ( buffer)
248254 }
249255 switch action {
250256 case . none:
@@ -260,8 +266,8 @@ extension Transaction: HTTPExecutableRequest {
260266 }
261267
262268 func succeedRequest( _ buffer: CircularBuffer < ByteBuffer > ? ) {
263- let succeedAction = self . stateLock . withLock {
264- self . state. succeedRequest ( buffer)
269+ let succeedAction = self . state . withLockedValue { state in
270+ state. succeedRequest ( buffer)
265271 }
266272 switch succeedAction {
267273 case . finishResponseStream( let source, let finalResponse) :
@@ -276,8 +282,8 @@ extension Transaction: HTTPExecutableRequest {
276282 }
277283
278284 func fail( _ error: Error ) {
279- let action = self . stateLock . withLock {
280- self . state. fail ( error)
285+ let action = self . state . withLockedValue { state in
286+ state. fail ( error)
281287 }
282288 self . performFailAction ( action)
283289 }
@@ -304,8 +310,8 @@ extension Transaction: HTTPExecutableRequest {
304310 }
305311
306312 func deadlineExceeded( ) {
307- let action = self . stateLock . withLock {
308- self . state. deadlineExceeded ( )
313+ let action = self . state . withLockedValue { state in
314+ state. deadlineExceeded ( )
309315 }
310316 self . performDeadlineExceededAction ( action)
311317 }
@@ -329,8 +335,8 @@ extension Transaction: HTTPExecutableRequest {
329335extension Transaction : NIOAsyncSequenceProducerDelegate {
330336 @usableFromInline
331337 func produceMore( ) {
332- let action = self . stateLock . withLock {
333- self . state. produceMore ( )
338+ let action = self . state . withLockedValue { state in
339+ state. produceMore ( )
334340 }
335341 switch action {
336342 case . none:
0 commit comments