chore: delete unused code that doesn't build on Windows (#22443)
parent
1e911cdba6
commit
372c481278
|
@ -1,104 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package lifecycle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var resourceDebugEnabled = os.Getenv("INFLUXDB_EXP_RESOURCE_DEBUG") != ""
|
||||
|
||||
// When in debug mode, we associate each reference an id and with that id the
|
||||
// stack trace that created it. We can't directly refer to the reference here
|
||||
// because we also associate a finalizer to print to stderr if a reference is
|
||||
// leaked, including where it came from if possible.
|
||||
|
||||
func init() {
|
||||
if !resourceDebugEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
// This goroutine will dump all live references and where they were created
|
||||
// when SIGUSR2 is sent to the process.
|
||||
go func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR2)
|
||||
for range ch {
|
||||
live.mu.Lock()
|
||||
for id, pcs := range live.live {
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
fmt.Fprintln(os.Stderr, "=== Live reference with id", id, "created from")
|
||||
summarizeStack(os.Stderr, pcs)
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
}
|
||||
live.mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// resourceClosed returns an error stating that some resource is closed with the
|
||||
// stack trace of the caller embedded.
|
||||
func resourceClosed() error {
|
||||
if !resourceDebugEnabled {
|
||||
return errors.New("resource closed")
|
||||
}
|
||||
|
||||
var buf [4096]byte
|
||||
return fmt.Errorf("resource closed:\n%s", buf[:runtime.Stack(buf[:], false)])
|
||||
}
|
||||
|
||||
// liveReferences keeps track of the stack traces of all of the live references.
|
||||
type liveReferences struct {
|
||||
mu sync.Mutex
|
||||
id uint64
|
||||
live map[uint64][]uintptr
|
||||
}
|
||||
|
||||
var live = &liveReferences{
|
||||
live: make(map[uint64][]uintptr),
|
||||
}
|
||||
|
||||
// finishId informs the liveReferences that the id is no longer in use.
|
||||
func (l *liveReferences) untrack(r *Reference) {
|
||||
if !resourceDebugEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
delete(l.live, r.id)
|
||||
runtime.SetFinalizer(r, nil)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// withFinalizer associates a finalizer with the Reference that will cause it
|
||||
// to print a leak message if it is not closed before it is garbage collected.
|
||||
func (l *liveReferences) track(r *Reference) *Reference {
|
||||
if !resourceDebugEnabled {
|
||||
return r
|
||||
}
|
||||
|
||||
var buf [32]uintptr
|
||||
pcs := append([]uintptr(nil), buf[:runtime.Callers(3, buf[:])]...)
|
||||
|
||||
l.mu.Lock()
|
||||
r.id, l.id = l.id, l.id+1
|
||||
l.live[r.id] = pcs
|
||||
l.mu.Unlock()
|
||||
|
||||
runtime.SetFinalizer(r, func(r *Reference) {
|
||||
l.leaked(r)
|
||||
r.Release()
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// leaked prints a loud message on stderr that the Reference was leaked and
|
||||
// what was responsible for calling it.
|
||||
func (l *liveReferences) leaked(r *Reference) {
|
||||
if !resourceDebugEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
pcs, ok := l.live[r.id]
|
||||
l.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
fmt.Fprintln(os.Stderr, "=== Leaked a reference with no stack associated!? ===")
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
fmt.Fprintln(os.Stderr, "=== Leaked a reference! Created from")
|
||||
summarizeStack(os.Stderr, pcs)
|
||||
fmt.Fprintln(os.Stderr, "=====================================================")
|
||||
}
|
||||
|
||||
// summarizeStack prints a line for each stack entry in the pcs to the writer.
|
||||
func summarizeStack(w io.Writer, pcs []uintptr) {
|
||||
frames := runtime.CallersFrames(pcs)
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
fmt.Fprintf(w, " %s:%s:%d\n",
|
||||
frame.Function,
|
||||
filepath.Base(frame.File),
|
||||
frame.Line)
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package lifecycle
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestReferenceLeak is only useful to test printing with the debug build tag.
|
||||
func TestReferenceLeak(t *testing.T) {
|
||||
var res Resource
|
||||
res.Open()
|
||||
res.Acquire()
|
||||
runtime.GC()
|
||||
}
|
Loading…
Reference in New Issue