Skip to content

Commit 999212d

Browse files
authored
feat(delay): add Delay queue (#61)
A Queue[T comparable] implementation where each element becomes dequeuable at a deadline computed by a caller-supplied function at Offer time. API: - NewDelay(elems, deadlineFunc, opts...) — matches NewPriority's constructor-time func pattern. - Queue[T] surface plus GetWait. - MarshalJSON serialises in deadline order. Impl: - Min-heap by deadline, written directly on []delayed[T] rather than via container/heap. The container/heap Push/Pop signatures require boxing each element into `any`, which pprof showed as the dominant allocation source (>75% of allocations, ~50% of CPU spent in GC). Typed push/pop/up/down keeps Get+Offer steady-state at 0 allocs. - GetWait uses sync.Cond + time.AfterFunc so "head is due" and "state changed" compose under a single Wait, matching the cond-var idiom used by Blocking. - Peek returns the head regardless of whether its deadline has passed (matches java.util.concurrent.DelayQueue.peek). - Panics on nil deadlineFunc or negative capacity, consistent with NewPriority. README: new Delay Queue section with usage example; benchmark table extended.
1 parent 5bacf9d commit 999212d

4 files changed

Lines changed: 955 additions & 3 deletions

File tree

README.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
---
1919

20-
The `queue` package provides thread-safe generic implementations in Go for the following data structures: `BlockingQueue`, `PriorityQueue`, `CircularQueue` and `Linked Queue`.
20+
The `queue` package provides thread-safe generic implementations in Go for the following data structures: `BlockingQueue`, `PriorityQueue`, `CircularQueue`, `Linked Queue` and `DelayQueue`.
2121

2222
A queue is a sequence of entities that is open at both ends where the elements are
2323
added (enqueued) to the tail (back) of the queue and removed (dequeued) from the head (front) of the queue.
@@ -36,6 +36,7 @@ Benchmarks and Example tests can be found in this package.
3636
* [Priority Queue](#priority-queue)
3737
* [Circular Queue](#circular-queue)
3838
* [Linked Queue](#linked-queue)
39+
* [Delay Queue](#delay-queue)
3940
* [Benchmarks](#benchmarks-)
4041
<!-- TOC -->
4142

@@ -267,6 +268,50 @@ func main() {
267268
}
268269
```
269270

271+
### Delay Queue
272+
273+
A `Delay` queue is a priority queue where each element becomes dequeuable at a deadline computed by a caller-supplied function at `Offer` time. `Get` returns `ErrNoElementsAvailable` until the head's deadline has passed; `GetWait` sleeps until it does. Useful for timers, retry scheduling, and TTL expiry.
274+
275+
```go
276+
package main
277+
278+
import (
279+
"fmt"
280+
"time"
281+
282+
"github.com/adrianbrad/queue"
283+
)
284+
285+
type task struct {
286+
id int
287+
runAt time.Time
288+
}
289+
290+
func main() {
291+
now := time.Now()
292+
293+
delayQueue := queue.NewDelay(
294+
[]task{
295+
{id: 1, runAt: now.Add(20 * time.Millisecond)},
296+
{id: 2, runAt: now.Add(5 * time.Millisecond)},
297+
},
298+
func(t task) time.Time { return t.runAt },
299+
)
300+
301+
size := delayQueue.Size()
302+
fmt.Println(size) // 2
303+
304+
// Non-blocking: not due yet.
305+
if _, err := delayQueue.Get(); err != nil {
306+
// err == queue.ErrNoElementsAvailable
307+
}
308+
309+
// Blocking: returns as soon as the head's deadline passes.
310+
next := delayQueue.GetWait()
311+
fmt.Printf("next: %d\n", next.id) // next: 2
312+
}
313+
```
314+
270315
## Benchmarks
271316

272317
Run locally with `go test -bench=. -benchmem -benchtime=3s -count=3`. Reported numbers are per-operation timings and allocations; absolute values vary by hardware, but the shape (zero-alloc reads everywhere, zero-alloc offer/get for Circular and Linked) should be stable.
@@ -284,4 +329,7 @@ BenchmarkLinkedQueue/Offer 22.7 ns/op 16 B/op 1 allocs/o
284329
BenchmarkPriorityQueue/Peek 3.9 ns/op 0 B/op 0 allocs/op
285330
BenchmarkPriorityQueue/Get_Offer 18.1 ns/op 0 B/op 0 allocs/op
286331
BenchmarkPriorityQueue/Offer 17.1 ns/op 48 B/op 0 allocs/op
332+
BenchmarkDelayQueue/Peek 4.1 ns/op 0 B/op 0 allocs/op
333+
BenchmarkDelayQueue/Get_Offer 52.4 ns/op 0 B/op 0 allocs/op
334+
BenchmarkDelayQueue/Offer 63.5 ns/op 315 B/op 0 allocs/op
287335
```

0 commit comments

Comments
 (0)