2019-02-02 16:18:25 +08:00
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-02-02 15:19:42 +08:00
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-02-02 15:19:42 +08:00
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// Package gqueue provides a dynamic/static concurrent-safe queue.
|
2019-01-16 13:35:16 +08:00
|
|
|
//
|
2019-04-25 22:14:20 +08:00
|
|
|
// Features:
|
|
|
|
|
//
|
|
|
|
|
// 1. FIFO queue(data -> list -> chan);
|
|
|
|
|
//
|
|
|
|
|
// 2. Fast creation and initialization;
|
|
|
|
|
//
|
|
|
|
|
// 3. Support dynamic queue size(unlimited queue size);
|
|
|
|
|
//
|
|
|
|
|
// 4. Blocking when reading data from queue;
|
2019-01-16 13:35:16 +08:00
|
|
|
//
|
2018-02-02 15:19:42 +08:00
|
|
|
package gqueue
|
|
|
|
|
|
|
|
|
|
import (
|
2019-03-08 17:54:50 +08:00
|
|
|
"github.com/gogf/gf/g/container/glist"
|
2018-02-02 15:19:42 +08:00
|
|
|
"math"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Queue struct {
|
2019-04-25 22:14:20 +08:00
|
|
|
limit int // Limit for queue size.
|
|
|
|
|
list *glist.List // Underlying list structure for data maintaining.
|
|
|
|
|
events chan struct{} // Events for data writing.
|
|
|
|
|
closed chan struct{} // Events for queue closing.
|
|
|
|
|
C chan interface{} // Underlying channel for data reading.
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
2018-09-15 16:40:13 +08:00
|
|
|
const (
|
2019-04-25 22:14:20 +08:00
|
|
|
// Size for queue buffer.
|
2019-01-09 12:54:37 +08:00
|
|
|
gDEFAULT_QUEUE_SIZE = 10000
|
2018-09-15 16:40:13 +08:00
|
|
|
)
|
|
|
|
|
|
2019-05-08 17:21:18 +08:00
|
|
|
// New returns an empty queue object.
|
2019-06-09 10:33:16 +08:00
|
|
|
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default.
|
|
|
|
|
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel.
|
2018-02-02 16:56:53 +08:00
|
|
|
func New(limit...int) *Queue {
|
2018-09-15 16:40:13 +08:00
|
|
|
q := &Queue {
|
2019-01-09 12:54:37 +08:00
|
|
|
closed : make(chan struct{}, 0),
|
2018-09-16 10:51:02 +08:00
|
|
|
}
|
|
|
|
|
if len(limit) > 0 {
|
2018-11-18 22:22:44 +08:00
|
|
|
q.limit = limit[0]
|
2019-01-09 12:54:37 +08:00
|
|
|
q.C = make(chan interface{}, limit[0])
|
2018-09-16 10:51:02 +08:00
|
|
|
} else {
|
2019-03-08 17:54:50 +08:00
|
|
|
q.list = glist.New()
|
2018-11-18 22:22:44 +08:00
|
|
|
q.events = make(chan struct{}, math.MaxInt32)
|
2019-01-09 12:54:37 +08:00
|
|
|
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
|
2018-09-16 10:51:02 +08:00
|
|
|
go q.startAsyncLoop()
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
2018-09-15 16:40:13 +08:00
|
|
|
return q
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// startAsyncLoop starts an asynchronous goroutine,
|
|
|
|
|
// which handles the data synchronization from list <q.list> to channel <q.C>.
|
2018-09-15 16:40:13 +08:00
|
|
|
func (q *Queue) startAsyncLoop() {
|
|
|
|
|
for {
|
2018-09-16 10:51:02 +08:00
|
|
|
select {
|
2019-01-09 12:54:37 +08:00
|
|
|
case <- q.closed:
|
2018-09-16 10:51:02 +08:00
|
|
|
return
|
|
|
|
|
case <- q.events:
|
2018-09-18 00:01:10 +08:00
|
|
|
for {
|
2019-01-09 12:54:37 +08:00
|
|
|
if length := q.list.Len(); length > 0 {
|
|
|
|
|
array := make([]interface{}, length)
|
|
|
|
|
for i := 0; i < length; i++ {
|
|
|
|
|
if e := q.list.Front(); e != nil {
|
|
|
|
|
array[i] = q.list.Remove(e)
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
q.C <- v
|
|
|
|
|
}
|
2018-09-18 00:01:10 +08:00
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-09-16 10:51:02 +08:00
|
|
|
}
|
2018-09-15 16:40:13 +08:00
|
|
|
}
|
2018-03-13 15:54:56 +08:00
|
|
|
}
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// Push pushes the data <v> into the queue.
|
2019-05-08 17:21:18 +08:00
|
|
|
// Note that it would panics if Push is called after the queue is closed.
|
2018-09-15 16:40:13 +08:00
|
|
|
func (q *Queue) Push(v interface{}) {
|
2018-02-02 16:56:53 +08:00
|
|
|
if q.limit > 0 {
|
2019-01-09 12:54:37 +08:00
|
|
|
q.C <- v
|
2018-03-13 15:54:56 +08:00
|
|
|
} else {
|
2018-09-15 16:40:13 +08:00
|
|
|
q.list.PushBack(v)
|
2019-01-09 12:54:37 +08:00
|
|
|
q.events <- struct{}{}
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// Pop pops an item from the queue in FIFO way.
|
2019-05-08 17:21:18 +08:00
|
|
|
// Note that it would return nil immediately if Pop is called after the queue is closed.
|
2018-09-15 16:40:13 +08:00
|
|
|
func (q *Queue) Pop() interface{} {
|
2019-01-09 12:54:37 +08:00
|
|
|
return <- q.C
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// Close closes the queue.
|
2019-05-08 17:21:18 +08:00
|
|
|
// Notice: It would notify all goroutines return immediately,
|
2019-06-09 10:33:16 +08:00
|
|
|
// which are being blocked reading using Pop method.
|
2018-02-02 15:19:42 +08:00
|
|
|
func (q *Queue) Close() {
|
2019-01-09 12:54:37 +08:00
|
|
|
close(q.C)
|
2018-09-15 16:40:13 +08:00
|
|
|
close(q.events)
|
2019-01-09 12:54:37 +08:00
|
|
|
close(q.closed)
|
2018-02-02 15:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
2019-04-25 22:14:20 +08:00
|
|
|
// Size returns the length of the queue.
|
2018-02-02 15:19:42 +08:00
|
|
|
func (q *Queue) Size() int {
|
2019-01-09 12:54:37 +08:00
|
|
|
return len(q.C) + q.list.Len()
|
2018-03-13 15:54:56 +08:00
|
|
|
}
|
|
|
|
|
|
2018-03-29 13:46:05 +08:00
|
|
|
|