You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The `queue` package provides thread-safe generic implementations in Go for the following data structures: `BlockingQueue`, `PriorityQueue`, `CircularQueue`, `Linked Queue` and `DelayQueue`.
20
+
Thread-safe, generic FIFO, priority, circular, linked, and delay queues for Go.
21
21
22
-
A queue is a sequence of entities that is open at both ends where the elements are
23
-
added (enqueued) to the tail (back) of the queue and removed (dequeued) from the head (front) of the queue.
22
+
## Features
24
23
25
-
The implementations are designed to be easy-to-use and provide a consistent API, satisfying the `Queue` interface provided by this package. .
24
+
- Five queue flavours behind one `Queue[T comparable]` interface, so you can swap implementations without changing call sites.
25
+
- Generic types with no reflection; zero third-party dependencies.
26
+
- Steady-state zero-alloc reads on every queue and zero-alloc offer/get on `Circular`, `Linked`, `Priority`, and `Delay`.
27
+
- Blocking variants (`OfferWait`, `GetWait`, `PeekWait`) for producer/consumer workloads.
28
+
-`Delay` queue for timers, retry scheduling, and TTL expiry.
29
+
- 100% test coverage and race-tested in CI.
26
30
27
-
Benchmarks and Example tests can be found in this package.
31
+
Full API reference at **[pkg.go.dev/github.com/adrianbrad/queue](https://pkg.go.dev/github.com/adrianbrad/queue)**.
28
32
29
33
<!-- TOC -->
30
34
*[queue](#queue)
35
+
*[Features](#features)
31
36
*[Installation](#installation)
32
37
*[Import](#import)
38
+
*[Quick start](#quick-start)
33
39
*[Choosing a queue](#choosing-a-queue)
34
40
*[Usage](#usage)
35
41
*[Queue Interface](#queue-interface)
@@ -38,7 +44,10 @@ Benchmarks and Example tests can be found in this package.
38
44
*[Circular Queue](#circular-queue)
39
45
*[Linked Queue](#linked-queue)
40
46
*[Delay Queue](#delay-queue)
41
-
*[Benchmarks](#benchmarks-)
47
+
*[Benchmarks](#benchmarks)
48
+
*[Contributing](#contributing)
49
+
*[Security](#security)
50
+
*[License](#license)
42
51
<!-- TOC -->
43
52
44
53
## Installation
@@ -55,15 +64,41 @@ To use this package in your project, you can import it as follows:
55
64
import"github.com/adrianbrad/queue"
56
65
```
57
66
67
+
## Quick start
68
+
69
+
```go
70
+
package main
71
+
72
+
import (
73
+
"fmt"
74
+
75
+
"github.com/adrianbrad/queue"
76
+
)
77
+
78
+
funcmain() {
79
+
q:= queue.NewLinked([]int{1, 2, 3})
80
+
81
+
_ = q.Offer(4)
82
+
83
+
// prints 1, then 2, then 3, then 4 (one per iteration)
84
+
for !q.IsEmpty() {
85
+
v, _:= q.Get()
86
+
fmt.Println(v)
87
+
}
88
+
}
89
+
```
90
+
91
+
Every implementation satisfies the same `Queue[T comparable]` interface. Pick a different constructor (`NewBlocking`, `NewPriority`, `NewCircular`, `NewDelay`) without changing the call sites.
|`Blocking`| FIFO | Optional; `Offer` errors on full | Yes —`OfferWait`, `GetWait`, `PeekWait`| You want a classic producer–consumer queue with backpressure and blocking semantics. |
63
-
|`Priority`| Custom (less func) | Optional; `Offer` errors on full | No | Order depends on a computed value — smallest deadline, highest score, lexicographic, etc. |
97
+
|`Blocking`| FIFO | Optional; `Offer` errors on full | Yes, via`OfferWait`, `GetWait`, `PeekWait`| You want a classic producer-consumer queue with backpressure and blocking semantics. |
98
+
|`Priority`| Custom (less func) | Optional; `Offer` errors on full | No | Order depends on a computed value (smallest deadline, highest score, lexicographic, etc). |
64
99
|`Circular`| FIFO | Required; `Offer`**overwrites the oldest**| No | You want fixed memory and the most recent N items; dropping older entries is acceptable. |
65
100
|`Linked`| FIFO | None (unbounded) | No | You need an unbounded FIFO and don't want to pick a capacity up front. |
66
-
|`Delay`| By deadline | Optional; `Offer` errors on full |`GetWait` sleeps until the head's deadline passes | Items should become available at a future time — timers, retry scheduling, TTL expiry. |
101
+
|`Delay`| By deadline | Optional; `Offer` errors on full |`GetWait` sleeps until the head's deadline passes | Items should become available at a future time (timers, retry scheduling, TTL expiry). |
67
102
68
103
## Usage
69
104
@@ -105,7 +140,7 @@ type Queue[T comparable] interface {
105
140
106
141
Blocking queue is a FIFO ordered data structure. Both blocking and non-blocking methods are implemented.
107
142
Blocking methods wait for the queue to have available items when dequeuing, and wait for a slot to become available in case the queue is full when enqueuing.
108
-
The non-blocking methods return an error if an element cannot be added or removed.
143
+
The non-blocking methods return an error if an element cannot be added or removed.
109
144
Implemented using sync.Cond from the standard library.
110
145
111
146
```go
@@ -140,14 +175,14 @@ func main() {
140
175
// handle err
141
176
}
142
177
143
-
fmt.Println("elem: ", elem) // elem: 2
178
+
fmt.Printf("elem: %d\n", elem) // elem: 2
144
179
}
145
180
```
146
181
147
182
### Priority Queue
148
183
149
-
Priority Queue is a data structure where the order of the elements is given by a comparator function provided at construction.
150
-
Implemented using container/heap standard library package.
184
+
Priority Queue is a data structure where the order of the elements is given by a less function provided at construction.
@@ -204,35 +239,35 @@ then the next element to be removed from the queue will be the element at index
204
239
package main
205
240
206
241
import (
207
-
"fmt"
242
+
"fmt"
208
243
209
-
"github.com/adrianbrad/queue"
244
+
"github.com/adrianbrad/queue"
210
245
)
211
246
212
247
funcmain() {
213
-
elems:= []int{2, 3, 4}
248
+
elems:= []int{2, 3, 4}
214
249
215
-
circularQueue:= queue.NewCircular(elems, 3)
250
+
circularQueue:= queue.NewCircular(elems, 3)
216
251
217
-
containsTwo:= circularQueue.Contains(2)
218
-
fmt.Println(containsTwo) // true
252
+
containsTwo:= circularQueue.Contains(2)
253
+
fmt.Println(containsTwo) // true
219
254
220
-
size:= circularQueue.Size()
221
-
fmt.Println(size) // 3
255
+
size:= circularQueue.Size()
256
+
fmt.Println(size) // 3
222
257
223
-
empty:= circularQueue.IsEmpty()
224
-
fmt.Println(empty) // false
258
+
empty:= circularQueue.IsEmpty()
259
+
fmt.Println(empty) // false
225
260
226
-
iferr:= circularQueue.Offer(1); err != nil {
227
-
// handle err
228
-
}
261
+
iferr:= circularQueue.Offer(1); err != nil {
262
+
// handle err
263
+
}
229
264
230
-
elem, err:= circularQueue.Get()
231
-
if err != nil {
232
-
// handle err
233
-
}
265
+
elem, err:= circularQueue.Get()
266
+
if err != nil {
267
+
// handle err
268
+
}
234
269
235
-
fmt.Printf("elem: %d\n", elem) // elem: 1
270
+
fmt.Printf("elem: %d\n", elem) // elem: 1
236
271
}
237
272
```
238
273
@@ -247,35 +282,35 @@ without the need for traversal.
247
282
package main
248
283
249
284
import (
250
-
"fmt"
285
+
"fmt"
251
286
252
-
"github.com/adrianbrad/queue"
287
+
"github.com/adrianbrad/queue"
253
288
)
254
289
255
290
funcmain() {
256
-
elems:= []int{2, 3, 4}
291
+
elems:= []int{2, 3, 4}
257
292
258
-
circularQueue:= queue.NewLinked(elems)
293
+
linkedQueue:= queue.NewLinked(elems)
259
294
260
-
containsTwo:=circularQueue.Contains(2)
261
-
fmt.Println(containsTwo) // true
295
+
containsTwo:=linkedQueue.Contains(2)
296
+
fmt.Println(containsTwo) // true
262
297
263
-
size:=circularQueue.Size()
264
-
fmt.Println(size) // 3
298
+
size:=linkedQueue.Size()
299
+
fmt.Println(size) // 3
265
300
266
-
empty:=circularQueue.IsEmpty()
267
-
fmt.Println(empty) // false
301
+
empty:=linkedQueue.IsEmpty()
302
+
fmt.Println(empty) // false
268
303
269
-
iferr:=circularQueue.Offer(1); err != nil {
270
-
// handle err
271
-
}
304
+
iferr:=linkedQueue.Offer(1); err != nil {
305
+
// handle err
306
+
}
272
307
273
-
elem, err:=circularQueue.Get()
274
-
if err != nil {
275
-
// handle err
276
-
}
308
+
elem, err:=linkedQueue.Get()
309
+
if err != nil {
310
+
// handle err
311
+
}
277
312
278
-
fmt.Printf("elem: %d\n", elem) // elem: 2
313
+
fmt.Printf("elem: %d\n", elem) // elem: 2
279
314
}
280
315
```
281
316
@@ -287,45 +322,45 @@ A `Delay` queue is a priority queue where each element becomes dequeuable at a d
287
322
package main
288
323
289
324
import (
290
-
"fmt"
291
-
"time"
325
+
"fmt"
326
+
"time"
292
327
293
-
"github.com/adrianbrad/queue"
328
+
"github.com/adrianbrad/queue"
294
329
)
295
330
296
331
type task struct {
297
-
id int
298
-
runAt time.Time
332
+
id int
333
+
runAt time.Time
299
334
}
300
335
301
336
funcmain() {
302
-
now:= time.Now()
303
-
304
-
delayQueue:= queue.NewDelay(
305
-
[]task{
306
-
{id: 1, runAt: now.Add(20 * time.Millisecond)},
307
-
{id: 2, runAt: now.Add(5 * time.Millisecond)},
308
-
},
309
-
func(t task) time.Time { return t.runAt },
310
-
)
311
-
312
-
size:= delayQueue.Size()
313
-
fmt.Println(size) // 2
314
-
315
-
// Non-blocking: not due yet.
316
-
if_, err:= delayQueue.Get(); err != nil {
317
-
// err == queue.ErrNoElementsAvailable
318
-
}
319
-
320
-
// Blocking: returns as soon as the head's deadline passes.
321
-
next:= delayQueue.GetWait()
322
-
fmt.Printf("next: %d\n", next.id) // next: 2
337
+
now:= time.Now()
338
+
339
+
delayQueue:= queue.NewDelay(
340
+
[]task{
341
+
{id: 1, runAt: now.Add(20 * time.Millisecond)},
342
+
{id: 2, runAt: now.Add(5 * time.Millisecond)},
343
+
},
344
+
func(t task) time.Time { return t.runAt },
345
+
)
346
+
347
+
size:= delayQueue.Size()
348
+
fmt.Println(size) // 2
349
+
350
+
// Non-blocking: not due yet.
351
+
if_, err:= delayQueue.Get(); err != nil {
352
+
// err == queue.ErrNoElementsAvailable
353
+
}
354
+
355
+
// Blocking: returns as soon as the head's deadline passes.
356
+
next:= delayQueue.GetWait()
357
+
fmt.Printf("next: %d\n", next.id) // next: 2
323
358
}
324
359
```
325
360
326
-
## Benchmarks
361
+
## Benchmarks
327
362
328
-
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 Circularand Linked) should be stable.
363
+
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, Linked, Priority, and Delay) should be stable.
PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the workflow, coding conventions, and the 100% coverage requirement. Ask questions by opening a GitHub issue.
386
+
387
+
## Security
388
+
389
+
Please report security issues privately following [SECURITY.md](SECURITY.md) rather than opening a public issue.
390
+
391
+
## License
392
+
393
+
MIT. See [LICENSE](LICENSE). Maintained by [@adrianbrad](https://github.com/adrianbrad).
0 commit comments