Stop for loop by passing empty struct down channel Go

pocockn :

I am attempting to create a poller in Go that spins up and every 24 hours executes a function.

I want to also be able to stop the polling, I'm attempting to do this by having a done channel and passing down an empty struct to stop the for loop.

In my tests, the for just loops infinitely and I can't seem to stop it, am I using the done channel incorrectly? The ticker case works as expected.

Poller struct {
    HandlerFunc HandlerFunc
    interval    *time.Ticker
    done        chan struct{}
}

func (p *Poller) Start() error {
    for {
        select {
        case <-p.interval.C:
            err := p.HandlerFunc()
            if err != nil {
                return err
            }
        case <-p.done:
            return nil
        }
    }
}

func (p *Poller) Stop() {
    p.done <- struct{}{}
}

Here is the test that's exeuting the code and causing the infinite loop.

poller := poller.NewPoller(
    testHandlerFunc,
    time.NewTicker(1*time.Millisecond),
)

err := poller.Start()
assert.Error(t, err)
poller.Stop()
Grigoriy Mikhalkin :

Seems like problem is in your use case, you calling poller.Start() in blocking maner, so poller.Stop() is never called. It's common, in go projects to call goroutine inside of Start/Run methods, so, in poller.Start(), i would do something like that:

func (p *Poller) Start() error {
    go func() {
        for {
            select {
            case <-p.interval.C:
                err := p.HandlerFunc()
                if err != nil {
                    return err
                }
            case <-p.done:
                return nil
            }
        }
    }
}

Also, there's no need to send empty struct to done channel. Closing channel like close(p.done) is more idiomatic for go.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=398379&siteId=1