Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: time: Add TickerFunc #68482

Open
sivchari opened this issue Jul 17, 2024 · 5 comments
Open

proposal: time: Add TickerFunc #68482

sivchari opened this issue Jul 17, 2024 · 5 comments
Labels
Milestone

Comments

@sivchari
Copy link
Contributor

sivchari commented Jul 17, 2024

Proposal Details

I propose TickerFunc to time package. This API is similar with time.AfterFunc.
Currently, if we execute some func regularly, we must write the code like following.

func main() {
	t := time.NewTicker(1 * time.Second)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go func() {
		for {
			select {
			case <-t.C:
				println("tick")
			case <-ctx.Done():
				return
			}
		}
	}()
        time.Sleep(N * time.Second)
        cancel()
        <-ctx.Done()
}

As you can see, this code is so complicated to what I want to do and if we forget the context or something to cancel, then this goroutine will be leaked.

TickerFunc(ctx context.Context, d Duration, f func()) *Ticker

The following code is example when I use time.TickerFunc.

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    time.TickerFunc(ctx, 1*time.Second, func() {
        println("tick")
    })
    time.Sleep(N * time.Second)
    cancel()
    <-ctx.Done()
}

func TickerFunc(ctx context.Context, d time.Duration, f func()) *time.Ticker {
    t := time.NewTicker(d)
    go func() {
        for {
            select {
            case <-t.C:
                f()
            case <-ctx.Done():
                t.Stop()
                return
            }
        }
    }()
    return t
}

It's simpler than first one. In additional, since we must pass the context, we can prevent to leak goroutine.
This API is helpful to execute simple task which you want do repeatedly.

@gopherbot gopherbot added this to the Proposal milestone Jul 17, 2024
@seankhliao
Copy link
Member

package time cannot import context, that would be a dependency cycle.

Do you have evidence of widespread use of this pattern in the ecosystem?

@ianlancetaylor
Copy link
Member

This seems easy to write outside of the standard library. In fact, it looks like you already wrote it. https://go.dev/doc/faq#x_in_std

@rittneje
Copy link

time.AfterFunc returns a time.Timer so that you can stop it. If we did the same with time.TickerFunc, then I don't think there would be any need for the time package to reference the context package, avoiding the aforementioned import cycle.

t := time.TickerFunc(1*time.Second, func() {
    fmt.Println("tick")
})
context.AfterFunc(ctx, t.Stop)

@earthboundkid
Copy link
Contributor

A related function that I have wanted which suffers from the time/context circularity is time.SleepContext, which would cancel a sleep if the context is closed. I think for now the best solution is to just put these in a personal utilities package, but I do wonder if something could be done in a Go 2.0 that combined context and time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

7 participants