Why a Timer variable assigned with time.Afterfunc executes its function even though outer func exits

47 views Asked by At

I have a forever loop that has a Timer variable that executes a function after some time.

package main

import (
    fmt "fmt"
    "time"
)

func exe() {
    fmt.Println("10-seconds-of-time-is-done")
}

func t() {
    var timerCheck *time.Timer
    fmt.Println("here's the timerCheck", timerCheck)
    for {
        timerCheck = time.AfterFunc(10*time.Second, func() { exe() })
        fmt.Println("exiting-from-function-t")
        return
    }
}

func main() {
    t()
    fmt.Println("waiting-inside-main")
    time.Sleep(20 * time.Second)
}

This is the output. I don't understand the function t() returns immediately but timerCheck executes its exe() function after 10 second.

here's the timerCheck <nil>
exiting-from-function-t
waiting-inside-main
10-seconds-of-time-is-done

How is this possible? In the standart library, it says as follows regarding AfterFunc

// AfterFunc waits for the duration to elapse and then calls f
// in its own goroutine. It returns a Timer that can
// be used to cancel the call using its Stop method.
func AfterFunc(d Duration, f func()) *Timer {
    t := &Timer{
        r: runtimeTimer{
            when: when(d),
            f:    goFunc,
            arg:  f,
        },
    }
    startTimer(&t.r)
    return t
}

I expect everything related timerCheck` should be freed since outer function's job is done, basically this function's stack is freed (I guess :/).

In order to check stack/heap allocations, the output is as follows;

./main.go:9:13: ... argument does not escape
./main.go:9:14: "10-seconds-of-time-is-done" escapes to heap
./main.go:14:13: ... argument does not escape
./main.go:14:14: "here's the timerCheck" escapes to heap
./main.go:16:47: func literal escapes to heap
./main.go:17:14: ... argument does not escape
./main.go:17:15: "exiting-from-function-t" escapes to heap
./main.go:24:13: ... argument does not escape
./main.go:24:14: "waiting-inside-main" escapes to heap

func literal escapes to heap is it because of this, so that exe function still runs?

I need some internal guidance here regarding how does this timerCheck and exe() function still works after 10 seconds? even though the function t() returns immediately. I didn't expect this log to be printed 10-seconds-of-time-is-done. Because function t() is done already.

Example code is executable and can be tried.

1

There are 1 answers

0
Burak Serdar On

The timer variable lives even after the function declaring it returns, because the timer creates a goroutine referencing to it. The goroutine waits for the given duration, sends its signal or calls the AfterFunc, and then terminates. Only after that the timer variable can be garbage collected. The AfterFunc executed asynchronously.