mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-20 14:41:31 +03:00
6b0918094d
* More diff
90 lines
2.3 KiB
Go
90 lines
2.3 KiB
Go
// Package runtimez provides functionality supplemental to stdlib's runtime pkg.
|
|
package runtimez
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/lg"
|
|
"github.com/neilotoole/sq/libsq/core/lg/lga"
|
|
)
|
|
|
|
// MemStatsRefresh determines how often the value returned by MemStats is
|
|
// refreshed.
|
|
var MemStatsRefresh = time.Millisecond * 250
|
|
|
|
var (
|
|
memStats *runtime.MemStats
|
|
memStatsMu sync.Mutex
|
|
memStatsNextRefresh time.Time
|
|
)
|
|
|
|
// MemStats returns the most recent memory stats. To avoid slamming the runtime,
|
|
// stats are retrieved periodically per [MemStatsRefresh]. The caller must not
|
|
// mutate the returned [*runtime.MemStats], as the same instance is returned to
|
|
// other callers.
|
|
func MemStats() *runtime.MemStats {
|
|
now := time.Now()
|
|
memStatsMu.Lock()
|
|
if now.After(memStatsNextRefresh) {
|
|
var ms runtime.MemStats
|
|
runtime.ReadMemStats(&ms)
|
|
memStats = &ms
|
|
memStatsNextRefresh = now.Add(MemStatsRefresh)
|
|
}
|
|
memStatsMu.Unlock()
|
|
|
|
return memStats
|
|
}
|
|
|
|
// StartMemStatsTracker starts a goroutine that tracks memory stats, returning
|
|
// the peak values of [runtime.MemStats.Sys], [runtime.MemStats.TotalAlloc] and
|
|
// [runtime.MemStats.PauseTotalNs]. The goroutine sleeps for sampleFreq between
|
|
// each sample and exits when ctx is done.
|
|
//
|
|
//nolint:revive // datarace
|
|
func StartMemStatsTracker(ctx context.Context) (sys, allocs, gcPauseNs *atomic.Uint64) {
|
|
lg.FromContext(ctx).Info("Starting memory stats tracker", lga.Freq, MemStatsRefresh)
|
|
|
|
sys = &atomic.Uint64{}
|
|
allocs = &atomic.Uint64{}
|
|
gcPauseNs = &atomic.Uint64{}
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(MemStatsRefresh)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
// Use our (cached) MemStats, which is updated periodically.
|
|
stats := MemStats()
|
|
if stats.Sys > sys.Load() {
|
|
sys.Store(stats.Sys)
|
|
}
|
|
|
|
allocs.Store(stats.TotalAlloc)
|
|
gcPauseNs.Store(stats.PauseTotalNs)
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
// We're about to exit; this time we do a call to the (uncached)
|
|
// runtime.ReadMemStats, just so we have fresh values on exit.
|
|
var ms runtime.MemStats
|
|
runtime.ReadMemStats(&ms)
|
|
if ms.Sys > sys.Load() {
|
|
sys.Store(stats.Sys)
|
|
}
|
|
|
|
allocs.Store(ms.TotalAlloc)
|
|
gcPauseNs.Store(ms.PauseTotalNs)
|
|
return
|
|
case <-ticker.C:
|
|
}
|
|
}
|
|
}()
|
|
|
|
return sys, allocs, gcPauseNs
|
|
}
|