├── .gitignore ├── README.md ├── queue_test.go ├── LICENSE └── queue.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | queue 2 | ===== 3 | 4 | #### Run tasks on seperate goroutines with control 5 | 6 | ### Basic Example 7 | 8 | import "github.com/otium/queue" 9 | 10 | q := queue.NewQueue(func(val interface{}) { 11 | 12 | 13 | }, 20) 14 | for i := 0; i < 200; i++ { 15 | q.Push(i) 16 | } 17 | q.Wait() 18 | 19 | 20 | Documentation: http://godoc.org/github.com/otium/queue 21 | -------------------------------------------------------------------------------- /queue_test.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "testing" 5 | "sync/atomic" 6 | ) 7 | 8 | func TestQueue (t *testing.T) { 9 | 10 | var count int32 = 0 11 | handler := func(val interface{}) { 12 | 13 | atomic.AddInt32(&count, int32(val.(int))) 14 | 15 | } 16 | q := NewQueue(handler, 5) 17 | 18 | for i := 0; i < 200; i++ { 19 | 20 | q.Push(i) 21 | } 22 | 23 | q.Wait() 24 | if count != 19900 { 25 | 26 | t.Fail() 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | // Package queue provides a queue for running tasks concurrently up to a certain limit. 2 | package queue 3 | 4 | import ( 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | type queue struct { 10 | Handler func(interface{}) 11 | ConcurrencyLimit int 12 | push chan interface{} 13 | pop chan struct{} 14 | suspend chan bool 15 | suspended bool 16 | stop chan struct{} 17 | stopped bool 18 | buffer []interface{} 19 | count int 20 | wg sync.WaitGroup 21 | } 22 | 23 | // Queue is the queue 24 | // Queue also has the members Handler and ConcurrencyLimit which can be set at anytime 25 | type Queue struct { 26 | *queue 27 | } 28 | 29 | // Handler is a function that takes any value, and is called every time a task is processed from the queue 30 | type Handler func(interface{}) 31 | 32 | // NewQueue must be called to initialize a new queue. 33 | // The first argument is a Handler 34 | // The second argument is an int which specifies how many operation can run in parallel in the queue, zero means unlimited. 35 | func NewQueue(handler Handler, concurrencyLimit int) *Queue { 36 | 37 | q := &Queue{ 38 | &queue{ 39 | Handler: handler, 40 | ConcurrencyLimit: concurrencyLimit, 41 | push: make(chan interface{}), 42 | pop: make(chan struct{}), 43 | suspend: make(chan bool), 44 | stop: make(chan struct{}), 45 | }, 46 | } 47 | 48 | go q.run() 49 | runtime.SetFinalizer(q, (*Queue).Stop) 50 | return q 51 | } 52 | 53 | // Push pushes a new value to the end of the queue 54 | func (q *Queue) Push(val interface{}) { 55 | q.push <- val 56 | } 57 | 58 | // Stop stops the queue from executing anymore tasks, and waits for the currently executing tasks to finish. 59 | // The queue can not be started again once this is called 60 | func (q *Queue) Stop() { 61 | 62 | q.stop <- struct{}{} 63 | runtime.SetFinalizer(q, nil) 64 | } 65 | 66 | // Wait calls wait on a waitgroup, that waits for all items of the queue to finish execution 67 | func (q *Queue) Wait() { 68 | 69 | q.wg.Wait() 70 | } 71 | 72 | // Count returns the number of currently executing tasks and the number of tasks waiting to be executed 73 | func (q *Queue) Len() (_, _ int) { 74 | 75 | return q.count, len(q.buffer) 76 | } 77 | 78 | func (q *queue) run() { 79 | 80 | defer func() { 81 | q.wg.Add(-len(q.buffer)) 82 | q.buffer = nil 83 | }() 84 | for { 85 | 86 | select { 87 | 88 | case val := <-q.push: 89 | q.buffer = append(q.buffer, val) 90 | q.wg.Add(1) 91 | case <-q.pop: 92 | q.count-- 93 | case suspend := <-q.suspend: 94 | if suspend != q.suspended { 95 | 96 | if suspend { 97 | q.wg.Add(1) 98 | } else { 99 | q.wg.Done() 100 | } 101 | q.suspended = suspend 102 | } 103 | case <-q.stop: 104 | q.stopped = true 105 | } 106 | 107 | if q.stopped && q.count == 0 { 108 | 109 | return 110 | } 111 | 112 | for (q.count < q.ConcurrencyLimit || q.ConcurrencyLimit == 0) && len(q.buffer) > 0 && !(q.suspended || q.stopped) { 113 | 114 | val := q.buffer[0] 115 | q.buffer = q.buffer[1:] 116 | q.count++ 117 | go func() { 118 | defer func() { 119 | q.pop <- struct{}{} 120 | q.wg.Done() 121 | }() 122 | q.Handler(val) 123 | 124 | }() 125 | } 126 | 127 | } 128 | 129 | } 130 | --------------------------------------------------------------------------------