Timeout after 10 seconds

Hello, i’m making an program in which we will ask user do you want to continue ? if he enters yes then it continues and if he didn’t entered anything till 10 seconds that program will exit. We’re reading input fro Scanf, is there any option to do this?

yo,
you need to use go-func statement.
It will start a new goroutime for your function. like:

        var answer string = ""
        go func() {
                time.Sleep(10 * time.Second)
                if answer == "" {
                        fmt.Println("timeout")
                        os.Exit(0)
                }
        }()

        fmt.Scanln(&answer)
        fmt.Println("answer is " + answer)

The above code creates a race condition on answer as one routine is writing to answer at fmt.Scanln(..) white the other routine is reading answer at if answer == "" { .. }.

Perhaps something like this:

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	result := make(chan string)

	go func() {
		var answer string

		fmt.Scanln(&answer)

		result <- answer
	}()

	select {
	case r := <-result:
		fmt.Println("answer is:", r)
	case <-ctx.Done():
		fmt.Println("timeout")
	}
}

Isn’t that what <-time.After(duration) is for?

package main

import (
	"fmt"
	"time"
)

func main() {
	result := make(chan string)

	go func() {
		var answer string
		fmt.Scanln(&answer)
		result <- answer
	}()

	select {
	case <-time.After(10 * time.Second):
		fmt.Println("timeout")
	case r := <-result:
		fmt.Println("answer is:", r)
	}
}
2 Likes

I don’t see that go run -race returns some issues, but if so, we can probably use Mutex here, so:

package main

import (
	"fmt"
	"os"
	"sync"
	"time"
)

func main() {
	m := &sync.Mutex{}
	var answer string = ""
	fmt.Print("do you want to continue?: ")
	go func() {
		time.Sleep(10 * time.Second)
		m.Lock()
		isEmpty := answer == ""
		m.Unlock()
		if isEmpty {
			fmt.Println("timeout")
			os.Exit(0)
		}
	}()

	fmt.Scanln(&answer)
	fmt.Println("answer is " + answer)
}

The Mutex must wrap the the fmt.Scanln(&answer) too - but that’s not a good idea as the fmt.Scanln will block much longer and therefore block the mutext too.


Besides that, there is no way to cancel the the fmt.Scanln call as it reads from os.Stdin. So even after you get the timeout (e.g. using time.Sleep in another go routine) there is no function to stop fmt.Scanln.

To solve that, don’t use Scanln on os.Stdin directly, but read from os.Stdin in a more unblocking way:

package main

import (
	"fmt"
	"time"
	"bufio"
	"os"
)

func main() {
	stdin := make(chan string)

	go func() {
		reader := bufio.NewReader(os.Stdin)
		// read Stdin line by line
		for {
			line, _ := reader.ReadString('\n')
			stdin <- line
		}
	}()

	select {
	case line := <-stdin:
		fmt.Println("Your input:", line)
		// use fmt.Scanf(line, ...) now

	case <-time.After(10 * time.Second):
		fmt.Println("Timeout.")
	}
}
2 Likes