github.com/grafana/pyroscope@v1.18.0/pkg/util/refctr/refctr.go (about) 1 package refctr 2 3 import "sync" 4 5 type Counter struct { 6 m sync.Mutex 7 c int 8 err error 9 } 10 11 // Inc increments the counter and calls the init function, 12 // if this is the first reference. The call returns an 13 // error only if init call has failed, and the reference 14 // has not been incremented. 15 func (r *Counter) Inc(init func() error) (err error) { 16 r.m.Lock() 17 defer func() { 18 // If initialization fails, we need to make sure 19 // the next call makes another attempt. 20 if err != nil { 21 r.c-- 22 } 23 r.m.Unlock() 24 }() 25 if r.c++; r.c > 1 { 26 return nil 27 } 28 // Mutex is acquired during the call in order to serialize 29 // access to the resources, so that the consequent callers 30 // only have access to them after initialization finishes. 31 return init() 32 } 33 34 // IncErr is identical to Inc, with the only difference that if the 35 // function fails, the error is returned on any further IncErr call, 36 // preventing from calling the faulty initialization function again. 37 func (r *Counter) IncErr(init func() error) (err error) { 38 r.m.Lock() 39 if r.err != nil { 40 err = r.err 41 r.m.Unlock() 42 return err 43 } 44 defer func() { 45 // If initialization fails, we need to make sure 46 // the next call makes another attempt. 47 if err != nil { 48 r.err = err 49 r.c-- 50 } 51 r.m.Unlock() 52 }() 53 if r.c++; r.c > 1 { 54 return nil 55 } 56 // Mutex is acquired during the call in order to serialize 57 // access to the resources, so that the consequent callers 58 // only have access to them after initialization finishes. 59 return init() 60 } 61 62 // Dec decrements the counter and calls the release function, 63 // if this is the last reference. 64 func (r *Counter) Dec(release func()) { 65 r.m.Lock() 66 defer r.m.Unlock() 67 if r.c < 0 { 68 panic("bug: negative reference counter") 69 } 70 if r.c--; r.c < 1 { 71 release() 72 } 73 }