Last active
March 29, 2023 20:53
-
-
Save ajitid/b22dcf46b62afb196805954e5a638f8c to your computer and use it in GitHub Desktop.
Wait forever so that your goroutines can finish in Golang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
NOT TO BE USED IN PRODUCTION | |
Lets you wait forever so that your goroutines can finish. | |
Works great when coupled with [watchexec](https://watchexec.github.io/) like `watchexec -rc go run .` while learning Go. | |
src: https://stackoverflow.com/questions/36419054/go-projects-main-goroutine-sleep-forever#comment132984686_36419288 | |
Should be used like: | |
```go | |
func main() { | |
// your code | |
waitForever() | |
} | |
``` | |
Do not do `defer waitForever()` otherwise you would be blocking the panic to occur (and only terminating the program with <ctrl-c> would make it occur, not ideal). | |
Using a blocking channel with `defer` is not a good idea in general. | |
*/ | |
package main | |
import ( | |
"os" | |
"os/signal" | |
"syscall" | |
) | |
func waitForever() { | |
exitSignal := make(chan os.Signal, 1) | |
signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) | |
<-exitSignal | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
NOT TO BE USED IN PRODUCTION | |
Lets you wait forever so that your goroutines can finish. | |
Works great when coupled with [watchexec](https://watchexec.github.io/) like `watchexec -rc go run .` while learning Go. | |
src: https://stackoverflow.com/questions/36419054/go-projects-main-goroutine-sleep-forever#comment132984686_36419288 | |
Should be placed in utils/ folder and be used like: | |
```go | |
import "your-pkg-name/utils" | |
func main() { | |
defer utils.WaitForever() | |
// rest of your code... | |
} | |
``` | |
*/ | |
package utils | |
import ( | |
"fmt" | |
"os" | |
"os/signal" | |
"runtime/debug" | |
"strings" | |
"syscall" | |
) | |
func WaitForever() { | |
// waiting for `exitSignal` would hide panic of current goroutine and would only show it | |
// when we do <ctrl-c>, so we have to check for any panic ourselves | |
if e := recover(); e != nil { | |
fmt.Println("panic:", e) | |
trace := string(debug.Stack()) | |
// `trace` could include panic's own fn call trace as well, which we'd discard | |
skipTrace := true | |
{ | |
tracePerLine := strings.Split(trace, "\n") | |
for i := 0; i < len(tracePerLine); i++ { | |
if strings.HasPrefix(tracePerLine[i], "panic(") && strings.Contains(tracePerLine[i+1], "/go/src/runtime/panic.go") { | |
i += 1 | |
skipTrace = false | |
continue | |
} | |
if skipTrace { | |
continue | |
} | |
fmt.Println(tracePerLine[i]) | |
} | |
} | |
// if it is still true, it would mean that haven't logged anything, so we'll log the whole trace | |
if skipTrace { | |
fmt.Print(trace) | |
} | |
// panic gives this status which is actually incorrect, | |
// but we'll imitate its behavior anyway, see: https://github.com/golang/go/issues/24284 | |
os.Exit(2) | |
} | |
exitSignal := make(chan os.Signal, 1) | |
signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) | |
<-exitSignal | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment