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  }