]> Cypherpunks repositories - gostls13.git/commitdiff
time: add AfterFunc to call a function after a given duration.
authorRoger Peppe <rogpeppe@gmail.com>
Mon, 10 Jan 2011 19:51:38 +0000 (11:51 -0800)
committerRob Pike <r@golang.org>
Mon, 10 Jan 2011 19:51:38 +0000 (11:51 -0800)
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

src/pkg/time/sleep.go
src/pkg/time/sleep_test.go

index 77b7b4a59354a4049b7e280136a58b24c0759d70..3538775adfb28c9dc0864182308a13296d1a9b28 100644 (file)
@@ -11,11 +11,11 @@ import (
        "container/heap"
 )
 
-// The event type represents a single After event.
+// The event type represents a single After or AfterFunc event.
 type event struct {
-       t        int64        // The absolute time that the event should fire.
-       c        chan<- int64 // The channel to send on.
-       sleeping bool         // A sleeper is sleeping for this event.
+       t        int64       // The absolute time that the event should fire.
+       f        func(int64) // The function to call when the event fires.
+       sleeping bool        // A sleeper is sleeping for this event.
 }
 
 type eventHeap []*event
@@ -55,15 +55,30 @@ func sleep(t, ns int64) (int64, os.Error) {
 // on the returned channel.
 func After(ns int64) <-chan int64 {
        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()
        t0 := events[0].t
-       heap.Push(events, &event{t, c, false})
+       heap.Push(events, &event{t, f, false})
        if t < t0 {
                go sleeper()
        }
        eventMutex.Unlock()
-       return c
 }
 
 // sleeper continually looks at the earliest event in the queue, marks it
@@ -102,7 +117,7 @@ func sleeper() {
                        e = events[0]
                }
                for t >= e.t {
-                       e.c <- t
+                       e.f(t)
                        heap.Pop(events)
                        e = events[0]
                }
index e70b84e97c15f6e2aaacab7319cfb77a35930f2c..9e36288f886306225914133c8dbcc9fbb774ca3e 100644 (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) {
        const delay = int64(100e6)
        start := Nanoseconds()