go

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 ?


Solution

  • 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.