├── Makefile ├── .travis.yml ├── LICENSE ├── README.md ├── queue.go └── queue_test.go /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | go vet -v ./... 3 | golint ./... 4 | go test -v -race ./... 5 | 6 | clean: 7 | rm -f ./example/example 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8 5 | - 1.7 6 | 7 | script: 8 | - go get -u github.com/golang/lint/golint 9 | - make test 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | 4 | opyright (c) <2015> 5 | 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A GoRoutine safe queue for Golang [![Build Status](https://travis-ci.org/damnever/goqueue.svg?branch=master)](https://travis-ci.org/damnever/goqueue) [![GoDoc](https://godoc.org/github.com/damnever/goqueue?status.svg)](https://godoc.org/github.com/damnever/goqueue) 2 | 3 | It is similar to the Queue of Python. 4 | 5 | ### Installation 6 | 7 | ``` 8 | go get github.com/damnever/goqueue 9 | ``` 10 | 11 | ### Example 12 | 13 | Just for example, I use `Queue.Get(0)` and `Queue.PutNoWait(value)` more and often, but channel is not a right way do that... 14 | 15 | ```Go 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "sync" 21 | 22 | "github.com/Damnever/goqueue" 23 | ) 24 | 25 | func main() { 26 | queue := goqueue.New(0) 27 | wg := &sync.WaitGroup{} 28 | 29 | worker := func(queue *goqueue.Queue) { 30 | defer wg.Done() 31 | for !queue.IsEmpty() { 32 | val, err := queue.Get(0) 33 | if err != nil { 34 | fmt.Println("Unexpect Error: %v\n", err) 35 | } 36 | num := val.(int) 37 | fmt.Printf("-> %v\n", num) 38 | if num%3 == 0 { 39 | for i := num + 1; i < num+3; i++ { 40 | queue.PutNoWait(i) 41 | } 42 | } 43 | } 44 | } 45 | 46 | go func() { 47 | defer wg.Done() 48 | for i := 0; i <= 27; i += 3 { 49 | queue.PutNoWait(i) 50 | } 51 | }() 52 | 53 | for i := 0; i < 5; i++ { 54 | go worker(queue) 55 | } 56 | 57 | wg.Add(6) 58 | wg.Wait() 59 | 60 | fmt.Println("All task done!!!") 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | /* 2 | A GoRoutine safe queue. 3 | */ 4 | 5 | package goqueue 6 | 7 | import ( 8 | "container/list" 9 | "errors" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | var ( 15 | // ErrEmptyQueue is returned when queue is empty. 16 | ErrEmptyQueue = errors.New("queue is empty") 17 | // ErrFullQueue is returned when queue is full. 18 | ErrFullQueue = errors.New("queue is full") 19 | ) 20 | 21 | type waiter chan interface{} 22 | 23 | func newWaiter() waiter { 24 | w := make(chan interface{}, 1) 25 | return w 26 | } 27 | 28 | // Queue is data structure, which has much similar behavior with channel. 29 | type Queue struct { 30 | maxSize int 31 | mutex sync.Mutex 32 | items *list.List // store items 33 | putters *list.List // store blocked Put operators 34 | getters *list.List // store blocked Get operators 35 | } 36 | 37 | // New create a new Queue, The maxSize variable sets the max Queue size. 38 | // If maxSize is zero, Queue will be infinite size, and Put always no wait. 39 | func New(maxSize int) *Queue { 40 | q := new(Queue) 41 | q.mutex = sync.Mutex{} 42 | q.maxSize = maxSize 43 | q.items = list.New() 44 | q.putters = list.New() 45 | q.getters = list.New() 46 | return q 47 | } 48 | 49 | func (q *Queue) newPutter() *list.Element { 50 | w := newWaiter() 51 | return q.putters.PushBack(w) 52 | } 53 | 54 | func (q *Queue) newGetter() *list.Element { 55 | w := newWaiter() 56 | return q.getters.PushBack(w) 57 | } 58 | 59 | func (q *Queue) notifyPutter(getter *list.Element) bool { 60 | if getter != nil { 61 | q.getters.Remove(getter) 62 | } 63 | if q.putters.Len() == 0 { 64 | return false 65 | } 66 | e := q.putters.Front() 67 | q.putters.Remove(e) 68 | w := e.Value.(waiter) 69 | w <- true 70 | return true 71 | } 72 | 73 | func (q *Queue) notifyGetter(putter *list.Element, val interface{}) bool { 74 | if putter != nil { 75 | q.putters.Remove(putter) 76 | } 77 | if q.getters.Len() == 0 { 78 | return false 79 | } 80 | e := q.getters.Front() 81 | q.getters.Remove(e) 82 | w := e.Value.(waiter) 83 | w <- val 84 | return true 85 | } 86 | 87 | func (q *Queue) clearPending() { 88 | for !q.isfull() && q.putters.Len() != 0 { 89 | q.notifyPutter(nil) 90 | } 91 | for !q.isempty() && q.getters.Len() != 0 { 92 | v := q.get() 93 | q.notifyGetter(nil, v) 94 | } 95 | } 96 | 97 | func (q *Queue) get() interface{} { 98 | e := q.items.Front() 99 | q.items.Remove(e) 100 | return e.Value 101 | } 102 | 103 | func (q *Queue) put(val interface{}) { 104 | q.items.PushBack(val) 105 | } 106 | 107 | // GetNoWait returns immediately, same as Get(-1). 108 | func (q *Queue) GetNoWait() (interface{}, error) { 109 | return q.Get(-1) 110 | } 111 | 112 | // Get gets an element from Queue. 113 | // If timeout less than 0, If Queue is empty, return (nil, ErrEmptyQueue); 114 | // If timeout equals to 0, block until get a value from Queue; 115 | // If timeout greater than 0, wait timeout seconds until get a value from Queue, 116 | // if timeout passed, return (nil, ErrEmptyQueue). 117 | func (q *Queue) Get(timeout time.Duration) (interface{}, error) { 118 | q.mutex.Lock() 119 | q.clearPending() 120 | isempty := q.isempty() 121 | if timeout < 0 && isempty { 122 | q.mutex.Unlock() 123 | return nil, ErrEmptyQueue 124 | } 125 | 126 | if !isempty { 127 | v := q.get() 128 | q.notifyPutter(nil) 129 | q.mutex.Unlock() 130 | return v, nil 131 | } 132 | 133 | e := q.newGetter() 134 | q.mutex.Unlock() 135 | w := e.Value.(waiter) 136 | 137 | var v interface{} 138 | if timeout == 0 { 139 | v = <-w 140 | } else { 141 | select { 142 | case v = <-w: 143 | case <-time.After(timeout): 144 | return nil, ErrEmptyQueue 145 | } 146 | } 147 | q.mutex.Lock() 148 | q.notifyPutter(e) 149 | q.mutex.Unlock() 150 | return v, nil 151 | } 152 | 153 | // PutNoWait puts an element into Queue immediately, same as Put(-1). 154 | func (q *Queue) PutNoWait(val interface{}) error { 155 | return q.Put(val, -1) 156 | } 157 | 158 | // Put puts an element into Queue. 159 | // If timeout less than 0, If Queue is full, return (nil, ErrFullQueue); 160 | // If timeout equals to 0, block until put a value into Queue; 161 | // If timeout greater than 0, wait timeout seconds until put a value into Queue, 162 | // if timeout passed, return (nil, ErrFullQueue). 163 | func (q *Queue) Put(val interface{}, timeout time.Duration) error { 164 | q.mutex.Lock() 165 | q.clearPending() 166 | isfull := q.isfull() 167 | if timeout < 0 && isfull { 168 | q.mutex.Unlock() 169 | return ErrFullQueue 170 | } 171 | 172 | if !isfull { 173 | if !q.notifyGetter(nil, val) { 174 | q.put(val) 175 | } 176 | q.mutex.Unlock() 177 | return nil 178 | } 179 | 180 | e := q.newPutter() 181 | q.mutex.Unlock() 182 | w := e.Value.(waiter) 183 | if timeout == 0 { 184 | <-w 185 | } else { 186 | select { 187 | case <-w: 188 | case <-time.After(timeout): 189 | return ErrFullQueue 190 | } 191 | } 192 | 193 | q.mutex.Lock() 194 | if !q.notifyGetter(e, val) { 195 | q.put(e) 196 | } 197 | q.mutex.Unlock() 198 | return nil 199 | } 200 | 201 | func (q *Queue) size() int { 202 | return q.items.Len() 203 | } 204 | 205 | // Size returns the size of Queue. 206 | func (q *Queue) Size() int { 207 | q.mutex.Lock() 208 | defer q.mutex.Unlock() 209 | return q.size() 210 | } 211 | 212 | func (q *Queue) isempty() bool { 213 | return (q.size() == 0) 214 | } 215 | 216 | // IsEmpty returns true if Queue is empty. 217 | func (q *Queue) IsEmpty() bool { 218 | q.mutex.Lock() 219 | defer q.mutex.Unlock() 220 | return q.isempty() 221 | } 222 | 223 | func (q *Queue) isfull() bool { 224 | return (q.maxSize > 0 && q.maxSize <= q.size()) 225 | } 226 | 227 | // IsFull returns true if Queue is full, always returns false if maxSize is 0. 228 | func (q *Queue) IsFull() bool { 229 | q.mutex.Lock() 230 | defer q.mutex.Unlock() 231 | return q.isfull() 232 | } 233 | -------------------------------------------------------------------------------- /queue_test.go: -------------------------------------------------------------------------------- 1 | package goqueue 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestUnBlockGetPut(t *testing.T) { 11 | value := 8888 12 | 13 | fmt.Println("Test unblock get/put, should no error...") 14 | queue := New(1) 15 | queue.PutNoWait(value) 16 | val, err := queue.GetNoWait() 17 | if err != nil { 18 | t.Fatalf("Unexpect error: %v\n", err) 19 | } else if val.(int) != value { 20 | t.Fatalf("Expect %v, got %v\n", value, val.(int)) 21 | } 22 | 23 | fmt.Println("Test unblock get/put, error should show up...") 24 | queue = New(1) 25 | _, err = queue.GetNoWait() 26 | if err == nil { 27 | t.Fatalf("No error show up\n") 28 | } 29 | queue.PutNoWait(value) 30 | err = queue.PutNoWait(value + 1) 31 | if err == nil { 32 | t.Fatalf("No error show up\n") 33 | } 34 | val, err = queue.GetNoWait() 35 | if err != nil { 36 | t.Fatalf("Unexpect error: %v\n", err) 37 | } else if val.(int) != value { 38 | t.Fatalf("Expect %v, got %v\n", value, val.(int)) 39 | } 40 | } 41 | 42 | func TestFIFO(t *testing.T) { 43 | size := 3 44 | queue := New(size) 45 | 46 | fmt.Println("Test FIFO...") 47 | for i := 0; i < size; i++ { 48 | queue.PutNoWait(i) 49 | } 50 | for i := 0; i < size; i++ { 51 | val, err := queue.GetNoWait() 52 | if err != nil { 53 | t.Fatalf("Unexpect error: %v\n", err) 54 | } else if val.(int) != i { 55 | t.Fatalf("Queue is not FIFO") 56 | } 57 | } 58 | if queue.Size() != 0 { 59 | t.Fatalf("Something wrong, Queue has %d more items.\n", queue.Size()) 60 | } 61 | } 62 | 63 | func TestBlockGetWithTimeout(t *testing.T) { 64 | wg := &sync.WaitGroup{} 65 | queue := New(1) 66 | 67 | get := func(timeout time.Duration, value int, noErr bool) { 68 | defer wg.Done() 69 | val, err := queue.Get(timeout) 70 | if noErr { 71 | if err == nil && val.(int) != value { 72 | t.Fatalf("Expect %v, got %v\n", value, val.(int)) 73 | } else if err != nil { 74 | t.Fatalf("Unexpect error: %v\n", err) 75 | } 76 | } else { 77 | if err == nil { 78 | t.Fatalf("Wanted EmptyQueueError, but got nil\n") 79 | } else if err != ErrEmptyQueue { 80 | t.Fatalf("Expect %v, got %v\n", ErrEmptyQueue, err) 81 | } 82 | } 83 | } 84 | 85 | put := func(d time.Duration, value int) { 86 | time.Sleep(d) 87 | queue.PutNoWait(value) 88 | } 89 | 90 | fmt.Println("Test block get without timeout, wait until an element available...") 91 | wg.Add(1) 92 | go put(3*time.Second, 9999) 93 | go get(0, 9999, true) 94 | wg.Wait() 95 | 96 | fmt.Println("Test block get without timeout, wait forever...") 97 | done := make(chan bool) 98 | queue2 := New(1) 99 | go func() { 100 | queue2.Get(0) 101 | done <- true 102 | }() 103 | select { 104 | case <-done: 105 | t.Fatalf("Queue.Get returned even though no element in Queue\n") 106 | case <-time.After(2 * time.Second): 107 | } 108 | queue2.PutNoWait(0) 109 | <-done 110 | close(done) 111 | 112 | fmt.Println("Test block get with timeout, if an element available before timeout, return immediately...") 113 | wg.Add(1) 114 | go put(2*time.Second, 8888) 115 | go get(3*time.Second, 8888, true) 116 | wg.Wait() 117 | 118 | fmt.Println("Test block get with timeout, if no element is available within timeout, return EmptyQueueError...") 119 | wg.Add(1) 120 | go put(4*time.Second, 7777) 121 | go get(2*time.Second, 7777, false) 122 | wg.Wait() 123 | } 124 | 125 | func TestBlockPutWithTimeout(t *testing.T) { 126 | queue := New(1) 127 | queue.PutNoWait(1111) 128 | wg := &sync.WaitGroup{} 129 | 130 | get := func(d time.Duration) { 131 | defer wg.Done() 132 | time.Sleep(d) 133 | queue.GetNoWait() 134 | } 135 | 136 | put := func(timeout time.Duration, value int, noErr bool) { 137 | defer wg.Done() 138 | err := queue.Put(value, timeout) 139 | if noErr { 140 | if err != nil { 141 | t.Fatalf("Unexpect error: %v\n", err) 142 | } 143 | } else { 144 | if err == nil { 145 | t.Fatalf("Wanted FullQueueError, but got nil\n") 146 | } else if err != ErrFullQueue { 147 | t.Fatalf("Expect %v, got %v\n", ErrFullQueue, err) 148 | } 149 | } 150 | } 151 | 152 | fmt.Println("Test block put without timeout, wait until a free slot available...") 153 | go get(2 * time.Second) 154 | go put(0, 9999, true) 155 | wg.Add(2) 156 | wg.Wait() 157 | 158 | fmt.Println("Test block put without timeout, wait forever...") 159 | queue2 := New(1) 160 | queue2.PutNoWait(1111) 161 | done := make(chan bool) 162 | go func() { 163 | queue2.Put(0, 3*time.Second) 164 | done <- true 165 | }() 166 | select { 167 | case <-done: 168 | t.Fatalf("Queue.Put returned even though no free slot in Queue\n") 169 | case <-time.After(2 * time.Second): 170 | } 171 | queue2.GetNoWait() 172 | <-done 173 | 174 | fmt.Println("Test block put with timeout, if a free slot is available before timeout, return immediately...") 175 | go get(2 * time.Second) 176 | go put(3*time.Second, 8888, true) 177 | wg.Add(2) 178 | wg.Wait() 179 | 180 | fmt.Println("Test block put with timeout, if no free slot is available within timeout, return FullQueueError...") 181 | go get(4 * time.Second) 182 | go put(2*time.Second, 7777, false) 183 | wg.Add(2) 184 | wg.Wait() 185 | } 186 | 187 | func TestConcurrentPutGet(t *testing.T) { 188 | queue := New(50) 189 | wg := &sync.WaitGroup{} 190 | 191 | fmt.Println("Test concurrent Get/Put...") 192 | go func() { 193 | defer wg.Done() 194 | dones := [10]chan bool{} 195 | for i := 0; i < 10; i++ { 196 | dones[i] = make(chan bool, 1) 197 | go func(i int) { 198 | for j := 1; j <= 100; j++ { 199 | queue.Put(i*j, 0) 200 | } 201 | dones[i] <- true 202 | }(i) 203 | } 204 | for i := 0; i < 10; i++ { 205 | <-dones[i] 206 | } 207 | }() 208 | 209 | go func() { 210 | defer wg.Done() 211 | dones := [9]chan bool{} 212 | for i := 0; i < 9; i++ { 213 | dones[i] = make(chan bool, 1) 214 | go func(i int) { 215 | for j := 1; j <= 100; j++ { 216 | queue.Get(0) 217 | } 218 | dones[i] <- true 219 | }(i) 220 | } 221 | for i := 0; i < 9; i++ { 222 | <-dones[i] 223 | } 224 | for i := 0; i < 50; i++ { 225 | queue.Get(0) 226 | } 227 | }() 228 | 229 | wg.Add(2) 230 | wg.Wait() 231 | 232 | if queue.Size() != 50 { 233 | t.Fatalf("Except Queue size %d, got %d.\n", 50, queue.Size()) 234 | } 235 | if queue.putters.Len() != 0 { 236 | t.Fatalf("Not right, still has %d Put operators is pending.\n", queue.putters.Len()) 237 | } 238 | if queue.getters.Len() != 0 { 239 | t.Fatalf("Not right, still has %d Get operators is pending.\n", queue.getters.Len()) 240 | } 241 | 242 | for i := 0; i < 30; i++ { 243 | queue.Get(0) 244 | } 245 | if queue.Size() != 20 { 246 | t.Fatalf("Expect Queue size is %d, got %d.\n", 30, queue.Size()) 247 | } 248 | 249 | for i := 0; i < 20; i++ { 250 | queue.GetNoWait() 251 | } 252 | if queue.Size() != 0 { 253 | t.Fatalf("Queue is not empty, still has %d items.\n", queue.Size()) 254 | } 255 | } 256 | --------------------------------------------------------------------------------