1
0
mirror of https://github.com/golang/go synced 2024-11-22 14:15:05 -07:00

time: add AfterFunc to call a function after a given duration.

The After code is trivially generalisable to provide support
for this, and it is possible to use AfterFunc to do
things that After cannot, such as waiting
for many events at varied times without an overhead
of one goroutine per event.

R=rsc, r
CC=golang-dev
https://golang.org/cl/3905041
This commit is contained in:
Roger Peppe 2011-01-10 11:51:38 -08:00 committed by Rob Pike
parent 23410ced69
commit 212e074be2
2 changed files with 61 additions and 8 deletions

View File

@ -11,11 +11,11 @@ import (
"container/heap" "container/heap"
) )
// The event type represents a single After event. // The event type represents a single After or AfterFunc event.
type event struct { type event struct {
t int64 // The absolute time that the event should fire. t int64 // The absolute time that the event should fire.
c chan<- int64 // The channel to send on. f func(int64) // The function to call when the event fires.
sleeping bool // A sleeper is sleeping for this event. sleeping bool // A sleeper is sleeping for this event.
} }
type eventHeap []*event type eventHeap []*event
@ -55,15 +55,30 @@ func sleep(t, ns int64) (int64, os.Error) {
// on the returned channel. // on the returned channel.
func After(ns int64) <-chan int64 { func After(ns int64) <-chan int64 {
c := make(chan int64, 1) c := make(chan int64, 1)
t := ns + Nanoseconds() after(ns, func(t int64) { c <- t })
return c
}
// AfterFunc waits at least ns nanoseconds before calling f
// in its own goroutine.
func AfterFunc(ns int64, f func()) {
after(ns, func(_ int64) {
go f()
})
}
// after is the implementation of After and AfterFunc.
// When the current time is after ns, it calls f with the current time.
// It assumes that f will not block.
func after(ns int64, f func(int64)) {
t := Nanoseconds() + ns
eventMutex.Lock() eventMutex.Lock()
t0 := events[0].t t0 := events[0].t
heap.Push(events, &event{t, c, false}) heap.Push(events, &event{t, f, false})
if t < t0 { if t < t0 {
go sleeper() go sleeper()
} }
eventMutex.Unlock() eventMutex.Unlock()
return c
} }
// sleeper continually looks at the earliest event in the queue, marks it // sleeper continually looks at the earliest event in the queue, marks it
@ -102,7 +117,7 @@ func sleeper() {
e = events[0] e = events[0]
} }
for t >= e.t { for t >= e.t {
e.c <- t e.f(t)
heap.Pop(events) heap.Pop(events)
e = events[0] e = events[0]
} }

View File

@ -26,6 +26,44 @@ func TestSleep(t *testing.T) {
} }
} }
// Test the basic function calling behavior. Correct queueing
// behavior is tested elsewhere, since After and AfterFunc share
// the same code.
func TestAfterFunc(t *testing.T) {
i := 10
c := make(chan bool)
var f func()
f = func() {
i--
if i >= 0 {
AfterFunc(0, f)
Sleep(1e9)
} else {
c <- true
}
}
AfterFunc(0, f)
<-c
}
func BenchmarkAfterFunc(b *testing.B) {
i := b.N
c := make(chan bool)
var f func()
f = func() {
i--
if i >= 0 {
AfterFunc(0, f)
} else {
c <- true
}
}
AfterFunc(0, f)
<-c
}
func TestAfter(t *testing.T) { func TestAfter(t *testing.T) {
const delay = int64(100e6) const delay = int64(100e6)
start := Nanoseconds() start := Nanoseconds()