Manse
Manse

Reputation: 38147

Conditional Timeout

I have some code, there are 3 timers,

If any of the timers are reached the app should cleanly exit. I have it working below

msgs := make(chan string)

go func() {
    time.Sleep(time.Second)
    msgs <- "test"
}()
// graceful max execution time
gracefulMaxTimeout := time.Second * time.Duration(10)
gracefulMaxTimer := time.NewTimer(gracefulMaxTimeout)

// idleTimeout
idleTimeout := time.Second * time.Duration(5)
idleTimer := time.NewTimer(idleTimeout)

// waitTimeout
waitTimeout := time.Second * time.Duration(2)
waitTimer := time.NewTimer(waitTimeout)
for {
    select {
    case <-gracefulMaxTimer.C:
        fmt.Println("GracefulMaxExecutionTimeout Reached")

        // graceful exit
        os.Exit(0)
    case <-idleTimer.C:
        fmt.Println("IdleTimeout Reached")

        // graceful exit
        os.Exit(0)
    case <-waitTimer.C:
        fmt.Println("WaitTimeout Reached")
        // graceful exit
        os.Exit(0)
    case msg := <-msgs:
        // stop wait timer
        waitTimer.Stop()
        fmt.Println(msg)

        // Reset idle timer
        if !idleTimer.Stop() {
            <-idleTimer.C
        }
        fmt.Println("IdleIimeout Reset")
        idleTimer.Reset(idleTimeout)
    }
}

Go Playground

I want to make the WaitTimeout optional but not sure how to approach it. If i surround the construction of the waitTimer with an if statement then it wont work as the waitTimer isnt defined for the select statement ... How can i make the WaitTimeout conditional ?

I can just .Stop() the timer after its created but that seems a little dirty ...

Upvotes: 2

Views: 597

Answers (1)

icza
icza

Reputation: 417672

You may declare the wait timer and its channel outside the if statement, and only initialize them if wait timer is needed. If not, the channel may remain its zero value–which is nil–because receiving from a nil channel blocks forever, so this case will never be ready (for details, see How does a non initialized channel behave?).

useWaitTimer := true

var (
    waitTimer  *time.Timer
    waitTimerC <-chan time.Time
)
if useWaitTimer {
    waitTimeout := time.Millisecond * time.Duration(500)
    waitTimer = time.NewTimer(waitTimeout)
    waitTimerC = waitTimer.C
}

// ...

for {
    select {
    // ...

    case <-waitTimerC:
        fmt.Println("WaitTimeout Reached")
        // graceful exit
        os.Exit(0)

    // ...
    }
}

Then of course you can only reset the wait timer if it exists, this must also be checked (and don't forget to drain the channel if it returns false):

// stop wait timer if exists
if waitTimer != nil && !waitTimer.Stop() {
    <-waitTimerC
}

Try it on the Go Playground.

Upvotes: 1

Related Questions