-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathqueue.go
More file actions
160 lines (131 loc) · 2.48 KB
/
Copy pathqueue.go
File metadata and controls
160 lines (131 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package queue
import (
"container/list"
"context"
"sync"
)
type Queue struct {
mu sync.RWMutex
cond *sync.Cond
l *list.List
length int
closed bool
}
func New() *Queue {
q := &Queue{
l: list.New(),
}
q.cond = sync.NewCond(&q.mu)
return q
}
func (q *Queue) Enqueue(value interface{}) error {
q.mu.Lock()
defer q.mu.Unlock()
if q.closed {
return ErrQueueClosed
}
q.l.PushBack(value)
q.length++
q.cond.Signal()
return nil
}
func (q *Queue) Dequeue() (value interface{}, hasNext bool) {
q.mu.Lock()
defer q.mu.Unlock()
if q.length == 0 {
var zero interface{}
return zero, false
}
elem := q.l.Front()
val := elem.Value
q.l.Remove(elem)
q.length--
return val, q.length > 0
}
// DequeueBlocking blocks until a message is available and returns it.
// Context can be used for cancellation and timeout control.
func (q *Queue) DequeueBlocking(ctx context.Context) (interface{}, error) {
// Use a channel to signal context cancellation
ctxDone := make(chan struct{})
go func() {
<-ctx.Done()
q.mu.Lock()
q.cond.Broadcast() // Wake up the waiting goroutine
q.mu.Unlock()
close(ctxDone)
}()
q.mu.Lock()
defer q.mu.Unlock()
for {
// If queue has elements, dequeue immediately
if q.length > 0 {
elem := q.l.Front()
val := elem.Value
q.l.Remove(elem)
q.length--
return val, nil
}
// If closed and empty → error
if q.closed {
var zero interface{}
return zero, ErrQueueClosed
}
// Check context before waiting
select {
case <-ctxDone:
var zero interface{}
return zero, ctx.Err()
default:
}
// Wait for signal (releases lock while waiting)
q.cond.Wait()
// After waking, check if it was due to context cancellation
select {
case <-ctxDone:
var zero interface{}
return zero, ctx.Err()
default:
}
}
}
func (q *Queue) Size() int {
q.mu.RLock()
defer q.mu.RUnlock()
return q.length
}
func (q *Queue) IsEmpty() bool {
q.mu.RLock()
defer q.mu.RUnlock()
return q.length == 0
}
func (q *Queue) Clear() {
q.mu.Lock()
defer q.mu.Unlock()
q.l.Init()
q.length = 0
}
func (q *Queue) Close() {
q.mu.Lock()
defer q.mu.Unlock()
q.closed = true
q.cond.Broadcast()
}
func (q *Queue) IsClosed() bool {
q.mu.RLock()
defer q.mu.RUnlock()
return q.closed
}
func (q *Queue) IsOpen() bool {
q.mu.RLock()
defer q.mu.RUnlock()
return !q.closed
}
var (
ErrQueueClosed = &QueueError{"queue is closed"}
)
type QueueError struct {
message string
}
func (e *QueueError) Error() string {
return e.message
}