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
|
|
}
|