mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? mutable state time 1) added time-skipping time-source wrapper 2) wrapped the time source of the mutable state/hbuilder with time-skipping features 3) exposed now() from a mutable state interface timer task time 4) added converter from virtual time to real time in task generators 5) added virtual time reference in timer task time validation ## Why? Principles of correct virtual time management - principle-1: all time in mutable state are consistent virtual - principle-2: wall clock time is used timer queues - principle-3: timer-task (re)generation - when mutable state skips time, non-stale timer tasks should be regenerated (workflow execution timeout, pending user timer) - when mutable state skips time, all future timer tasks should reflec the change - principle-4: timer-task fire time validation - when timer queues validate the fire time, the time of the mutable state should be used as the reference time ## How did you test it? - [x] built - [x] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s) - [x] added new functional test(s)
49 lines
1.7 KiB
Go
49 lines
1.7 KiB
Go
package clock
|
|
|
|
import "time"
|
|
|
|
var _ TimeSource = (*TimeSkippingTimeSourceWrapper)(nil)
|
|
|
|
// TimeSkippingTimeSourceWrapper wraps a base TimeSource and adds an offset
|
|
// returned by getOffset to Now() and Since(). The offset is read lazily on
|
|
// every call so callers can back it with live state (e.g. a field on
|
|
// MutableState) without re-wrapping when that state changes.
|
|
//
|
|
// AfterFunc and NewTimer delegate to the base TimeSource and are not
|
|
// affected by the offset.
|
|
type TimeSkippingTimeSourceWrapper struct {
|
|
base TimeSource
|
|
getOffset func() time.Duration
|
|
}
|
|
|
|
// WrapTimeSourceWithTimeSkipping returns a TimeSource that adds getOffset() to
|
|
// the base's Now()/Since() on every call. If getOffset is nil, the wrapper behaves
|
|
// as a pass-through to base.
|
|
func WrapTimeSourceWithTimeSkipping(base TimeSource, getOffset func() time.Duration) TimeSource {
|
|
return &TimeSkippingTimeSourceWrapper{base: base, getOffset: getOffset}
|
|
}
|
|
|
|
func (ts *TimeSkippingTimeSourceWrapper) Now() time.Time {
|
|
t := ts.base.Now()
|
|
if ts.getOffset != nil {
|
|
t = t.Add(ts.getOffset())
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (ts *TimeSkippingTimeSourceWrapper) Since(t time.Time) time.Duration {
|
|
return ts.Now().Sub(t)
|
|
}
|
|
|
|
// AfterFunc delegates to the base TimeSource and does not apply the offset.
|
|
// TODO@time-skipping: examine if there is any need to skip time for this method.
|
|
func (ts *TimeSkippingTimeSourceWrapper) AfterFunc(d time.Duration, f func()) Timer {
|
|
return ts.base.AfterFunc(d, f)
|
|
}
|
|
|
|
// NewTimer delegates to the base TimeSource and does not apply the offset.
|
|
// TODO@time-skipping: examine if there is any need to skip time for this method.
|
|
func (ts *TimeSkippingTimeSourceWrapper) NewTimer(d time.Duration) (<-chan time.Time, Timer) {
|
|
return ts.base.NewTimer(d)
|
|
}
|