Documentation
¶
Overview ¶
Package timeutil provides serializable timer types that behave like time.AfterFunc but can be snapshotted, serialized, and restored across process restarts or long-lived workflows.
The main type is Timer, which keeps its runtime behaviour, including automatic callback execution via a background time.Timer, while exposing deterministic state through TimerSnapshot. Snapshots can be marshaled to JSON, stored, and later passed to RestoreTimer to obtain a fresh timer instance. Callbacks are runtime-only; they must be reattached after restoration with Timer.SetCallback or Timer.Reset.
Watchdog is created inactive and armed with Watchdog.Start or Watchdog.Reset. Watchdog.Stop allows reuse; Watchdog.Close permanently disables it.
Basic usage:
// Create timer with automatic callback execution.
timer := timeutil.AfterFunc(5*time.Second, func() {
log.Println("Timer expired!")
})
// Persist the timer state.
snap := timer.Snapshot()
data, _ := json.Marshal(snap)
// Restore later and reattach callbacks.
var restoredSnap timeutil.TimerSnapshot
_ = json.Unmarshal(data, &restoredSnap)
restored, _ := timeutil.RestoreTimer(&restoredSnap)
restored.SetCallback(func() {
log.Println("Restored timer expired!")
})
All timer operations are thread-safe and can be called concurrently from multiple goroutines.
Index ¶
- type Error
- type Timer
- func (t *Timer) Duration() time.Duration
- func (t *Timer) Elapsed() time.Duration
- func (t *Timer) Expired() bool
- func (t *Timer) Left() time.Duration
- func (t *Timer) MarshalJSON() ([]byte, error)
- func (t *Timer) Reset(duration time.Duration)
- func (t *Timer) SetCallback(f func())
- func (t *Timer) Snapshot() *TimerSnapshot
- func (t *Timer) StartTime() time.Time
- func (t *Timer) State() TimerState
- func (t *Timer) Stop() bool
- func (t *Timer) StopTime() time.Time
- func (t *Timer) ToJSON() ([]byte, error)
- func (t *Timer) UnmarshalJSON(data []byte) error
- func (t *Timer) UpdateState()
- type TimerSnapshot
- type TimerState
- type Watchdog
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Error ¶
type Error string
Error is a package-level error type used for simple sentinel errors.
const ( // ErrInvalidTimerSnapshot is returned when a snapshot is nil or contains // invalid timer state and cannot be used to restore a timer. ErrInvalidTimerSnapshot Error = "invalid timer snapshot" )
type Timer ¶
type Timer struct {
// contains filtered or unexported fields
}
Timer represents a timer that can be serialized to/from JSON. It tracks the start time, duration, and current state and can export/import a lightweight TimerSnapshot for storage. Runtime-only fields such as callbacks and the underlying time.Timer are intentionally excluded from the snapshot and must be reattached manually after restoration. Timer automatically manages a real time.Timer for callback execution while it is running.
Example ¶
package main
import (
"fmt"
"time"
"github.com/ghettovoice/timeutil"
)
func main() {
// Create a new timer that expires after a short duration.
// In production code this would typically be several seconds.
timer := timeutil.NewTimer(10 * time.Millisecond)
// Set a callback to execute when timer expires
timer.SetCallback(func() {
// Handle timeout: send response, cleanup, etc.
})
// Check timer state
if timer.Expired() {
// Timer has expired, apply additional actions
fmt.Println("timer expired!")
} else {
fmt.Println("timer is still running")
}
// Get remaining time
left := timer.Left()
if left > 0 {
// Timer is still running
}
// Serialize timer for persistence
data, _ := timer.ToJSON()
// Wait for expiration
time.Sleep(20 * time.Millisecond)
// Later, restore the timer
restoredTimer, _ := timeutil.FromJSON(data)
// Set callback for restored timer
restoredTimer.SetCallback(func() {
fmt.Println("callback fired!")
})
if restoredTimer.Expired() {
// Timer expired while serialized, handle appropriately
fmt.Println("restored timer expired!")
}
time.Sleep(10 * time.Millisecond)
}
Output: timer is still running restored timer expired! callback fired!
func AfterFunc ¶
AfterFunc creates a new Timer with the given duration and callback. The timer is started immediately and the callback will be executed when it expires.
Example ¶
package main
import (
"fmt"
"sync/atomic"
"time"
"github.com/ghettovoice/timeutil"
)
func main() {
var fired int32
// AfterFunc starts the timer and executes the callback in its own goroutine
// when the timer expires.
timer := timeutil.AfterFunc(10*time.Millisecond, func() {
atomic.AddInt32(&fired, 1)
})
// Wait for the real timer to fire.
time.Sleep(25 * time.Millisecond)
fmt.Println(timer.Expired())
fmt.Println(atomic.LoadInt32(&fired) > 0)
}
Output: true true
func FromTime ¶
FromTime creates a new Timer with the given start time and duration. This is useful for recreating timers from serialized data. Unlike FromJSON, this does not automatically call UpdateState(). You should call UpdateState() after creating the timer to check expiration and trigger callbacks.
Example ¶
package main
import (
"fmt"
"time"
"github.com/ghettovoice/timeutil"
)
func main() {
// Recreate a timer that started 5 seconds ago with a 1 second duration.
start := time.Now().Add(-5 * time.Second)
timer := timeutil.FromTime(start, time.Second)
// UpdateState recalculates the timer state and triggers callbacks if needed.
timer.UpdateState()
fmt.Println(timer.Expired())
fmt.Println(timer.State())
}
Output: true expired
func NewTimer ¶
NewTimer creates a new Timer with the given duration. The timer is started immediately.
func RestoreTimer ¶
func RestoreTimer(snap *TimerSnapshot) (*Timer, error)
RestoreTimer recreates a Timer from its snapshot. It returns an error if snap is nil or contains an invalid state. Callback-related fields are left nil; callers should reattach callbacks or restart timers using [SetCallback] / [Reset] as appropriate after restoration.
Example ¶
package main
import (
"fmt"
"time"
"github.com/ghettovoice/timeutil"
)
func main() {
// A snapshot captures only deterministic state; it can be stored or restored
// at any time.
snap := &timeutil.TimerSnapshot{
StartTime: time.Now().Add(-5 * time.Second),
Duration: time.Second,
State: timeutil.TimerStateExpired,
}
timer, err := timeutil.RestoreTimer(snap)
if err != nil {
panic(err)
}
fmt.Println(timer.Expired())
}
Output: true
func (*Timer) Left ¶
Left returns the time remaining until the timer expires. Returns 0 if the timer is expired or stopped.
func (*Timer) MarshalJSON ¶
MarshalJSON implements json.Marshaler.
func (*Timer) Reset ¶
Reset resets the timer with a new duration, starting from now. The callback is preserved - if one was set, it will execute when the new duration expires. To clear the callback, call Stop() first.
func (*Timer) SetCallback ¶
func (t *Timer) SetCallback(f func())
SetCallback sets a function to be executed when the timer expires. Similar to time.AfterFunc, the function is called in its own goroutine. If the timer has already expired, the function will be executed immediately. If the timer is stopped, the function will not be executed. This method automatically starts a real time.Timer to handle callback execution.
func (*Timer) Snapshot ¶
func (t *Timer) Snapshot() *TimerSnapshot
Snapshot returns a serializable copy of the timer state. The returned snapshot can be serialized directly or passed to RestoreTimer to recreate a timer instance with the same timing metadata.
func (*Timer) State ¶
func (t *Timer) State() TimerState
State returns the current timer state in a thread-safe manner.
func (*Timer) Stop ¶
Stop stops the timer and updates its state. If the timer is stopped, the callback will not be executed.
func (*Timer) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
func (*Timer) UpdateState ¶
func (t *Timer) UpdateState()
UpdateState updates the timer's state based on the current time. This is useful for timers created with FromTime or when the caller wants to manually re-check expiration after a period of inactivity.
type TimerSnapshot ¶
type TimerSnapshot struct {
State TimerState `json:"state"`
StartTime time.Time `json:"start_time"`
Duration time.Duration `json:"duration"`
StopTime time.Time `json:"stop_time,omitzero"`
}
TimerSnapshot represents a serializable view of a timer. Only deterministic fields are included so that the snapshot can be safely persisted or transferred between goroutines or processes.
func SnapshotTimer ¶
func SnapshotTimer(t *Timer) *TimerSnapshot
SnapshotTimer safely snapshots the provided timer.
func (*TimerSnapshot) IsValid ¶
func (s *TimerSnapshot) IsValid() bool
IsValid reports whether the snapshot contains a known timer state.
func (*TimerSnapshot) Validate ¶
func (s *TimerSnapshot) Validate() error
Validate returns an error if the snapshot is nil or contains an invalid state, zero start time, or an inconsistent stop time.
type TimerState ¶
type TimerState uint
TimerState represents the current state of a serializable timer.
const ( // TimerStateInvalid is the zero value and represents an unknown or invalid state. TimerStateInvalid TimerState = iota // TimerStateRunning indicates that the timer is active and counting down. TimerStateRunning // TimerStateStopped indicates that the timer was stopped before expiration. TimerStateStopped // TimerStateExpired indicates that the timer has reached its expiration time. TimerStateExpired )
func TimerStateFromString ¶
func TimerStateFromString(s string) TimerState
TimerStateFromString parses a timer state from its textual representation. It returns TimerStateInvalid when the input does not match a known state.
func (TimerState) AppendText ¶
func (s TimerState) AppendText(b []byte) ([]byte, error)
AppendText appends the textual representation of the state to b.
func (TimerState) IsValid ¶
func (s TimerState) IsValid() bool
IsValid reports whether the state is one of the defined timer states.
func (TimerState) MarshalText ¶
func (s TimerState) MarshalText() ([]byte, error)
MarshalText implements encoding.TextMarshaler for JSON/text serialization.
func (TimerState) String ¶
func (s TimerState) String() string
String returns the textual representation of the state, or "invalid" for an unknown state.
func (*TimerState) UnmarshalText ¶
func (s *TimerState) UnmarshalText(data []byte) error
UnmarshalText implements encoding.TextUnmarshaler for JSON/text deserialization.
type Watchdog ¶
type Watchdog struct {
// contains filtered or unexported fields
}
Watchdog invokes a callback after an explicitly started timeout unless reset.
All methods are concurrent-safe and are no-ops on a nil receiver; a zero Watchdog is valid and permanently disabled. Stop allows reuse, while Close permanently disables the watchdog.
Callbacks execute outside the watchdog's mutex and may overlap across armed generations; callers must synchronize callback access to shared state.
Example ¶
package main
import (
"fmt"
"time"
"github.com/ghettovoice/timeutil"
)
func main() {
expired := make(chan struct{})
// NewWatchdog creates an inactive watchdog; Start arms it to fire after
// the configured inactivity period unless it is reset or stopped.
watchdog := timeutil.NewWatchdog(10*time.Millisecond, func() { close(expired) })
defer watchdog.Close()
// Start arms the watchdog without extending an already running countdown.
watchdog.Start()
<-expired
fmt.Println("expired")
}
Output: expired
func NewWatchdog ¶
NewWatchdog creates an inactive watchdog timer; call Start or Reset to arm it. A nonpositive timeout leaves the watchdog permanently disabled. A nil callback is permitted; expiration then invokes nothing.
func (*Watchdog) Close ¶ added in v0.2.0
func (t *Watchdog) Close()
Close permanently disables the watchdog and releases the stored callback. Close is idempotent and, like Stop, does not cancel or wait for a callback already admitted for execution; it may begin or continue after Close returns.
func (*Watchdog) Reset ¶
func (t *Watchdog) Reset()
Reset arms or restarts a full timeout from now whether the watchdog is dormant, stopped, or expired. It is a no-op on a closed or disabled watchdog.