|
@ -0,0 +1,152 @@ |
|
|
|
|
|
package common |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
The purpose of CList is to provide a goroutine-safe linked-list. |
|
|
|
|
|
NOTE: Not all methods of container/list are (yet) implemented. |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
|
"sync" |
|
|
|
|
|
"sync/atomic" |
|
|
|
|
|
"unsafe" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// CElement is an element of a linked-list
|
|
|
|
|
|
// Traversal from a CElement are goroutine-safe.
|
|
|
|
|
|
type CElement struct { |
|
|
|
|
|
next unsafe.Pointer |
|
|
|
|
|
wg *sync.WaitGroup |
|
|
|
|
|
Value interface{} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Blocking implementation of Next().
|
|
|
|
|
|
// If return is nil, this element was removed from the list.
|
|
|
|
|
|
func (e *CElement) NextWait() *CElement { |
|
|
|
|
|
e.wg.Wait() |
|
|
|
|
|
return e.Next() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (e *CElement) Next() *CElement { |
|
|
|
|
|
next := atomic.LoadPointer(&e.next) |
|
|
|
|
|
if next == nil { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
return (*CElement)(next) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// CList represents a linked list.
|
|
|
|
|
|
// The zero value for CList is an empty list ready to use.
|
|
|
|
|
|
// Operations are goroutine-safe.
|
|
|
|
|
|
type CList struct { |
|
|
|
|
|
mtx sync.Mutex |
|
|
|
|
|
wg *sync.WaitGroup |
|
|
|
|
|
head *CElement // first element
|
|
|
|
|
|
tail *CElement // last element
|
|
|
|
|
|
len int // list length
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) Init() *CList { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
l.wg = waitGroup1() |
|
|
|
|
|
l.head = nil |
|
|
|
|
|
l.tail = nil |
|
|
|
|
|
l.len = 0 |
|
|
|
|
|
return l |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewCList() *CList { return new(CList).Init() } |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) Len() int { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
return l.len |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) Front() *CElement { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
return l.head |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) FrontWait() *CElement { |
|
|
|
|
|
for { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
head := l.head |
|
|
|
|
|
wg := l.wg |
|
|
|
|
|
l.mtx.Unlock() |
|
|
|
|
|
if head == nil { |
|
|
|
|
|
wg.Wait() |
|
|
|
|
|
} else { |
|
|
|
|
|
return head |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) Back() *CElement { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
return l.tail |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) BackWait() *CElement { |
|
|
|
|
|
for { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
tail := l.tail |
|
|
|
|
|
wg := l.wg |
|
|
|
|
|
l.mtx.Unlock() |
|
|
|
|
|
if tail == nil { |
|
|
|
|
|
wg.Wait() |
|
|
|
|
|
} else { |
|
|
|
|
|
return tail |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) PushBack(v interface{}) *CElement { |
|
|
|
|
|
e := &CElement{ |
|
|
|
|
|
next: nil, |
|
|
|
|
|
wg: waitGroup1(), |
|
|
|
|
|
Value: v, |
|
|
|
|
|
} |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
l.len += 1 |
|
|
|
|
|
if l.tail == nil { |
|
|
|
|
|
l.head = e |
|
|
|
|
|
l.tail = e |
|
|
|
|
|
l.wg.Done() |
|
|
|
|
|
return e |
|
|
|
|
|
} else { |
|
|
|
|
|
oldTail := l.tail |
|
|
|
|
|
atomic.StorePointer(&oldTail.next, unsafe.Pointer(e)) |
|
|
|
|
|
l.tail = e |
|
|
|
|
|
oldTail.wg.Done() |
|
|
|
|
|
return e |
|
|
|
|
|
} |
|
|
|
|
|
return e |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (l *CList) RemoveFront() interface{} { |
|
|
|
|
|
l.mtx.Lock() |
|
|
|
|
|
defer l.mtx.Unlock() |
|
|
|
|
|
if l.head == nil { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
oldFront := l.head |
|
|
|
|
|
next := (*CElement)(oldFront.next) |
|
|
|
|
|
l.head = next |
|
|
|
|
|
if next == nil { |
|
|
|
|
|
l.tail = nil |
|
|
|
|
|
l.wg = waitGroup1() |
|
|
|
|
|
} |
|
|
|
|
|
l.len -= 1 |
|
|
|
|
|
atomic.StorePointer(&oldFront.next, unsafe.Pointer(nil)) |
|
|
|
|
|
return oldFront.Value |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func waitGroup1() (wg *sync.WaitGroup) { |
|
|
|
|
|
wg = &sync.WaitGroup{} |
|
|
|
|
|
wg.Add(1) |
|
|
|
|
|
return |
|
|
|
|
|
} |