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 }