mirror of
https://github.com/duanhf2012/origin.git
synced 2026-02-04 06:54:45 +08:00
新增异步队列
This commit is contained in:
23
util/queue/.gitignore
vendored
Normal file
23
util/queue/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
7
util/queue/.travis.yml
Normal file
7
util/queue/.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
21
util/queue/LICENSE
Normal file
21
util/queue/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Evan Huus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
16
util/queue/README.md
Normal file
16
util/queue/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Queue
|
||||
=====
|
||||
|
||||
[](https://travis-ci.org/eapache/queue)
|
||||
[](https://godoc.org/github.com/eapache/queue)
|
||||
[](https://eapache.github.io/conduct.html)
|
||||
|
||||
A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki.
|
||||
Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
|
||||
substantial memory and time benefits, and fewer GC pauses.
|
||||
|
||||
The queue implemented here is as fast as it is in part because it is *not* thread-safe.
|
||||
|
||||
Follows semantic versioning using https://gopkg.in/ - import from
|
||||
[`gopkg.in/eapache/queue.v1`](https://gopkg.in/eapache/queue.v1)
|
||||
for guaranteed API stability.
|
||||
104
util/queue/queue.go
Normal file
104
util/queue/queue.go
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki.
|
||||
Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
|
||||
substantial memory and time benefits, and fewer GC pauses.
|
||||
|
||||
The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe.
|
||||
*/
|
||||
package queue
|
||||
|
||||
// minQueueLen is smallest capacity that queue may have.
|
||||
// Must be power of 2 for bitwise modulus: x % n == x & (n - 1).
|
||||
const minQueueLen = 16
|
||||
|
||||
// Queue represents a single instance of the queue data structure.
|
||||
type Queue struct {
|
||||
buf []interface{}
|
||||
head, tail, count int
|
||||
}
|
||||
|
||||
// New constructs and returns a new Queue.
|
||||
func NewQueue() *Queue {
|
||||
return &Queue{
|
||||
buf: make([]interface{}, minQueueLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Length returns the number of elements currently stored in the queue.
|
||||
func (q *Queue) Length() int {
|
||||
return q.count
|
||||
}
|
||||
|
||||
// resizes the queue to fit exactly twice its current contents
|
||||
// this can result in shrinking if the queue is less than half-full
|
||||
func (q *Queue) resize() {
|
||||
newBuf := make([]interface{}, q.count<<1)
|
||||
|
||||
if q.tail > q.head {
|
||||
copy(newBuf, q.buf[q.head:q.tail])
|
||||
} else {
|
||||
n := copy(newBuf, q.buf[q.head:])
|
||||
copy(newBuf[n:], q.buf[:q.tail])
|
||||
}
|
||||
|
||||
q.head = 0
|
||||
q.tail = q.count
|
||||
q.buf = newBuf
|
||||
}
|
||||
|
||||
// Add puts an element on the end of the queue.
|
||||
func (q *Queue) Add(elem interface{}) {
|
||||
if q.count == len(q.buf) {
|
||||
q.resize()
|
||||
}
|
||||
|
||||
q.buf[q.tail] = elem
|
||||
// bitwise modulus
|
||||
q.tail = (q.tail + 1) & (len(q.buf) - 1)
|
||||
q.count++
|
||||
}
|
||||
|
||||
// Peek returns the element at the head of the queue. This call panics
|
||||
// if the queue is empty.
|
||||
func (q *Queue) Peek() interface{} {
|
||||
if q.count <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return q.buf[q.head]
|
||||
}
|
||||
|
||||
// Get returns the element at index i in the queue. If the index is
|
||||
// invalid, the call will panic. This method accepts both positive and
|
||||
// negative index values. Index 0 refers to the first element, and
|
||||
// index -1 refers to the last.
|
||||
func (q *Queue) Get(i int) interface{} {
|
||||
// If indexing backwards, convert to positive index.
|
||||
if i < 0 {
|
||||
i += q.count
|
||||
}
|
||||
if i < 0 || i >= q.count {
|
||||
panic("queue: Get() called with index out of range")
|
||||
}
|
||||
// bitwise modulus
|
||||
return q.buf[(q.head+i)&(len(q.buf)-1)]
|
||||
}
|
||||
|
||||
// Remove removes and returns the element from the front of the queue. If the
|
||||
// queue is empty, the call will panic.
|
||||
func (q *Queue) Pop() interface{} {
|
||||
if q.count <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := q.buf[q.head]
|
||||
q.buf[q.head] = nil
|
||||
// bitwise modulus
|
||||
q.head = (q.head + 1) & (len(q.buf) - 1)
|
||||
q.count--
|
||||
// Resize down if buffer 1/4 full.
|
||||
if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) {
|
||||
q.resize()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
178
util/queue/queue_test.go
Normal file
178
util/queue/queue_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package queue
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestQueueSimple(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
if q.Peek().(int) != i {
|
||||
t.Error("peek", i, "had value", q.Peek())
|
||||
}
|
||||
x := q.Remove()
|
||||
if x != i {
|
||||
t.Error("remove", i, "had value", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueWrapping(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
q.Remove()
|
||||
q.Add(minQueueLen + i)
|
||||
}
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
if q.Peek().(int) != i+3 {
|
||||
t.Error("peek", i, "had value", q.Peek())
|
||||
}
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueLength(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
if q.Length() != 0 {
|
||||
t.Error("empty queue length not 0")
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Add(i)
|
||||
if q.Length() != i+1 {
|
||||
t.Error("adding: queue with", i, "elements has length", q.Length())
|
||||
}
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Remove()
|
||||
if q.Length() != 1000-i-1 {
|
||||
t.Error("removing: queue with", 1000-i-i, "elements has length", q.Length())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueGet(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Add(i)
|
||||
for j := 0; j < q.Length(); j++ {
|
||||
if q.Get(j).(int) != j {
|
||||
t.Errorf("index %d doesn't contain %d", j, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueGetNegative(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Add(i)
|
||||
for j := 1; j <= q.Length(); j++ {
|
||||
if q.Get(-j).(int) != q.Length()-j {
|
||||
t.Errorf("index %d doesn't contain %d", -j, q.Length()-j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueGetOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
q.Add(1)
|
||||
q.Add(2)
|
||||
q.Add(3)
|
||||
|
||||
assertPanics(t, "should panic when negative index", func() {
|
||||
q.Get(-4)
|
||||
})
|
||||
|
||||
assertPanics(t, "should panic when index greater than length", func() {
|
||||
q.Get(4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueuePeekOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
assertPanics(t, "should panic when peeking empty queue", func() {
|
||||
q.Peek()
|
||||
})
|
||||
|
||||
q.Add(1)
|
||||
q.Remove()
|
||||
|
||||
assertPanics(t, "should panic when peeking emptied queue", func() {
|
||||
q.Peek()
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueueRemoveOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
assertPanics(t, "should panic when removing empty queue", func() {
|
||||
q.Remove()
|
||||
})
|
||||
|
||||
q.Add(1)
|
||||
q.Remove()
|
||||
|
||||
assertPanics(t, "should panic when removing emptied queue", func() {
|
||||
q.Remove()
|
||||
})
|
||||
}
|
||||
|
||||
func assertPanics(t *testing.T, name string, f func()) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("%s: didn't panic as expected", name)
|
||||
}
|
||||
}()
|
||||
|
||||
f()
|
||||
}
|
||||
|
||||
// General warning: Go's benchmark utility (go test -bench .) increases the number of
|
||||
// iterations until the benchmarks take a reasonable amount of time to run; memory usage
|
||||
// is *NOT* considered. On my machine, these benchmarks hit around ~1GB before they've had
|
||||
// enough, but if you have less than that available and start swapping, then all bets are off.
|
||||
|
||||
func BenchmarkQueueSerial(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(nil)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Peek()
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQueueGet(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQueueTickTock(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(nil)
|
||||
q.Peek()
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
51
util/queue/syncqueue.go
Normal file
51
util/queue/syncqueue.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package queue
|
||||
|
||||
import "sync"
|
||||
|
||||
type SyncQueue struct {
|
||||
que *Queue
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (q *SyncQueue) Len() int {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
return q.que.Length()
|
||||
}
|
||||
|
||||
func (q *SyncQueue) Add(elem interface{}) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.que.Add(elem)
|
||||
}
|
||||
|
||||
func (q *SyncQueue) Peek() interface{} {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
return q.que.Peek()
|
||||
}
|
||||
|
||||
func (q *SyncQueue) Get(i int) interface{} {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
return q.que.Get(i)
|
||||
}
|
||||
|
||||
func (q *SyncQueue) Pop() interface{} {
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
return q.que.Pop()
|
||||
}
|
||||
|
||||
func NewSyncQueue() *SyncQueue {
|
||||
syncQueue := SyncQueue{}
|
||||
syncQueue.que = NewQueue()
|
||||
|
||||
return &syncQueue
|
||||
}
|
||||
Reference in New Issue
Block a user