Repository navigation
Expand file tree
/
Copy pathdata_packet_queue.go
More file actions
124 lines (113 loc) · 2.32 KB
/
Copy pathdata_packet_queue.go
File metadata and controls
124 lines (113 loc) · 2.32 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
package openconnect
import (
"context"
"sync"
)
type dataPacketQueue[T any] struct {
access sync.Mutex
items []T
head int
length int
notEmpty chan struct{}
notFull chan struct{}
closed bool
}
func newDataPacketQueue[T any](capacity int) *dataPacketQueue[T] {
return &dataPacketQueue[T]{
items: make([]T, capacity),
notEmpty: make(chan struct{}),
notFull: make(chan struct{}),
}
}
func (q *dataPacketQueue[T]) PushBatch(ctx context.Context, items []T) int {
pushed := 0
for pushed < len(items) {
q.access.Lock()
if q.closed || ctx.Err() != nil {
q.access.Unlock()
return pushed
}
if q.length < len(q.items) {
wasEmpty := q.length == 0
tail := (q.head + q.length) % len(q.items)
q.items[tail] = items[pushed]
q.length++
pushed++
if wasEmpty {
q.signalNotEmptyLocked()
}
q.access.Unlock()
continue
}
notFull := q.notFull
q.access.Unlock()
select {
case <-ctx.Done():
return pushed
case <-notFull:
}
}
return pushed
}
func (q *dataPacketQueue[T]) Pop(maximumItems int) []T {
q.access.Lock()
count := q.length
if maximumItems > 0 {
count = min(count, maximumItems)
}
if count == 0 {
q.access.Unlock()
return nil
}
items := make([]T, count)
for index := range count {
itemIndex := (q.head + index) % len(q.items)
items[index] = q.items[itemIndex]
var zero T
q.items[itemIndex] = zero
}
q.head = (q.head + count) % len(q.items)
q.length -= count
q.signalNotFullLocked()
q.access.Unlock()
return items
}
func (q *dataPacketQueue[T]) Wake() <-chan struct{} {
q.access.Lock()
defer q.access.Unlock()
if q.length > 0 || q.closed {
ready := make(chan struct{})
close(ready)
return ready
}
return q.notEmpty
}
func (q *dataPacketQueue[T]) Closed() bool {
q.access.Lock()
defer q.access.Unlock()
return q.closed
}
func (q *dataPacketQueue[T]) Close() {
q.access.Lock()
if !q.closed {
q.closed = true
q.signalNotEmptyLocked()
q.signalNotFullLocked()
}
q.access.Unlock()
}
func (q *dataPacketQueue[T]) Drain(release func(T)) {
for _, item := range q.Pop(0) {
if release != nil {
release(item)
}
}
}
func (q *dataPacketQueue[T]) signalNotEmptyLocked() {
close(q.notEmpty)
q.notEmpty = make(chan struct{})
}
func (q *dataPacketQueue[T]) signalNotFullLocked() {
close(q.notFull)
q.notFull = make(chan struct{})
}