WaitLock tries to acquire all locks within a specified waiting period. It will attempt to acquire locks with exponential backoff if any lock is held. If lock acquisition fails after the wait timeout, all acquired locks are released. Parameters: - ctx: The context for managing the wait request lifecy
(ctx context.Context, lockTimeout, waitTimeout time.Duration)
| 238 | // - waitTimeout: The maximum time to wait for all locks to become available. |
| 239 | // Returns an error if all locks could not be acquired within the wait timeout. |
| 240 | func (m *MultiLocker) WaitLock(ctx context.Context, lockTimeout, waitTimeout time.Duration) error { |
| 241 | if err := ctx.Err(); err != nil { |
| 242 | return fmt.Errorf("lock wait cancelled: %w", err) |
| 243 | } |
| 244 | |
| 245 | deadline := time.Now().Add(waitTimeout) |
| 246 | |
| 247 | for time.Now().Before(deadline) { |
| 248 | err := m.Lock(ctx, lockTimeout) |
| 249 | if err == nil { |
| 250 | return nil |
| 251 | } |
| 252 | if !errors.Is(err, ErrLockHeld) { |
| 253 | if ctxErr := ctx.Err(); ctxErr != nil { |
| 254 | return fmt.Errorf("lock wait cancelled: %w", ctxErr) |
| 255 | } |
| 256 | return err |
| 257 | } |
| 258 | if err := sleepWithJitter(ctx, deadline); err != nil { |
| 259 | return fmt.Errorf("lock wait cancelled: %w", err) |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | return &lockError{ |
| 264 | err: ErrLockWaitTimeout, |
| 265 | msg: "failed to acquire all locks within the wait timeout", |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | // Unlock releases all locks in reverse order of acquisition. |
| 270 | // Releasing in reverse order is a best practice for multi-lock scenarios. |