Compare commits

...

2 Commits

Author SHA1 Message Date
tycho garen
7497331f47 change lock 2021-05-12 10:38:47 -04:00
tycho garen
d893463bb8 libs/clist: omit channels and use waitgroups 2021-05-12 10:35:56 -04:00
2 changed files with 46 additions and 58 deletions

View File

@@ -12,7 +12,6 @@ func BenchmarkDetaching(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
start.removed = true
start.DetachNext()
start.DetachPrev()
tmp := nxt
nxt = nxt.Next()
@@ -30,7 +29,6 @@ func BenchmarkRemoved(b *testing.B) {
nxt := start.Next()
b.ResetTimer()
for i := 0; i < b.N; i++ {
start.Removed()
tmp := nxt
nxt = nxt.Next()
start = tmp

View File

@@ -44,14 +44,12 @@ waiting on NextWait() (since it's just a read operation).
*/
type CElement struct {
mtx tmsync.RWMutex
prev *CElement
prevWg *sync.WaitGroup
prevWaitCh chan struct{}
next *CElement
nextWg *sync.WaitGroup
nextWaitCh chan struct{}
removed bool
mtx tmsync.RWMutex
prev *CElement
prevWg *sync.WaitGroup
next *CElement
nextWg *sync.WaitGroup
removed bool
Value interface{} // immutable
}
@@ -94,22 +92,36 @@ func (e *CElement) PrevWait() *CElement {
}
}
// PrevWaitChan can be used to wait until Prev becomes not nil. Once it does,
// channel will be closed.
func (e *CElement) PrevWaitChan() <-chan struct{} {
e.mtx.RLock()
defer e.mtx.RUnlock()
return e.prevWaitCh
}
// NextWaitChan can be used to wait until Next becomes not nil. Once it does,
// channel will be closed.
func (e *CElement) NextWaitChan() <-chan struct{} {
e.mtx.RLock()
defer e.mtx.RUnlock()
wg := e.nextWg
return e.nextWaitCh
ret := make(chan struct{})
go func() {
wg.Wait()
close(ret)
}()
return ret
}
// PrevWaitChan can be used to wait until Prev becomes not nil. Once it does,
// channel will be closed.
func (e *CElement) PrevWaitChan() <-chan struct{} {
e.mtx.RLock()
defer e.mtx.RUnlock()
wg := e.prevWg
ret := make(chan struct{})
go func() {
wg.Wait()
close(ret)
}()
return ret
}
// Nonblocking, may return nil if at the end.
@@ -128,23 +140,6 @@ func (e *CElement) Prev() *CElement {
return prev
}
func (e *CElement) Removed() bool {
e.mtx.RLock()
isRemoved := e.removed
e.mtx.RUnlock()
return isRemoved
}
func (e *CElement) DetachNext() {
e.mtx.Lock()
if !e.removed {
e.mtx.Unlock()
panic("DetachNext() must be called after Remove(e)")
}
e.next = nil
e.mtx.Unlock()
}
func (e *CElement) DetachPrev() {
e.mtx.Lock()
if !e.removed {
@@ -169,11 +164,9 @@ func (e *CElement) SetNext(newNext *CElement) {
// events, new Add calls must happen after all previous Wait calls have
// returned.
e.nextWg = waitGroup1() // WaitGroups are difficult to re-use.
e.nextWaitCh = make(chan struct{})
}
if oldNext == nil && newNext != nil {
e.nextWg.Done()
close(e.nextWaitCh)
}
e.mtx.Unlock()
}
@@ -187,11 +180,9 @@ func (e *CElement) SetPrev(newPrev *CElement) {
e.prev = newPrev
if oldPrev != nil && newPrev == nil {
e.prevWg = waitGroup1() // WaitGroups are difficult to re-use.
e.prevWaitCh = make(chan struct{})
}
if oldPrev == nil && newPrev != nil {
e.prevWg.Done()
close(e.prevWaitCh)
}
e.mtx.Unlock()
}
@@ -204,11 +195,9 @@ func (e *CElement) SetRemoved() {
// This wakes up anyone waiting in either direction.
if e.prev == nil {
e.prevWg.Done()
close(e.prevWaitCh)
}
if e.next == nil {
e.nextWg.Done()
close(e.nextWaitCh)
}
e.mtx.Unlock()
}
@@ -222,7 +211,6 @@ func (e *CElement) SetRemoved() {
type CList struct {
mtx tmsync.RWMutex
wg *sync.WaitGroup
waitCh chan struct{}
head *CElement // first element
tail *CElement // last element
len int // list length
@@ -239,7 +227,6 @@ func newWithMax(maxLength int) *CList {
l.maxLen = maxLength
l.wg = waitGroup1()
l.waitCh = make(chan struct{})
l.head = nil
l.tail = nil
l.len = 0
@@ -303,10 +290,17 @@ func (l *CList) BackWait() *CElement {
// WaitChan can be used to wait until Front or Back becomes not nil. Once it
// does, channel will be closed.
func (l *CList) WaitChan() <-chan struct{} {
l.mtx.Lock()
defer l.mtx.Unlock()
l.mtx.RLock()
defer l.mtx.RUnlock()
wg := l.wg
return l.waitCh
ret := make(chan struct{})
go func() {
wg.Wait()
close(ret)
}()
return ret
}
// Panics if list grows beyond its max length.
@@ -315,20 +309,17 @@ func (l *CList) PushBack(v interface{}) *CElement {
// Construct a new element
e := &CElement{
prev: nil,
prevWg: waitGroup1(),
prevWaitCh: make(chan struct{}),
next: nil,
nextWg: waitGroup1(),
nextWaitCh: make(chan struct{}),
removed: false,
Value: v,
prev: nil,
prevWg: waitGroup1(),
next: nil,
nextWg: waitGroup1(),
removed: false,
Value: v,
}
// Release waiters on FrontWait/BackWait maybe
if l.len == 0 {
l.wg.Done()
close(l.waitCh)
}
if l.len >= l.maxLen {
panic(fmt.Sprintf("clist: maximum length list reached %d", l.maxLen))
@@ -372,7 +363,6 @@ func (l *CList) Remove(e *CElement) interface{} {
// If we're removing the only item, make CList FrontWait/BackWait wait.
if l.len == 1 {
l.wg = waitGroup1() // WaitGroups are difficult to re-use.
l.waitCh = make(chan struct{})
}
// Update l.len