RudeusGreyrat
RudeusGreyrat

Reputation: 117

Handle Go Routine Errors without hanging the program

I have a function that start a Listener.

func startListener(string config) error {
   return http.ListenAndServe(config, nil)
}

I run this function as a go routine in another function:

func Stuff() {
   go startListener("0.0.0.0:8080")
   doOtherStuff()
}

When I don't handle errors, doOtherStuff() executes concurrently with startListener().

But I want to handle errors in my Go Routine. I created a channel that receive the error:

func Stuff() {
   errChan := make(chan error, 1)
   go func() {
       errChan <- startListener("0.0.0.0:8080")
   }()
   
   if err := <-errChan; err != nil {
       fmt.Printf("Error received from goroutine: %v\n", err)
   } else {
       fmt.Println("Goroutine completed without error")
   }
   doOtherStuff()
}

Problem is that when there is no error, doOtherStuff() is never executed. The entire program flow hangs because it is listening on port 8080.

How can I handle error without hanging my program ?

Upvotes: -3

Views: 44

Answers (1)

Bhanuchander Udhayakumar
Bhanuchander Udhayakumar

Reputation: 1646

  • You cannot rely on the error channel or the error returned from the call startListener to confirm that the server started successfully or not since it is a blocking call.

  • Do a Health check or similar call to confirm that the server started successfully.

I belive that you are planning to run some post jobs once the server started successfully. So instead depending on the return error, Register the post jobs in a goroutine which will wait for the successful server start like shown below,

var waitForServerStart = make(chan bool)

func registerPostJob() {
    fmt.Println("Waiting for the server start trigger")
    <-waitForServerStart
    fmt.Println("Received the signal to start post jobs")
    for i := 0; i < 3; i++ {
        resp, _ := http.Get("http://localhost:8080/")
        if resp.StatusCode == http.StatusOK {
            fmt.Println("Seems server started successfully")
            doOtherStuff()
            return
        }
        time.Sleep(1 * time.Second)
    }
}

func startListener(config string) error {
    handler := http.NewServeMux()
    handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    })
    return http.ListenAndServe(config, handler)
}

func doOtherStuff() {
    fmt.Println("doOtherStuff executed")
}

func Stuff() {
    go registerPostJob()
    // other stuffs
    time.Sleep(3 * time.Second)
    // took time say 3 seconds
    close(waitForServerStart)
    if err := startListener("localhost:8080"); err != nil {
        fmt.Printf("Error received from goroutine: %v\n", err)
    }
}

Hope it helps.

Upvotes: 1

Related Questions