├── README.md ├── lfreequeue.go ├── lfreequeue_test.go └── watchiterator.go /README.md: -------------------------------------------------------------------------------- 1 | # lfreequeue 2 | 3 | A lock-free queue implementation for golang 4 | 5 | # Installation 6 | 7 | # go get github.com/scryner/lfreequeue 8 | 9 | # Usage 10 | ## Example 1: simple enqueing & dequeing 11 | 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "github.com/scryner/lfreequeue" 17 | ) 18 | 19 | func main() { 20 | q := lfreequeue.NewQueue() 21 | 22 | v1 := "test_string" 23 | v2 := 100 24 | 25 | // enqueing 26 | q.Enqueue(v1) 27 | q.Enqueue(v2) 28 | 29 | // dequeing 30 | r1 := q.Dequeue() 31 | r2 := q.Dequeue() 32 | 33 | fmt.Println(r1, r2) 34 | } 35 | 36 | ## Example 2: iterator 37 | lfreequeue provides iterator. You can use iterator just as a common 'for' loop statement. 38 | 39 | q := lfreequeue.NewQueue() 40 | 41 | q.Enqueue("a string") 42 | q.Enqueue(100) 43 | 44 | for v := range q.Iter() { 45 | switch v2 := v.(type) { 46 | case string: 47 | fmt.Println("print string:", v2) 48 | case int: 49 | fmt.Println("print int:", v2) 50 | default: 51 | fmt.Println("print other:", v2) 52 | } 53 | } 54 | 55 | ## Example 3: watch iterator 56 | Watch iterator is almost same as iterator, except it would not close channel. 57 | It is useful for producer and consumer pattern. 58 | 59 | q := lfreequeue.NewQueue() 60 | 61 | watchIterator := q.NewWatchIterator() 62 | iter := watchIterator.Iter() 63 | defer watchIterator.Close() 64 | 65 | // producer 66 | go func() { 67 | for i := 0; i < 10; i++ { 68 | q.Enqueue(i) 69 | } 70 | }() 71 | 72 | // consumer 73 | L: 74 | for { 75 | select { 76 | case v := <-iter: 77 | fmt.Println("received:", v) 78 | 79 | case <-time.After(1 * time.Second): 80 | fmt.Println("timeouted!") 81 | break L 82 | } 83 | } 84 | 85 | # Author 86 | 87 | Seonghwan Jeong <scryner@gmail.com> -------------------------------------------------------------------------------- /lfreequeue.go: -------------------------------------------------------------------------------- 1 | package lfreequeue 2 | 3 | import ( 4 | "unsafe" 5 | "sync/atomic" 6 | ) 7 | 8 | // private structure 9 | type node struct { 10 | value interface{} 11 | next *node 12 | } 13 | 14 | type queue struct { 15 | dummy *node 16 | tail *node 17 | } 18 | 19 | func newQueue() *queue { 20 | q := new(queue) 21 | q.dummy = new(node) 22 | q.tail = q.dummy 23 | 24 | return q 25 | } 26 | 27 | func (q *queue) enqueue(v interface{}) { 28 | var oldTail, oldTailNext *node 29 | 30 | newNode := new(node) 31 | newNode.value = v 32 | 33 | newNodeAdded := false 34 | 35 | for !newNodeAdded { 36 | oldTail = q.tail 37 | oldTailNext = oldTail.next 38 | 39 | if q.tail != oldTail { 40 | continue 41 | } 42 | 43 | if oldTailNext != nil { 44 | atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.tail)), unsafe.Pointer(oldTail), unsafe.Pointer(oldTailNext)) 45 | continue 46 | } 47 | 48 | newNodeAdded = atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&oldTail.next)), unsafe.Pointer(oldTailNext), unsafe.Pointer(newNode)) 49 | } 50 | 51 | atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.tail)), unsafe.Pointer(oldTail), unsafe.Pointer(newNode)) 52 | } 53 | 54 | func (q *queue) dequeue() (interface{}, bool) { 55 | var temp interface{} 56 | var oldDummy, oldHead *node 57 | 58 | removed := false 59 | 60 | for !removed { 61 | oldDummy = q.dummy 62 | oldHead = oldDummy.next 63 | oldTail := q.tail 64 | 65 | if q.dummy != oldDummy { 66 | continue 67 | } 68 | 69 | if oldHead == nil { 70 | return nil, false 71 | } 72 | 73 | if oldTail == oldDummy { 74 | atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.tail)), unsafe.Pointer(oldTail), unsafe.Pointer(oldHead)) 75 | continue 76 | } 77 | 78 | temp = oldHead.value 79 | removed = atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.dummy)), unsafe.Pointer(oldDummy), unsafe.Pointer(oldHead)) 80 | } 81 | 82 | return temp, true 83 | } 84 | 85 | func (q *queue) iterate(c chan<- interface{}) { 86 | for { 87 | datum, ok := q.dequeue() 88 | if !ok { 89 | break 90 | } 91 | 92 | c <- datum 93 | } 94 | close(c) 95 | } 96 | 97 | func (q *queue) iter() <-chan interface{} { 98 | c := make(chan interface{}) 99 | go q.iterate(c) 100 | return c 101 | } 102 | 103 | // Public structure 104 | type Queue struct { 105 | q *queue 106 | wakeUpNotifiesQueue *queue 107 | } 108 | 109 | func NewQueue() *Queue { 110 | return &Queue{ 111 | q: newQueue(), 112 | wakeUpNotifiesQueue: newQueue(), 113 | } 114 | } 115 | 116 | func (q *Queue) Enqueue(v interface{}) { 117 | q.q.enqueue(v) 118 | 119 | // notify 120 | for notify := range q.wakeUpNotifiesQueue.iter() { 121 | notify2 := notify.(chan int) 122 | 123 | go func(){ 124 | notify2 <- 1 125 | }() 126 | } 127 | } 128 | 129 | func (q *Queue) Dequeue() (interface{}, bool) { 130 | return q.q.dequeue() 131 | } 132 | 133 | func (q *Queue) Iter() <-chan interface{} { 134 | return q.q.iter() 135 | } 136 | 137 | func (q *Queue) Watch() <-chan int { 138 | c := make(chan int) 139 | q.wakeUpNotifiesQueue.enqueue(c) 140 | 141 | return c 142 | } 143 | 144 | func (q *Queue) NewWatchIterator() *watchIterator { 145 | return &watchIterator{ 146 | queue: q, 147 | quit: make(chan int), 148 | } 149 | } -------------------------------------------------------------------------------- /lfreequeue_test.go: -------------------------------------------------------------------------------- 1 | package lfreequeue 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const dataLen = 10000 11 | var q *Queue 12 | var data []string 13 | var mdata map[string]bool 14 | 15 | func isIn(data []string, datum string) (found bool) { 16 | for _, d := range data { 17 | if d == datum { 18 | found = true 19 | return 20 | } 21 | } 22 | 23 | return 24 | } 25 | 26 | func TestInit(t *testing.T) { 27 | mdata = make(map[string]bool) 28 | } 29 | 30 | func TestSingleGoroutine(t *testing.T) { 31 | q = NewQueue() 32 | 33 | v1 := "scryner" 34 | v2 := "skdul" 35 | 36 | q.Enqueue(v1) 37 | q.Enqueue(v2) 38 | 39 | w1, ok := q.Dequeue() 40 | if !ok || w1 != v1 { 41 | t.Errorf("not same: %v, %v", v1, w1) 42 | 43 | } 44 | 45 | w2, ok := q.Dequeue() 46 | if !ok || w2 != v2 { 47 | t.Errorf("not same: %v, %v", v2, w2) 48 | } 49 | } 50 | 51 | func TestConcurrentWrite(t *testing.T) { 52 | 53 | for i := 0; i < dataLen; i++ { 54 | datum := fmt.Sprintf("data_%d", i) 55 | data = append(data, datum) 56 | } 57 | 58 | var wg sync.WaitGroup 59 | 60 | for i := 0; i < dataLen; i++ { 61 | datum := data[i] 62 | 63 | wg.Add(1) 64 | 65 | go func() { 66 | q.Enqueue(datum) 67 | wg.Done() 68 | }() 69 | } 70 | 71 | wg.Wait() 72 | } 73 | 74 | type result struct { 75 | retval string 76 | } 77 | 78 | func TestConcurrentRead(t *testing.T) { 79 | var wg sync.WaitGroup 80 | 81 | var results []*result 82 | for i := 0; i < dataLen; i++ { 83 | ret := new(result) 84 | results = append(results, ret) 85 | 86 | wg.Add(1) 87 | 88 | go func(ret *result) { 89 | datum, _ := q.Dequeue() 90 | datum2 := datum.(string) 91 | ret.retval = datum2 92 | 93 | wg.Done() 94 | }(ret) 95 | } 96 | 97 | wg.Wait() 98 | 99 | for i := 0; i < dataLen; i++ { 100 | ret := results[i] 101 | 102 | if !isIn(data, ret.retval) { 103 | t.Errorf("datum is not in data: %v", ret.retval) 104 | return 105 | } 106 | 107 | if _, ok := mdata[ret.retval]; ok { 108 | t.Errorf("redundant retrieval: %v", ret.retval) 109 | return 110 | } 111 | 112 | mdata[ret.retval] = true 113 | } 114 | 115 | _, ok := q.Dequeue() 116 | if ok { 117 | t.Errorf("queue must be empty") 118 | return 119 | } 120 | } 121 | 122 | func TestConcurrentReadWrite(t *testing.T) { 123 | succEnq := make(chan int) 124 | succDeq := make(chan int) 125 | 126 | for i := 0; i < dataLen * 2; i++ { 127 | go func(i int) { 128 | if i % 2 == 0 { 129 | // enqueue 130 | datum := data[i/2] 131 | q.Enqueue(datum) 132 | succEnq <- 1 133 | 134 | if (i / 2) + 1 == dataLen { 135 | succEnq <- -1 136 | } 137 | } else { 138 | // dequeue 139 | _, ok := q.Dequeue() 140 | if ok { 141 | succDeq <- 1 142 | } else { 143 | succDeq <- 0 144 | } 145 | 146 | if (i / 2) + 1 == dataLen { 147 | succDeq <- -1 148 | } 149 | } 150 | }(i) 151 | } 152 | 153 | var enqSuccess, deqSuccess int 154 | var endEnq, endDeq bool 155 | 156 | for { 157 | if endEnq && endDeq { 158 | break 159 | } 160 | 161 | select { 162 | case i := <-succEnq: 163 | if i < 0 { 164 | endEnq = true 165 | } else { 166 | enqSuccess += i 167 | } 168 | case i := <-succDeq: 169 | if i < 0 { 170 | endDeq = true 171 | } else { 172 | deqSuccess += i 173 | } 174 | } 175 | } 176 | 177 | if enqSuccess != dataLen { 178 | t.Errorf("some %d enqueing operations is wrong", dataLen - enqSuccess) 179 | return 180 | } 181 | 182 | retry := dataLen - deqSuccess 183 | 184 | for i := 0; i < retry; i++ { 185 | _, ok := q.Dequeue() 186 | if !ok { 187 | t.Errorf("retry dequeue failed: total %d retry but, dequeue has %d", retry, i) 188 | return 189 | } 190 | } 191 | 192 | if _, ok := q.Dequeue(); ok { 193 | t.Errorf("queue must be empty") 194 | return 195 | } 196 | } 197 | 198 | func TestIter(t *testing.T) { 199 | v1 := "test1" 200 | v2 := "test2" 201 | 202 | q.Enqueue(v1) 203 | q.Enqueue(v2) 204 | 205 | var data []interface{} 206 | 207 | for datum := range q.Iter() { 208 | data = append(data, datum) 209 | } 210 | 211 | for i, datum := range data { 212 | datum2 := datum.(string) 213 | 214 | var s string 215 | 216 | switch i { 217 | case 0: 218 | s = "test1" 219 | case 1: 220 | s = "test2" 221 | } 222 | 223 | if s != datum2 { 224 | t.Errorf("not matched: %v, %v", s, datum2) 225 | } 226 | } 227 | } 228 | 229 | func TestWatchIterator(t *testing.T) { 230 | watchIterator := q.NewWatchIterator() 231 | iter := watchIterator.Iter() 232 | defer watchIterator.Close() 233 | 234 | timeout := time.After(time.Second * 1) 235 | var timeouted bool 236 | 237 | select { 238 | case <-iter: 239 | timeouted = false 240 | case <-timeout: 241 | timeouted = true 242 | } 243 | 244 | if !timeouted { 245 | t.Errorf("it must be timeouted, because queue may be empty") 246 | return 247 | } 248 | 249 | var poped []int 250 | var wg sync.WaitGroup 251 | 252 | // consumer 253 | wg.Add(1) 254 | go func() { 255 | defer wg.Done() 256 | until := time.After(time.Second * 1) 257 | 258 | for { 259 | select { 260 | case <-until: 261 | return 262 | case v := <-iter: 263 | poped = append(poped, v.(int)) 264 | } 265 | } 266 | }() 267 | 268 | // producer 269 | wg.Add(1) 270 | go func() { 271 | defer wg.Done() 272 | interval := time.Millisecond * 300 273 | 274 | for i := 0; i < 5; i++ { 275 | q.Enqueue(i) 276 | time.Sleep(interval) 277 | } 278 | }() 279 | 280 | wg.Wait() 281 | 282 | for i := 0; i < 3; i++ { 283 | if i != poped[i] { 284 | t.Errorf("poped elements invalid: %d (expected %d)", poped[i], i) 285 | return 286 | } 287 | } 288 | 289 | timeout = time.After(time.Millisecond * 200) 290 | select { 291 | case <-timeout: 292 | case v := <-iter: 293 | poped = append(poped, v.(int)) 294 | } 295 | 296 | for i := 0; i < 5; i++ { 297 | if i != poped[i] { 298 | t.Errorf("poped elements invalid: %d (expected %d)", poped[i], i) 299 | return 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /watchiterator.go: -------------------------------------------------------------------------------- 1 | package lfreequeue 2 | 3 | type watchIterator struct { 4 | queue *Queue 5 | quit chan int 6 | } 7 | 8 | func (w *watchIterator) Iter() <-chan interface{} { 9 | c := make(chan interface{}) 10 | go w.watchAndIterate(c) 11 | return c 12 | } 13 | 14 | func (w *watchIterator) watchAndIterate(c chan<- interface{}) { 15 | for { 16 | datum, ok := w.queue.Dequeue() 17 | 18 | if !ok { 19 | notify := w.queue.Watch() 20 | 21 | select { 22 | case <-notify: 23 | continue 24 | case <-w.quit: 25 | go func() { 26 | <-notify 27 | }() 28 | goto endIteration 29 | } 30 | 31 | } else { 32 | c <- datum 33 | } 34 | } 35 | 36 | endIteration: 37 | close(c) 38 | } 39 | 40 | func (w *watchIterator) Close() { 41 | go func(){ 42 | w.quit <- 1 43 | }() 44 | } --------------------------------------------------------------------------------