| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.21 |
| |
| package quic |
| |
| import "context" |
| |
| // A queue is an unbounded queue of some item (new connections and streams). |
| type queue[T any] struct { |
| // The gate condition is set if the queue is non-empty or closed. |
| gate gate |
| err error |
| q []T |
| } |
| |
| func newQueue[T any]() queue[T] { |
| return queue[T]{gate: newGate()} |
| } |
| |
| // close closes the queue, causing pending and future pop operations |
| // to return immediately with err. |
| func (q *queue[T]) close(err error) { |
| q.gate.lock() |
| defer q.unlock() |
| if q.err == nil { |
| q.err = err |
| } |
| } |
| |
| // put appends an item to the queue. |
| // It returns true if the item was added, false if the queue is closed. |
| func (q *queue[T]) put(v T) bool { |
| q.gate.lock() |
| defer q.unlock() |
| if q.err != nil { |
| return false |
| } |
| q.q = append(q.q, v) |
| return true |
| } |
| |
| // get removes the first item from the queue, blocking until ctx is done, an item is available, |
| // or the queue is closed. |
| func (q *queue[T]) get(ctx context.Context) (T, error) { |
| return q.getWithHooks(ctx, nil) |
| } |
| |
| // getWithHooks is get, but uses testHooks for locking when non-nil. |
| // This is a bit of an layer violation, but a simplification overall. |
| func (q *queue[T]) getWithHooks(ctx context.Context, testHooks connTestHooks) (T, error) { |
| var zero T |
| var err error |
| if testHooks != nil { |
| err = testHooks.waitAndLockGate(ctx, &q.gate) |
| } else { |
| err = q.gate.waitAndLockContext(ctx) |
| } |
| if err != nil { |
| return zero, err |
| } |
| defer q.unlock() |
| if q.err != nil { |
| return zero, q.err |
| } |
| v := q.q[0] |
| copy(q.q[:], q.q[1:]) |
| q.q[len(q.q)-1] = zero |
| q.q = q.q[:len(q.q)-1] |
| return v, nil |
| } |
| |
| func (q *queue[T]) unlock() { |
| q.gate.unlock(q.err != nil || len(q.q) > 0) |
| } |