@@ -19,7 +19,7 @@ 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 : Sendable {
2323 let logger : Logger
2424
2525 let request : HTTPClientRequest . Prepared
@@ -28,8 +28,8 @@ import NIOSSL
2828 let preferredEventLoop : EventLoop
2929 let requestOptions : RequestOptions
3030
31- private let stateLock = NIOLock ( )
32- private var state : StateMachine
31+ // TODO: Rewrite using `NIOLoopBound` to avoid the unnecessary lock
32+ private let state : NIOLockedValueBox < StateMachine >
3333
3434 init (
3535 request: HTTPClientRequest . Prepared ,
@@ -44,7 +44,7 @@ import NIOSSL
4444 self . logger = logger
4545 self . connectionDeadline = connectionDeadline
4646 self . preferredEventLoop = preferredEventLoop
47- self . state = StateMachine ( responseContinuation)
47+ self . state = NIOLockedValueBox ( StateMachine ( responseContinuation) )
4848 }
4949
5050 func cancel( ) {
@@ -56,8 +56,8 @@ import NIOSSL
5656 private func writeOnceAndOneTimeOnly( byteBuffer: ByteBuffer ) {
5757 // This method is synchronously invoked after sending the request head. For this reason we
5858 // can make a number of assumptions, how the state machine will react.
59- let writeAction = self . stateLock . withLock {
60- self . state. writeNextRequestPart ( )
59+ let writeAction = self . state . withLockedValue { state in
60+ state. writeNextRequestPart ( )
6161 }
6262
6363 switch writeAction {
@@ -99,30 +99,33 @@ import NIOSSL
9999
100100 struct BreakTheWriteLoopError : Swift . Error { }
101101
102+ // FIXME: Refactor this to not use `self.state.unsafe`.
102103 private func writeRequestBodyPart( _ part: ByteBuffer ) async throws {
103- self . stateLock . lock ( )
104- switch self . state. writeNextRequestPart ( ) {
104+ self . state . unsafe . lock ( )
105+ switch self . state. unsafe . withValueAssumingLockIsAcquired ( { state in state . writeNextRequestPart ( ) } ) {
105106 case . writeAndContinue( let executor) :
106- self . stateLock . unlock ( )
107+ self . state . unsafe . unlock ( )
107108 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
108109
109110 case . writeAndWait( let executor) :
110111 try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < Void , Error > ) in
111- self . state. waitForRequestBodyDemand ( continuation: continuation)
112- self . stateLock. unlock ( )
112+ self . state. unsafe. withValueAssumingLockIsAcquired ( { state in
113+ state. waitForRequestBodyDemand ( continuation: continuation)
114+ } )
115+ self . state. unsafe. unlock ( )
113116
114117 executor. writeRequestBodyPart ( . byteBuffer( part) , request: self , promise: nil )
115118 }
116119
117120 case . fail:
118- self . stateLock . unlock ( )
121+ self . state . unsafe . unlock ( )
119122 throw BreakTheWriteLoopError ( )
120123 }
121124 }
122125
123126 private func requestBodyStreamFinished( ) {
124- let finishAction = self . stateLock . withLock {
125- self . state. finishRequestBodyStream ( )
127+ let finishAction = self . state . withLockedValue { state in
128+ state. finishRequestBodyStream ( )
126129 }
127130
128131 switch finishAction {
@@ -150,8 +153,8 @@ extension Transaction: HTTPSchedulableRequest {
150153 var requiredEventLoop : EventLoop ? { nil }
151154
152155 func requestWasQueued( _ scheduler: HTTPRequestScheduler ) {
153- self . stateLock . withLock {
154- self . state. requestWasQueued ( scheduler)
156+ self . state . withLockedValue { state in
157+ state. requestWasQueued ( scheduler)
155158 }
156159 }
157160}
@@ -165,8 +168,8 @@ extension Transaction: HTTPExecutableRequest {
165168 // MARK: Request
166169
167170 func willExecuteRequest( _ executor: HTTPRequestExecutor ) {
168- let action = self . stateLock . withLock {
169- self . state. willExecuteRequest ( executor)
171+ let action = self . state . withLockedValue { state in
172+ state. willExecuteRequest ( executor)
170173 }
171174
172175 switch action {
@@ -183,8 +186,8 @@ extension Transaction: HTTPExecutableRequest {
183186 func requestHeadSent( ) { }
184187
185188 func resumeRequestBodyStream( ) {
186- let action = self . stateLock . withLock {
187- self . state. resumeRequestBodyStream ( )
189+ let action = self . state . withLockedValue { state in
190+ state. resumeRequestBodyStream ( )
188191 }
189192
190193 switch action {
@@ -214,16 +217,16 @@ extension Transaction: HTTPExecutableRequest {
214217 }
215218
216219 func pauseRequestBodyStream( ) {
217- self . stateLock . withLock {
218- self . state. pauseRequestBodyStream ( )
220+ self . state . withLockedValue { state in
221+ state. pauseRequestBodyStream ( )
219222 }
220223 }
221224
222225 // MARK: Response
223226
224227 func receiveResponseHead( _ head: HTTPResponseHead ) {
225- let action = self . stateLock . withLock {
226- self . state. receiveResponseHead ( head, delegate: self )
228+ let action = self . state . withLockedValue { state in
229+ state. receiveResponseHead ( head, delegate: self )
227230 }
228231
229232 switch action {
@@ -243,8 +246,8 @@ extension Transaction: HTTPExecutableRequest {
243246 }
244247
245248 func receiveResponseBodyParts( _ buffer: CircularBuffer < ByteBuffer > ) {
246- let action = self . stateLock . withLock {
247- self . state. receiveResponseBodyParts ( buffer)
249+ let action = self . state . withLockedValue { state in
250+ state. receiveResponseBodyParts ( buffer)
248251 }
249252 switch action {
250253 case . none:
@@ -260,8 +263,8 @@ extension Transaction: HTTPExecutableRequest {
260263 }
261264
262265 func succeedRequest( _ buffer: CircularBuffer < ByteBuffer > ? ) {
263- let succeedAction = self . stateLock . withLock {
264- self . state. succeedRequest ( buffer)
266+ let succeedAction = self . state . withLockedValue { state in
267+ state. succeedRequest ( buffer)
265268 }
266269 switch succeedAction {
267270 case . finishResponseStream( let source, let finalResponse) :
@@ -276,8 +279,8 @@ extension Transaction: HTTPExecutableRequest {
276279 }
277280
278281 func fail( _ error: Error ) {
279- let action = self . stateLock . withLock {
280- self . state. fail ( error)
282+ let action = self . state . withLockedValue { state in
283+ state. fail ( error)
281284 }
282285 self . performFailAction ( action)
283286 }
@@ -304,8 +307,8 @@ extension Transaction: HTTPExecutableRequest {
304307 }
305308
306309 func deadlineExceeded( ) {
307- let action = self . stateLock . withLock {
308- self . state. deadlineExceeded ( )
310+ let action = self . state . withLockedValue { state in
311+ state. deadlineExceeded ( )
309312 }
310313 self . performDeadlineExceededAction ( action)
311314 }
@@ -329,8 +332,8 @@ extension Transaction: HTTPExecutableRequest {
329332extension Transaction : NIOAsyncSequenceProducerDelegate {
330333 @usableFromInline
331334 func produceMore( ) {
332- let action = self . stateLock . withLock {
333- self . state. produceMore ( )
335+ let action = self . state . withLockedValue { state in
336+ state. produceMore ( )
334337 }
335338 switch action {
336339 case . none:
0 commit comments