Browse Source

Added CList

pull/1842/head
Jae Kwon 9 years ago
parent
commit
c65e9e7d91
1 changed files with 152 additions and 0 deletions
  1. +152
    -0
      clist.go

+ 152
- 0
clist.go View File

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

Loading…
Cancel
Save