github.com/gogf/gf/v2@v2.7.4/container/gqueue/gqueue.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 // Package gqueue provides dynamic/static concurrent-safe queue. 8 // 9 // Features: 10 // 11 // 1. FIFO queue(data -> list -> chan); 12 // 13 // 2. Fast creation and initialization; 14 // 15 // 3. Support dynamic queue size(unlimited queue size); 16 // 17 // 4. Blocking when reading data from queue; 18 package gqueue 19 20 import ( 21 "math" 22 23 "github.com/gogf/gf/v2/container/glist" 24 "github.com/gogf/gf/v2/container/gtype" 25 ) 26 27 // Queue is a concurrent-safe queue built on doubly linked list and channel. 28 type Queue struct { 29 limit int // Limit for queue size. 30 list *glist.List // Underlying list structure for data maintaining. 31 closed *gtype.Bool // Whether queue is closed. 32 events chan struct{} // Events for data writing. 33 C chan interface{} // Underlying channel for data reading. 34 } 35 36 const ( 37 defaultQueueSize = 10000 // Size for queue buffer. 38 defaultBatchSize = 10 // Max batch size per-fetching from list. 39 ) 40 41 // New returns an empty queue object. 42 // Optional parameter `limit` is used to limit the size of the queue, which is unlimited in default. 43 // When `limit` is given, the queue will be static and high performance which is comparable with stdlib channel. 44 func New(limit ...int) *Queue { 45 q := &Queue{ 46 closed: gtype.NewBool(), 47 } 48 if len(limit) > 0 && limit[0] > 0 { 49 q.limit = limit[0] 50 q.C = make(chan interface{}, limit[0]) 51 } else { 52 q.list = glist.New(true) 53 q.events = make(chan struct{}, math.MaxInt32) 54 q.C = make(chan interface{}, defaultQueueSize) 55 go q.asyncLoopFromListToChannel() 56 } 57 return q 58 } 59 60 // Push pushes the data `v` into the queue. 61 // Note that it would panic if Push is called after the queue is closed. 62 func (q *Queue) Push(v interface{}) { 63 if q.limit > 0 { 64 q.C <- v 65 } else { 66 q.list.PushBack(v) 67 if len(q.events) < defaultQueueSize { 68 q.events <- struct{}{} 69 } 70 } 71 } 72 73 // Pop pops an item from the queue in FIFO way. 74 // Note that it would return nil immediately if Pop is called after the queue is closed. 75 func (q *Queue) Pop() interface{} { 76 return <-q.C 77 } 78 79 // Close closes the queue. 80 // Notice: It would notify all goroutines return immediately, 81 // which are being blocked reading using Pop method. 82 func (q *Queue) Close() { 83 if !q.closed.Cas(false, true) { 84 return 85 } 86 if q.events != nil { 87 close(q.events) 88 } 89 if q.limit > 0 { 90 close(q.C) 91 } else { 92 for i := 0; i < defaultBatchSize; i++ { 93 q.Pop() 94 } 95 } 96 } 97 98 // Len returns the length of the queue. 99 // Note that the result might not be accurate if using unlimited queue size as there's an 100 // asynchronous channel reading the list constantly. 101 func (q *Queue) Len() (length int64) { 102 bufferedSize := int64(len(q.C)) 103 if q.limit > 0 { 104 return bufferedSize 105 } 106 return int64(q.list.Size()) + bufferedSize 107 } 108 109 // Size is alias of Len. 110 // Deprecated: use Len instead. 111 func (q *Queue) Size() int64 { 112 return q.Len() 113 } 114 115 // asyncLoopFromListToChannel starts an asynchronous goroutine, 116 // which handles the data synchronization from list `q.list` to channel `q.C`. 117 func (q *Queue) asyncLoopFromListToChannel() { 118 defer func() { 119 if q.closed.Val() { 120 _ = recover() 121 } 122 }() 123 for !q.closed.Val() { 124 <-q.events 125 for !q.closed.Val() { 126 if bufferLength := q.list.Len(); bufferLength > 0 { 127 // When q.C is closed, it will panic here, especially q.C is being blocked for writing. 128 // If any error occurs here, it will be caught by recover and be ignored. 129 for i := 0; i < bufferLength; i++ { 130 q.C <- q.list.PopFront() 131 } 132 } else { 133 break 134 } 135 } 136 // Clear q.events to remain just one event to do the next synchronization check. 137 for i := 0; i < len(q.events)-1; i++ { 138 <-q.events 139 } 140 } 141 // It should be here to close `q.C` if `q` is unlimited size. 142 // It's the sender's responsibility to close channel when it should be closed. 143 close(q.C) 144 }