sq/libsq/core/runtimez/runtimez.go
Neil O'Toole 6b0918094d
More diff stuff (#406)
* More diff
2024-03-03 23:21:10 -07:00

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
}