influxdb/pkg/lifecycle/resource.go

105 lines
2.6 KiB
Go

package lifecycle
import "sync"
// Resource keeps track of references and has some compile time debug hooks
// to help diagnose leaks. It keeps track of if it is open or not and allows
// blocking until all references are released.
type Resource struct {
stmu sync.Mutex // protects state transitions
chmu sync.RWMutex // protects channel mutations
ch chan struct{} // signals references to close
wg sync.WaitGroup // counts outstanding references
}
// Open marks the resource as open.
func (res *Resource) Open() {
res.stmu.Lock()
defer res.stmu.Unlock()
res.chmu.Lock()
res.ch = make(chan struct{})
res.chmu.Unlock()
}
// Close waits for any outstanding references and marks the resource as closed
// so that Acquire returns an error.
func (res *Resource) Close() {
res.stmu.Lock()
defer res.stmu.Unlock()
res.chmu.Lock()
if res.ch != nil {
close(res.ch) // signal any references.
res.ch = nil // stop future Acquires
}
res.chmu.Unlock()
res.wg.Wait() // wait for any acquired references
}
// Opened returns true if the resource is currently open. It may be immediately
// false in the presence of concurrent Open and Close calls.
func (res *Resource) Opened() bool {
res.chmu.RLock()
opened := res.ch != nil
res.chmu.RUnlock()
return opened
}
// Acquire returns a Reference used to keep alive some resource.
func (res *Resource) Acquire() (*Reference, error) {
res.chmu.RLock()
defer res.chmu.RUnlock()
ch := res.ch
if ch == nil {
return nil, resourceClosed()
}
res.wg.Add(1)
return live.track(&Reference{wg: &res.wg, ch: ch}), nil
}
// Reference is an open reference for some resource.
type Reference struct {
once sync.Once
wg *sync.WaitGroup
ch <-chan struct{}
id uint64
}
// Closing returns a channel that will be closed when the associated resource begins closing.
func (ref *Reference) Closing() <-chan struct{} { return ref.ch }
// Release causes the Reference to be freed. It is safe to call multiple times.
func (ref *Reference) Release() {
ref.once.Do(func() {
live.untrack(ref)
ref.wg.Done()
})
}
// Close makes a Reference an io.Closer. It is safe to call multiple times.
func (ref *Reference) Close() error {
ref.Release()
return nil
}
// References is a helper to aggregate a group of references.
type References []*Reference
// Release releases all of the references. It is safe to call multiple times.
func (refs References) Release() {
for _, ref := range refs {
ref.Release()
}
}
// Close makes References an io.Closer. It is safe to call multiple times.
func (refs References) Close() error {
refs.Release()
return nil
}