MCPcopy
hub / github.com/redis/go-redis / AwaitAndTransition

Method AwaitAndTransition

internal/pool/conn_state.go:216–281  ·  view source on GitHub ↗

AwaitAndTransition waits for the connection to reach one of the valid states, then atomically transitions to the target state. Returns the current state after the transition attempt and an error if the operation failed. The returned state is the CURRENT state (after the attempt), not the previous st

(
	ctx context.Context,
	validFromStates []ConnState,
	targetState ConnState,
)

Source from the content-addressed store, hash-verified

214// - If already in a valid state, this is very fast (no allocation, no waiting)
215// - If waiting is required, allocates one waiter struct and one channel
216func (sm *ConnStateMachine) AwaitAndTransition(
217 ctx context.Context,
218 validFromStates []ConnState,
219 targetState ConnState,
220) (ConnState, error) {
221 // Fast path: try immediate transition with CAS to prevent race conditions
222 // BUT: only if there are no waiters in the queue (to maintain FIFO ordering)
223 if sm.waiterCount.Load() == 0 {
224 for _, fromState := range validFromStates {
225 // Check if we're already in target state
226 if fromState == targetState && sm.GetState() == targetState {
227 return targetState, nil
228 }
229
230 // Try to atomically swap from fromState to targetState
231 if sm.state.CompareAndSwap(uint32(fromState), uint32(targetState)) {
232 // Success! We transitioned atomically
233 sm.notifyWaiters()
234 return targetState, nil
235 }
236 }
237 }
238
239 // Fast path failed - check if we should wait or fail
240 currentState := sm.GetState()
241
242 // Check if closed
243 if currentState == StateClosed {
244 return currentState, ErrStateMachineClosed
245 }
246
247 // Slow path: need to wait for state change
248 // Create waiter with valid states map for fast lookup
249 validStatesMap := make(map[ConnState]struct{}, len(validFromStates))
250 for _, s := range validFromStates {
251 validStatesMap[s] = struct{}{}
252 }
253
254 w := &waiter{
255 validStates: validStatesMap,
256 targetState: targetState,
257 done: make(chan error, 1), // Buffered to avoid goroutine leak
258 }
259
260 // Add to FIFO queue
261 sm.mu.Lock()
262 elem := sm.waiters.PushBack(w)
263 sm.waiterCount.Add(1)
264 sm.mu.Unlock()
265
266 // Wait for state change or timeout
267 select {
268 case <-ctx.Done():
269 // Timeout or cancellation - remove from queue
270 sm.mu.Lock()
271 sm.waiters.Remove(elem)
272 sm.waiterCount.Add(-1)
273 sm.mu.Unlock()

Calls 6

GetStateMethod · 0.95
notifyWaitersMethod · 0.95
AddMethod · 0.65
RemoveMethod · 0.65
ErrMethod · 0.65
LoadMethod · 0.45