@@ -21,16 +21,18 @@ extension ValkeyChannelHandler {
21
21
struct StateMachine < Context> : ~ Copyable {
22
22
@usableFromInline
23
23
enum State : ~ Copyable {
24
- case initializing
24
+ case initialized
25
+ case connected( ConnectedState )
25
26
case active( ActiveState )
26
27
case closing( ActiveState )
27
- case closed
28
+ case closed( Error ? )
28
29
29
30
@usableFromInline
30
31
var description : String {
31
32
borrowing get {
32
33
switch self {
33
- case . initializing: " initializing "
34
+ case . initialized: " initialized "
35
+ case . connected: " connected "
34
36
case . active: " active "
35
37
case . closing: " closing "
36
38
case . closed: " closed "
@@ -60,8 +62,21 @@ extension ValkeyChannelHandler {
60
62
}
61
63
}
62
64
65
+ @usableFromInline
66
+ struct ConnectedState {
67
+ let context : Context
68
+ var pendingHelloCommand : PendingCommand
69
+
70
+ func cancel( requestID: Int ) -> PendingCommand ? {
71
+ if pendingHelloCommand. requestID == requestID {
72
+ return pendingHelloCommand
73
+ }
74
+ return nil
75
+ }
76
+ }
77
+
63
78
init ( ) {
64
- self . state = . initializing
79
+ self . state = . initialized
65
80
}
66
81
67
82
private init ( _ state: consuming State ) {
@@ -70,16 +85,20 @@ extension ValkeyChannelHandler {
70
85
71
86
/// handler has become active
72
87
@usableFromInline
73
- mutating func setActive ( context: Context ) {
88
+ mutating func setConnected ( context: Context , pendingHelloCommand : PendingCommand ) {
74
89
switch consume self. state {
75
- case . initializing:
76
- self = . active( . init( context: context, pendingCommands: [ ] ) )
90
+ case . initialized:
91
+ self = . connected(
92
+ . init( context: context, pendingHelloCommand: pendingHelloCommand)
93
+ )
94
+ case . connected:
95
+ preconditionFailure ( " Cannot set connected state when state is connected " )
77
96
case . active:
78
- preconditionFailure ( " Cannot set active state when state is active " )
97
+ preconditionFailure ( " Cannot set connected state when state is active " )
79
98
case . closing:
80
- preconditionFailure ( " Cannot set active state when state is closing " )
99
+ preconditionFailure ( " Cannot set connected state when state is closing " )
81
100
case . closed:
82
- preconditionFailure ( " Cannot set active state when state is closed " )
101
+ preconditionFailure ( " Cannot set connected state when state is closed " )
83
102
}
84
103
}
85
104
@@ -99,17 +118,19 @@ extension ValkeyChannelHandler {
99
118
@usableFromInline
100
119
mutating func sendCommands( _ pendingCommands: some Collection < PendingCommand > ) -> SendCommandAction {
101
120
switch consume self. state {
102
- case . initializing:
103
- preconditionFailure ( " Cannot send command when initializing " )
121
+ case . initialized:
122
+ preconditionFailure ( " Cannot send command when initialized " )
123
+ case . connected:
124
+ preconditionFailure ( " Cannot send command when in connected state " )
104
125
case . active( var state) :
105
126
state. pendingCommands. append ( contentsOf: pendingCommands)
106
127
self = . active( state)
107
128
return . sendCommand( state. context)
108
129
case . closing( let state) :
109
130
self = . closing( state)
110
131
return . throwError( ValkeyClientError ( . connectionClosing) )
111
- case . closed:
112
- self = . closed
132
+ case . closed( let error ) :
133
+ self = . closed( error )
113
134
return . throwError( ValkeyClientError ( . connectionClosed) )
114
135
}
115
136
}
@@ -124,19 +145,29 @@ extension ValkeyChannelHandler {
124
145
@usableFromInline
125
146
enum ReceivedResponseAction {
126
147
case respond( PendingCommand , DeadlineCallbackAction )
127
- case respondAndClose( PendingCommand )
148
+ case respondAndClose( PendingCommand , Error ? )
128
149
case closeWithError( Error )
129
150
}
130
151
131
152
/// handler wants to send a command
132
153
@usableFromInline
133
- mutating func receivedResponse( ) -> ReceivedResponseAction {
154
+ mutating func receivedResponse( token : RESPToken ) -> ReceivedResponseAction {
134
155
switch consume self. state {
135
- case . initializing:
136
- preconditionFailure ( " Cannot send command when initializing " )
156
+ case . initialized:
157
+ preconditionFailure ( " Cannot send command when initialized " )
158
+ case . connected( let state) :
159
+ switch token. value {
160
+ case . bulkError( let message) , . simpleError( let message) :
161
+ let error = ValkeyClientError ( . commandError, message: String ( buffer: message) )
162
+ self = . closed( error)
163
+ return . respondAndClose( state. pendingHelloCommand, error)
164
+ default :
165
+ self = . active( . init( context: state. context, pendingCommands: . init( ) ) )
166
+ return . respond( state. pendingHelloCommand, . cancel)
167
+ }
137
168
case . active( var state) :
138
169
guard let command = state. pendingCommands. popFirst ( ) else {
139
- self = . closed
170
+ self = . closed( nil )
140
171
return . closeWithError( ValkeyClientError ( . unsolicitedToken, message: " Received a token without having sent a command " ) )
141
172
}
142
173
self = . active( state)
@@ -170,14 +201,45 @@ extension ValkeyChannelHandler {
170
201
}
171
202
return . respond( command, deadlineCallback)
172
203
} else {
173
- self = . closed
174
- return . respondAndClose( command)
204
+ self = . closed( nil )
205
+ return . respondAndClose( command, nil )
175
206
}
176
207
case . closed:
177
208
preconditionFailure ( " Cannot receive command on closed connection " )
178
209
}
179
210
}
180
211
212
+ @usableFromInline
213
+ enum WaitOnActiveAction {
214
+ case waitForPromise( EventLoopPromise < RESPToken > )
215
+ case reportedClosed( Error ? )
216
+ case done
217
+ }
218
+
219
+ mutating func waitOnActive( ) -> WaitOnActiveAction {
220
+ switch consume self. state {
221
+ case . initialized:
222
+ preconditionFailure ( " Cannot wait until connection has succeeded " )
223
+ case . connected( let state) :
224
+ switch state. pendingHelloCommand. promise {
225
+ case . nio( let promise) :
226
+ self = . connected( state)
227
+ return . waitForPromise( promise)
228
+ case . swift:
229
+ preconditionFailure ( " Connected state cannot be setup with a Swift continuation " )
230
+ }
231
+ case . active( let state) :
232
+ self = . active( state)
233
+ return . done
234
+ case . closing( let state) :
235
+ self = . closing( state)
236
+ return . done
237
+ case . closed( let error) :
238
+ self = . closed( error)
239
+ return . reportedClosed( error)
240
+ }
241
+ }
242
+
181
243
@usableFromInline
182
244
enum HitDeadlineAction {
183
245
case failPendingCommandsAndClose( Context , Deque < PendingCommand > )
@@ -188,12 +250,20 @@ extension ValkeyChannelHandler {
188
250
@usableFromInline
189
251
mutating func hitDeadline( now: NIODeadline ) -> HitDeadlineAction {
190
252
switch consume self. state {
191
- case . initializing:
192
- preconditionFailure ( " Cannot cancel when initializing " )
253
+ case . initialized:
254
+ preconditionFailure ( " Cannot cancel when initialized " )
255
+ case . connected( let state) :
256
+ if state. pendingHelloCommand. deadline <= now {
257
+ self = . closed( ValkeyClientError ( . timeout) )
258
+ return . failPendingCommandsAndClose( state. context, [ state. pendingHelloCommand] )
259
+ } else {
260
+ self = . connected( state)
261
+ return . reschedule( state. pendingHelloCommand. deadline)
262
+ }
193
263
case . active( let state) :
194
264
if let firstCommand = state. pendingCommands. first {
195
265
if firstCommand. deadline <= now {
196
- self = . closed
266
+ self = . closed( ValkeyClientError ( . timeout ) )
197
267
return . failPendingCommandsAndClose( state. context, state. pendingCommands)
198
268
} else {
199
269
self = . active( state)
@@ -208,14 +278,14 @@ extension ValkeyChannelHandler {
208
278
preconditionFailure ( " Cannot be in closing state with no pending commands " )
209
279
}
210
280
if firstCommand. deadline <= now {
211
- self = . closed
281
+ self = . closed( ValkeyClientError ( . timeout ) )
212
282
return . failPendingCommandsAndClose( state. context, state. pendingCommands)
213
283
} else {
214
284
self = . closing( state)
215
285
return . reschedule( firstCommand. deadline)
216
286
}
217
- case . closed:
218
- self = . closed
287
+ case . closed( let error ) :
288
+ self = . closed( error )
219
289
return . clearCallback
220
290
}
221
291
}
@@ -230,12 +300,24 @@ extension ValkeyChannelHandler {
230
300
@usableFromInline
231
301
mutating func cancel( requestID: Int ) -> CancelAction {
232
302
switch consume self. state {
233
- case . initializing:
234
- preconditionFailure ( " Cannot cancel when initializing " )
303
+ case . initialized:
304
+ preconditionFailure ( " Cannot cancel when initialized " )
305
+ case . connected( let state) :
306
+ if let command = state. cancel ( requestID: requestID) {
307
+ self = . closed( CancellationError ( ) )
308
+ return . failPendingCommandsAndClose(
309
+ state. context,
310
+ cancel: [ command] ,
311
+ closeConnectionDueToCancel: [ ]
312
+ )
313
+ } else {
314
+ self = . connected( state)
315
+ return . doNothing
316
+ }
235
317
case . active( let state) :
236
318
let ( cancel, closeConnectionDueToCancel) = state. cancel ( requestID: requestID)
237
319
if cancel. count > 0 {
238
- self = . closed
320
+ self = . closed( CancellationError ( ) )
239
321
return . failPendingCommandsAndClose(
240
322
state. context,
241
323
cancel: cancel,
@@ -248,7 +330,7 @@ extension ValkeyChannelHandler {
248
330
case . closing( let state) :
249
331
let ( cancel, closeConnectionDueToCancel) = state. cancel ( requestID: requestID)
250
332
if cancel. count > 0 {
251
- self = . closed
333
+ self = . closed( CancellationError ( ) )
252
334
return . failPendingCommandsAndClose(
253
335
state. context,
254
336
cancel: cancel,
@@ -258,8 +340,8 @@ extension ValkeyChannelHandler {
258
340
self = . closing( state)
259
341
return . doNothing
260
342
}
261
- case . closed:
262
- self = . closed
343
+ case . closed( let error ) :
344
+ self = . closed( error )
263
345
return . doNothing
264
346
}
265
347
}
@@ -274,22 +356,25 @@ extension ValkeyChannelHandler {
274
356
@usableFromInline
275
357
mutating func gracefulShutdown( ) -> GracefulShutdownAction {
276
358
switch consume self. state {
277
- case . initializing :
278
- self = . closed
359
+ case . initialized :
360
+ self = . closed( nil )
279
361
return . doNothing
362
+ case . connected( let state) :
363
+ self = . closing( . init( context: state. context, pendingCommands: [ state. pendingHelloCommand] ) )
364
+ return . waitForPendingCommands( state. context)
280
365
case . active( let state) :
281
366
if state. pendingCommands. count > 0 {
282
367
self = . closing( . init( context: state. context, pendingCommands: state. pendingCommands) )
283
368
return . waitForPendingCommands( state. context)
284
369
} else {
285
- self = . closed
370
+ self = . closed( nil )
286
371
return . closeConnection( state. context)
287
372
}
288
373
case . closing( let state) :
289
374
self = . closing( state)
290
375
return . doNothing
291
- case . closed:
292
- self = . closed
376
+ case . closed( let error ) :
377
+ self = . closed( error )
293
378
return . doNothing
294
379
}
295
380
}
@@ -303,17 +388,20 @@ extension ValkeyChannelHandler {
303
388
@usableFromInline
304
389
mutating func close( ) -> CloseAction {
305
390
switch consume self. state {
306
- case . initializing :
307
- self = . closed
391
+ case . initialized :
392
+ self = . closed( nil )
308
393
return . doNothing
394
+ case . connected( let state) :
395
+ self = . closed( nil )
396
+ return . failPendingCommandsAndClose( state. context, [ state. pendingHelloCommand] )
309
397
case . active( let state) :
310
- self = . closed
398
+ self = . closed( nil )
311
399
return . failPendingCommandsAndClose( state. context, state. pendingCommands)
312
400
case . closing( let state) :
313
- self = . closed
401
+ self = . closed( nil )
314
402
return . failPendingCommandsAndClose( state. context, state. pendingCommands)
315
- case . closed:
316
- self = . closed
403
+ case . closed( let error ) :
404
+ self = . closed( error )
317
405
return . doNothing
318
406
}
319
407
}
@@ -328,23 +416,30 @@ extension ValkeyChannelHandler {
328
416
@usableFromInline
329
417
mutating func setClosed( ) -> SetClosedAction {
330
418
switch consume self. state {
331
- case . initializing :
332
- self = . closed
419
+ case . initialized :
420
+ self = . closed( nil )
333
421
return . doNothing
422
+ case . connected( let state) :
423
+ self = . closed( nil )
424
+ return . failPendingCommandsAndSubscriptions( [ state. pendingHelloCommand] )
334
425
case . active( let state) :
335
- self = . closed
426
+ self = . closed( nil )
336
427
return . failPendingCommandsAndSubscriptions( state. pendingCommands)
337
428
case . closing( let state) :
338
- self = . closed
429
+ self = . closed( nil )
339
430
return . failPendingCommandsAndSubscriptions( state. pendingCommands)
340
- case . closed:
341
- self = . closed
431
+ case . closed( let error ) :
432
+ self = . closed( error )
342
433
return . doNothing
343
434
}
344
435
}
345
436
346
- private static var initializing : Self {
347
- StateMachine ( . initializing)
437
+ private static var initialized : Self {
438
+ StateMachine ( . initialized)
439
+ }
440
+
441
+ private static func connected( _ state: ConnectedState ) -> Self {
442
+ StateMachine ( . connected( state) )
348
443
}
349
444
350
445
private static func active( _ state: ActiveState ) -> Self {
@@ -355,8 +450,8 @@ extension ValkeyChannelHandler {
355
450
StateMachine ( . closing( state) )
356
451
}
357
452
358
- private static var closed : Self {
359
- StateMachine ( . closed)
453
+ private static func closed( _ error : Error ? ) -> Self {
454
+ StateMachine ( . closed( error ) )
360
455
}
361
456
}
362
457
}
0 commit comments