github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekaerr/error_pool_private.go (about)

     1  // Copyright © 2020-2021. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekaerr
     7  
     8  import (
     9  	"runtime"
    10  	"sync"
    11  	"sync/atomic"
    12  
    13  	"github.com/qioalice/ekago/v3/internal/ekaletter"
    14  )
    15  
    16  type (
    17  	// ErrorPoolStat is an internal struct that allows you to inspect
    18  	// how Error's pool utilized and it's current state.
    19  	ErrorPoolStat struct {
    20  
    21  		// AllocCalls is how much absolutely new Error objects
    22  		// with included ekaletter.LetterField are created using new RAM slice.
    23  		AllocCalls uint64
    24  
    25  		// NewCalls is how much attempts both of to allocate a new Error objects
    26  		// or pop an one from Error's pool were here.
    27  		//
    28  		// Once again.
    29  		// It contains AllocCalls + popping an oldest (and prepared to reuse)
    30  		// Error objects from its pool.
    31  		NewCalls uint64
    32  
    33  		// ReleaseCalls is how much Error objects were returned to its pool
    34  		// and prepared for being reused.
    35  		ReleaseCalls uint64
    36  	}
    37  )
    38  
    39  // EPS returns an EntryPoolStat object that contains an info about utilizing
    40  // Logger's Entry pool. Using that info you can figure out how often
    41  // a new Logger's Entry objects are created and how often the oldest ones
    42  // are reused.
    43  //
    44  // In 99% cases you don't need to know that stat,
    45  // and you should not to worry about that.
    46  func EPS() (eps ErrorPoolStat) {
    47  	eps.AllocCalls = atomic.LoadUint64(&eps.AllocCalls)
    48  	eps.NewCalls = atomic.LoadUint64(&eps.NewCalls)
    49  	eps.ReleaseCalls = atomic.LoadUint64(&eps.ReleaseCalls)
    50  	return
    51  }
    52  
    53  var (
    54  	// errorPool is the pool of Error (with allocated ekaletter.Letter) objects
    55  	// for being reused.
    56  	errorPool sync.Pool
    57  
    58  	// eps contains current state of Error's pool utilizing,
    59  	// and its copy is returned by EPS() function.
    60  	eps ErrorPoolStat
    61  )
    62  
    63  // allocError creates a new Error object, creates a new ekaletter.Letter object inside,
    64  // performs base initialization and returns it.
    65  func allocError() any {
    66  
    67  	e := new(Error)
    68  	e.letter = new(ekaletter.Letter)
    69  	e.letter.Messages = make([]ekaletter.LetterMessage, 0, 8)
    70  	e.letter.Fields = make([]ekaletter.LetterField, 0, 16)
    71  
    72  	runtime.SetFinalizer(e, releaseErrorForFinalizer)
    73  	e.needSetFinalizer = false
    74  
    75  	// SystemFields is used for saving Error's meta data.
    76  
    77  	e.letter.SystemFields = make([]ekaletter.LetterField, 3)
    78  
    79  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_CLASS_ID].Key = "error_class_id"
    80  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_CLASS_ID].Kind |=
    81  		ekaletter.KIND_FLAG_SYSTEM | ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID
    82  
    83  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_CLASS_NAME].Key = "error_class_name"
    84  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_CLASS_NAME].Kind |=
    85  		ekaletter.KIND_FLAG_SYSTEM | ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_NAME
    86  
    87  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_ERROR_ID].Key = "error_id"
    88  	e.letter.SystemFields[_ERR_SYS_FIELD_IDX_ERROR_ID].Kind |=
    89  		ekaletter.KIND_FLAG_SYSTEM | ekaletter.KIND_SYS_TYPE_EKAERR_UUID
    90  
    91  	atomic.AddUint64(&eps.AllocCalls, 1)
    92  	return e
    93  }
    94  
    95  // acquireError returns a new *Error object from the Error's pool or newly instantiated.
    96  func acquireError() *Error {
    97  	atomic.AddUint64(&eps.NewCalls, 1)
    98  	return errorPool.Get().(*Error).prepare()
    99  }
   100  
   101  // releaseError returns Error to the Error's pool for being reused in the future
   102  // and that Error could be obtained later using acquireError().
   103  func releaseError(e *Error) {
   104  	atomic.AddUint64(&eps.ReleaseCalls, 1)
   105  	errorPool.Put(e.cleanup())
   106  }
   107  
   108  // releaseErrorForFinalizer is a callback for runtime.SetFinalizer()
   109  // that allows to return an Error to its pool if it's gone out of scope
   110  // without automatic returning to its pool by any ekalog.Logger's finisher.
   111  func releaseErrorForFinalizer(e *Error) {
   112  	e.needSetFinalizer = true
   113  	releaseError(e)
   114  }