TryTransition attempts an immediate state transition without waiting. Returns the current state after the transition attempt and an error if the transition failed. The returned state is the CURRENT state (after the attempt), not the previous state. This is faster than AwaitAndTransition when you don
(validFromStates []ConnState, targetState ConnState)
| 169 | // |
| 170 | // Performance: Zero allocations on success path (hot path). |
| 171 | func (sm *ConnStateMachine) TryTransition(validFromStates []ConnState, targetState ConnState) (ConnState, error) { |
| 172 | // Try each valid from state with CAS |
| 173 | // This ensures only ONE goroutine can successfully transition at a time |
| 174 | for _, fromState := range validFromStates { |
| 175 | // Try to atomically swap from fromState to targetState |
| 176 | // If successful, we won the race and can proceed |
| 177 | if sm.state.CompareAndSwap(uint32(fromState), uint32(targetState)) { |
| 178 | // Success! We transitioned atomically |
| 179 | // Hot path optimization: only check for waiters if transition succeeded |
| 180 | // This avoids atomic load on every Get/Put when no waiters exist |
| 181 | if sm.waiterCount.Load() > 0 { |
| 182 | sm.notifyWaiters() |
| 183 | } |
| 184 | return targetState, nil |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // All CAS attempts failed - state is not valid for this transition |
| 189 | // Return the current state so caller can decide what to do |
| 190 | // Note: This error path allocates, but it's the exceptional case |
| 191 | currentState := sm.GetState() |
| 192 | return currentState, fmt.Errorf("%w: cannot transition from %s to %s (valid from: %v)", |
| 193 | ErrInvalidStateTransition, currentState, targetState, validFromStates) |
| 194 | } |
| 195 | |
| 196 | // Transition unconditionally transitions to the target state. |
| 197 | // Use with caution - prefer AwaitAndTransition or TryTransition for safety. |