@@ -14,20 +14,27 @@ type BlockingDequeue[T any] struct {
14
14
15
15
writeCond * sync.Cond // condition used to lock and notify about writing to the encapsulated list
16
16
capacityLock * sync.RWMutex // lock used to protect the capacity
17
+ onFullLock * sync.RWMutex // lock used to protect the onFull callback
18
+ onEmptyLock * sync.RWMutex // lock used to protect the onEmpty callback
17
19
18
20
capacity int
19
21
20
- OnFull func () // Optional callback function invoked when the dequeue is full
21
- OnEmpty func () // Optional callback function invoked when the dequeue is empty
22
+ onFull func () // Optional callback function invoked when the dequeue is full
23
+ onEmpty func () // Optional callback function invoked when the dequeue is empty
22
24
}
23
25
24
26
// Creates a new blocking dequeue with infinite capacity.
25
27
// The dequeue MUST only be created using this method.
26
28
func NewBlockingDequeue [T any ]() * BlockingDequeue [T ] {
27
29
d := new (BlockingDequeue [T ])
28
30
d .list = list .New ()
31
+
29
32
d .writeCond = sync .NewCond (& sync.Mutex {})
33
+
30
34
d .capacityLock = & sync.RWMutex {}
35
+ d .onFullLock = & sync.RWMutex {}
36
+ d .onEmptyLock = & sync.RWMutex {}
37
+
31
38
return d
32
39
}
33
40
@@ -47,9 +54,12 @@ func (d *BlockingDequeue[T]) PushFront(item T) {
47
54
// Notify the consumer that an item has been added
48
55
defer d .writeCond .Broadcast ()
49
56
57
+ d .onFullLock .RLock ()
58
+ defer d .onFullLock .RUnlock ()
59
+
50
60
// Call the OnFull callback if the dequeue is full
51
- if d .isFull_unsafe () && d .OnFull != nil {
52
- d .OnFull ()
61
+ if d .isFull_unsafe () && d .onFull != nil {
62
+ d .onFull ()
53
63
}
54
64
}
55
65
@@ -68,8 +78,11 @@ func (d *BlockingDequeue[T]) PushBack(item T) {
68
78
defer d .writeCond .Broadcast ()
69
79
70
80
// Call the OnFull callback if the dequeue is full
71
- if d .isFull_unsafe () && d .OnFull != nil {
72
- d .OnFull ()
81
+ d .onFullLock .RLock ()
82
+ defer d .onFullLock .RUnlock ()
83
+
84
+ if d .isFull_unsafe () && d .onFull != nil {
85
+ d .onFull ()
73
86
}
74
87
}
75
88
@@ -88,8 +101,11 @@ func (d *BlockingDequeue[T]) PopFront() T {
88
101
defer d .writeCond .Broadcast ()
89
102
90
103
// Call the OnEmpty callback if the dequeue is empty
91
- if d .isEmpty_unsafe () && d .OnEmpty != nil {
92
- d .OnEmpty ()
104
+ d .onEmptyLock .RLock ()
105
+ defer d .onEmptyLock .RUnlock ()
106
+
107
+ if d .isEmpty_unsafe () && d .onEmpty != nil {
108
+ d .onEmpty ()
93
109
}
94
110
95
111
return item
@@ -110,8 +126,11 @@ func (d *BlockingDequeue[T]) PopBack() T {
110
126
defer d .writeCond .Broadcast ()
111
127
112
128
// Call the OnEmpty callback if the dequeue is empty
113
- if d .isEmpty_unsafe () && d .OnEmpty != nil {
114
- d .OnEmpty ()
129
+ d .onEmptyLock .RLock ()
130
+ defer d .onEmptyLock .RUnlock ()
131
+
132
+ if d .isEmpty_unsafe () && d .onEmpty != nil {
133
+ d .onEmpty ()
115
134
}
116
135
117
136
return item
@@ -143,6 +162,26 @@ func (d *BlockingDequeue[T]) PeekBack() T {
143
162
return element .Value .(T )
144
163
}
145
164
165
+ // ================================[Listeners related]================================
166
+
167
+ // Set the callback function invoked when the dequeue is full.
168
+ // Attempting to update the dequeue in the callback function will cause a deadlock.
169
+ func (d * BlockingDequeue [T ]) SetOnFull (onFull func ()) {
170
+ d .onFullLock .Lock ()
171
+ defer d .onFullLock .Unlock ()
172
+
173
+ d .onFull = onFull
174
+ }
175
+
176
+ // Set the callback function invoked when the dequeue is empty.
177
+ // Attempting to update the dequeue in the callback function will cause a deadlock.
178
+ func (d * BlockingDequeue [T ]) SetOnEmpty (onEmpty func ()) {
179
+ d .onEmptyLock .Lock ()
180
+ defer d .onEmptyLock .Unlock ()
181
+
182
+ d .onEmpty = onEmpty
183
+ }
184
+
146
185
// ================================[Size/Capacity related]================================
147
186
148
187
// Set dequeue capacity, if capacity is 0, dequeue is infinite.
0 commit comments