mirror of
https://github.com/yusing/godoxy.git
synced 2025-06-09 04:52:35 +02:00
rewrite and fix reference counter
This commit is contained in:
parent
5fa0d47c0d
commit
af14966b09
2 changed files with 97 additions and 21 deletions
|
@ -1,42 +1,41 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type RefCount struct {
|
||||
_ NoCopy
|
||||
|
||||
refCh chan bool
|
||||
notifyZero chan struct{}
|
||||
mu sync.Mutex
|
||||
cond *sync.Cond
|
||||
refCount uint32
|
||||
zeroCh chan struct{}
|
||||
}
|
||||
|
||||
func NewRefCounter() *RefCount {
|
||||
rc := &RefCount{
|
||||
refCh: make(chan bool, 1),
|
||||
notifyZero: make(chan struct{}),
|
||||
refCount: 1,
|
||||
zeroCh: make(chan struct{}),
|
||||
}
|
||||
go func() {
|
||||
refCount := uint32(1)
|
||||
for isAdd := range rc.refCh {
|
||||
if isAdd {
|
||||
refCount++
|
||||
} else {
|
||||
refCount--
|
||||
}
|
||||
if refCount <= 0 {
|
||||
close(rc.notifyZero)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
rc.cond = sync.NewCond(&rc.mu)
|
||||
return rc
|
||||
}
|
||||
|
||||
func (rc *RefCount) Zero() <-chan struct{} {
|
||||
return rc.notifyZero
|
||||
return rc.zeroCh
|
||||
}
|
||||
|
||||
func (rc *RefCount) Add() {
|
||||
rc.refCh <- true
|
||||
atomic.AddUint32(&rc.refCount, 1)
|
||||
}
|
||||
|
||||
func (rc *RefCount) Sub() {
|
||||
rc.refCh <- false
|
||||
if atomic.AddUint32(&rc.refCount, ^uint32(0)) == 0 {
|
||||
rc.mu.Lock()
|
||||
close(rc.zeroCh)
|
||||
rc.cond.Broadcast()
|
||||
rc.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
|
77
internal/utils/ref_count_test.go
Normal file
77
internal/utils/ref_count_test.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRefCounter_AddSub(t *testing.T) {
|
||||
rc := NewRefCounter()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
rc.Add()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
rc.Sub()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
select {
|
||||
case <-rc.Zero():
|
||||
// Expected behavior
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Expected Zero channel to close, but it didn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefCounter_MultipleAddSub(t *testing.T) {
|
||||
rc := NewRefCounter()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
numAdds := 5
|
||||
numSubs := 5
|
||||
wg.Add(numAdds + numSubs)
|
||||
|
||||
for range numAdds {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
rc.Add()
|
||||
}()
|
||||
}
|
||||
|
||||
for range numSubs {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
rc.Sub()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
select {
|
||||
case <-rc.Zero():
|
||||
// Expected behavior
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Expected Zero channel to close, but it didn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefCounter_ZeroInitially(t *testing.T) {
|
||||
rc := NewRefCounter()
|
||||
rc.Sub() // Bring count to zero
|
||||
|
||||
select {
|
||||
case <-rc.Zero():
|
||||
// Expected behavior
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Expected Zero channel to close, but it didn't")
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue