diff --git a/rules/wrapper/wrapper.go b/rules/wrapper/wrapper.go index 9252539f..f7388d2a 100644 --- a/rules/wrapper/wrapper.go +++ b/rules/wrapper/wrapper.go @@ -11,9 +11,9 @@ type RuleWrapper struct { C.Rule disabled atomic.Bool hitCount atomic.Uint64 - hitAt atomic.Int64 // unix microsecond + hitAt atomicTime missCount atomic.Uint64 - missAt atomic.Int64 // unix microsecond + missAt atomicTime } func (r *RuleWrapper) IsDisabled() bool { @@ -29,7 +29,7 @@ func (r *RuleWrapper) HitCount() uint64 { } func (r *RuleWrapper) HitAt() time.Time { - return time.UnixMicro(r.hitAt.Load()) + return r.hitAt.Load() } func (r *RuleWrapper) MissCount() uint64 { @@ -37,7 +37,7 @@ func (r *RuleWrapper) MissCount() uint64 { } func (r *RuleWrapper) MissAt() time.Time { - return time.UnixMicro(r.missAt.Load()) + return r.missAt.Load() } func (r *RuleWrapper) Unwrap() C.Rule { @@ -46,12 +46,12 @@ func (r *RuleWrapper) Unwrap() C.Rule { func (r *RuleWrapper) Hit() { r.hitCount.Add(1) - r.hitAt.Store(time.Now().UnixMicro()) + r.hitAt.Store(time.Now()) } func (r *RuleWrapper) Miss() { r.missCount.Add(1) - r.missAt.Store(time.Now().UnixMicro()) + r.missAt.Store(time.Now()) } func (r *RuleWrapper) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) { @@ -70,3 +70,41 @@ func (r *RuleWrapper) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (boo func NewRuleWrapper(rule C.Rule) C.RuleWrapper { return &RuleWrapper{Rule: rule} } + +// atomicTime is a wrapper of [atomic.Int64] to provide atomic time storage. +// it only saves unix nanosecond export from time.Time. +// unlike atomic.TypedValue[time.Time] always escapes a new time.Time to heap when storing. +// that will lead to higher GC pressure during high frequency writes. +// be careful, it discards monotime so should not be used for internal time comparisons. +type atomicTime struct { + i atomic.Int64 +} + +func (t *atomicTime) Load() time.Time { + return time.Unix(0, t.i.Load()) +} + +func (t *atomicTime) Store(v time.Time) { + t.i.Store(v.UnixNano()) +} + +func (t *atomicTime) Swap(v time.Time) time.Time { + return time.Unix(0, t.i.Swap(v.UnixNano())) +} + +func (t *atomicTime) CompareAndSwap(old, new time.Time) bool { + return t.i.CompareAndSwap(old.UnixNano(), new.UnixNano()) +} + +func (t *atomicTime) MarshalText() ([]byte, error) { + return t.Load().MarshalText() +} + +func (t *atomicTime) UnmarshalText(text []byte) error { + var v time.Time + if err := v.UnmarshalText(text); err != nil { + return err + } + t.Store(v) + return nil +}