timer

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2024 License: MIT Imports: 2 Imported by: 43

README

Go Timer implementation with a fixed Reset behavior

GoDoc Go Report Card

This is a lightweight timer implementation which is a drop-in replacement for Go's Timer. Reset behaves as one would expect and drains the timer.C channel automatically. The core design of this package is similar to the original runtime timer implementation.

These two lines are equivalent except for saving some garbage:

t.Reset(x)

t := timer.NewTimer(x)

See issues:

Quote from the Timer Go doc reference:

Reset changes the timer to expire after duration d. It returns true if the timer had been active, false if the timer had expired or been stopped.

To reuse an active timer, always call its Stop method first and—if it had expired—drain the value from its channel. For example: [...] This should not be done concurrent to other receives from the Timer's channel.

Note that it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring. Reset should always be used in concert with Stop, as described above. The return value exists to preserve compatibility with existing programs.

Broken behavior sample

Sample 1
package main

import (
    "log"
    "time"
)

func main() {
	start := time.Now()

	// Start a new timer with a timeout of 1 second.
	timer := time.NewTimer(1 * time.Second)

	// Wait for 2 seconds.
	// Meanwhile the timer fired and filled the channel.
	time.Sleep(2 * time.Second)

	// Reset the timer. This should act exactly as creating a new timer.
	timer.Reset(1 * time.Second)

	// However this will fire immediately, because the channel was not drained.
	// See issue: https://github.com/golang/go/issues/11513
	<-timer.C

	if int(time.Since(start).Seconds()) != 3 {
		log.Fatalf("took ~%v seconds, should be ~3 seconds\n", int(time.Since(start).Seconds()))
	}
}
Sample 2
package main

import "time"

const (
	keepaliveInterval = 2 * time.Millisecond
)

var (
	resetC = make(chan struct{}, 1)
)

func main() {
	go keepaliveLoop()

	// Sample routine triggering the reset.
	// Example: this could be due to incoming peer requests and
	// a keepalive check should be reset to the max keepalive timeout.
	for i := 0; i < 1000; i++ {
		time.Sleep(time.Millisecond)
		resetKeepalive()
	}
}

func resetKeepalive() {
	// Don't block if there is already a reset request.
	select {
	case resetC <- struct{}{}:
	default:
	}
}

func keepaliveLoop() {
	t := time.NewTimer(keepaliveInterval)

	for {
		select {
		case <-resetC:
			time.Sleep(3 * time.Millisecond) // Simulate some reset work...
			t.Reset(keepaliveInterval)
		case <-t.C:
			ping()
			t.Reset(keepaliveInterval)
		}
	}
}

func ping() {
	panic("ping must not be called in this example")
}

Documentation

Overview

Package timer is a Go timer implementation with a fixed Reset behavior.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Timer

type Timer struct {
	C <-chan time.Time
	// contains filtered or unexported fields
}

The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc. A Timer must be created with NewTimer. NewStoppedTimer or AfterFunc.

func NewStoppedTimer

func NewStoppedTimer() *Timer

NewStoppedTimer creates a new stopped Timer.

func NewTimer

func NewTimer(d time.Duration) *Timer

NewTimer creates a new Timer that will send the current time on its channel after at least duration d.

func (*Timer) Reset

func (t *Timer) Reset(d time.Duration) bool

Reset changes the timer to expire after duration d. It returns true if the timer had been active, false if the timer had expired or been stopped. The channel t.C is cleared and calling t.Reset() behaves as creating a new Timer.

func (*Timer) Stop

func (t *Timer) Stop() (wasActive bool)

Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped. Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL