github.com/zhongdalu/gf@v1.0.0/g/container/gqueue/gqueue.go (about) 1 // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf. 6 7 // Package gqueue provides a 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 // 19 package gqueue 20 21 import ( 22 "github.com/zhongdalu/gf/g/container/glist" 23 "github.com/zhongdalu/gf/g/container/gtype" 24 "math" 25 ) 26 27 type Queue struct { 28 limit int // Limit for queue size. 29 list *glist.List // Underlying list structure for data maintaining. 30 closed *gtype.Bool // Whether queue is closed. 31 events chan struct{} // Events for data writing. 32 C chan interface{} // Underlying channel for data reading. 33 } 34 35 const ( 36 // Size for queue buffer. 37 gDEFAULT_QUEUE_SIZE = 10000 38 // Max batch size per-fetching from list. 39 gDEFAULT_MAX_BATCH_SIZE = 10 40 ) 41 42 // New returns an empty queue object. 43 // Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default. 44 // When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel. 45 func New(limit ...int) *Queue { 46 q := &Queue{ 47 closed: gtype.NewBool(), 48 } 49 if len(limit) > 0 && limit[0] > 0 { 50 q.limit = limit[0] 51 q.C = make(chan interface{}, limit[0]) 52 } else { 53 q.list = glist.New() 54 q.events = make(chan struct{}, math.MaxInt32) 55 q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE) 56 go q.startAsyncLoop() 57 } 58 return q 59 } 60 61 // startAsyncLoop starts an asynchronous goroutine, 62 // which handles the data synchronization from list <q.list> to channel <q.C>. 63 func (q *Queue) startAsyncLoop() { 64 defer func() { 65 if q.closed.Val() { 66 _ = recover() 67 } 68 }() 69 for !q.closed.Val() { 70 <-q.events 71 for !q.closed.Val() { 72 if length := q.list.Len(); length > 0 { 73 if length > gDEFAULT_MAX_BATCH_SIZE { 74 length = gDEFAULT_MAX_BATCH_SIZE 75 } 76 for _, v := range q.list.PopFronts(length) { 77 // When q.C is closed, it will panic here, especially q.C is being blocked for writing. 78 // If any error occurs here, it will be caught by recover and be ignored. 79 q.C <- v 80 } 81 } else { 82 break 83 } 84 } 85 // Clear q.events to remain just one event to do the next synchronization check. 86 for i := 0; i < len(q.events)-1; i++ { 87 <-q.events 88 } 89 } 90 // It should be here to close q.C if <q> is unlimited size. 91 // It's the sender's responsibility to close channel when it should be closed. 92 close(q.C) 93 } 94 95 // Push pushes the data <v> into the queue. 96 // Note that it would panics if Push is called after the queue is closed. 97 func (q *Queue) Push(v interface{}) { 98 if q.limit > 0 { 99 q.C <- v 100 } else { 101 q.list.PushBack(v) 102 if len(q.events) < gDEFAULT_QUEUE_SIZE { 103 q.events <- struct{}{} 104 } 105 } 106 } 107 108 // Pop pops an item from the queue in FIFO way. 109 // Note that it would return nil immediately if Pop is called after the queue is closed. 110 func (q *Queue) Pop() interface{} { 111 return <-q.C 112 } 113 114 // Close closes the queue. 115 // Notice: It would notify all goroutines return immediately, 116 // which are being blocked reading using Pop method. 117 func (q *Queue) Close() { 118 q.closed.Set(true) 119 if q.events != nil { 120 close(q.events) 121 } 122 if q.limit > 0 { 123 close(q.C) 124 } 125 for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ { 126 q.Pop() 127 } 128 } 129 130 // Len returns the length of the queue. 131 func (q *Queue) Len() (length int) { 132 if q.list != nil { 133 length += q.list.Len() 134 } 135 length += len(q.C) 136 return 137 } 138 139 // Size is alias of Len. 140 func (q *Queue) Size() int { 141 return q.Len() 142 }