mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-11-29 00:23:14 +00:00
Just some name changes, put in a stand in emission amount, and started copying the algo from Karlsen. Not release worthy yet. Therefore Dev branch exists now. Also, for now this is for research purposes only. I got no clue what to build on top of Kaspa yet. Help would be appreciated for ideas and implementations.
85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
package panics
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/zoomy-network/zoomyd/infrastructure/logger"
|
|
"os"
|
|
"runtime/debug"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const exitHandlerTimeout = 5 * time.Second
|
|
|
|
// HandlePanic recovers panics and then initiates a clean shutdown.
|
|
func HandlePanic(log *logger.Logger, goroutineName string, goroutineStackTrace []byte) {
|
|
err := recover()
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
reason := fmt.Sprintf("Fatal error in goroutine `%s`: %+v", goroutineName, err)
|
|
exit(log, reason, debug.Stack(), goroutineStackTrace)
|
|
}
|
|
|
|
var goroutineLastID uint64
|
|
|
|
// GoroutineWrapperFunc returns a goroutine wrapper function that handles panics and writes them to the log.
|
|
func GoroutineWrapperFunc(log *logger.Logger) func(name string, spawnedFunction func()) {
|
|
return func(name string, f func()) {
|
|
stackTrace := debug.Stack()
|
|
go func() {
|
|
handleSpawnedFunction(log, stackTrace, name, f)
|
|
}()
|
|
}
|
|
}
|
|
|
|
// AfterFuncWrapperFunc returns a time.AfterFunc wrapper function that handles panics.
|
|
func AfterFuncWrapperFunc(log *logger.Logger) func(name string, d time.Duration, f func()) *time.Timer {
|
|
return func(name string, d time.Duration, f func()) *time.Timer {
|
|
stackTrace := debug.Stack()
|
|
return time.AfterFunc(d, func() {
|
|
handleSpawnedFunction(log, stackTrace, name, f)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Exit prints the given reason to log and initiates a clean shutdown.
|
|
func Exit(log *logger.Logger, reason string) {
|
|
exit(log, reason, nil, nil)
|
|
}
|
|
|
|
// Exit prints the given reason, prints either of the given stack traces (if not nil),
|
|
// waits for them to finish writing, and exits.
|
|
func exit(log *logger.Logger, reason string, currentThreadStackTrace []byte, goroutineStackTrace []byte) {
|
|
exitHandlerDone := make(chan struct{})
|
|
go func() {
|
|
log.Criticalf("Exiting: %s", reason)
|
|
if goroutineStackTrace != nil {
|
|
log.Criticalf("Goroutine stack trace: %s", goroutineStackTrace)
|
|
}
|
|
if currentThreadStackTrace != nil {
|
|
log.Criticalf("Stack trace: %s", currentThreadStackTrace)
|
|
}
|
|
log.Backend().Close()
|
|
close(exitHandlerDone)
|
|
}()
|
|
|
|
select {
|
|
case <-time.After(exitHandlerTimeout):
|
|
fmt.Fprintln(os.Stderr, "Couldn't exit gracefully.")
|
|
case <-exitHandlerDone:
|
|
}
|
|
fmt.Println("Exiting...")
|
|
os.Exit(1)
|
|
}
|
|
|
|
func handleSpawnedFunction(log *logger.Logger, stackTrace []byte, spawnedFunctionName string, spawnedFunction func()) {
|
|
goroutineID := atomic.AddUint64(&goroutineLastID, 1)
|
|
goroutineName := fmt.Sprintf("%s %d", spawnedFunctionName, goroutineID)
|
|
utilLog.Tracef("Started goroutine `%s`", goroutineName)
|
|
defer utilLog.Tracef("Ended goroutine `%s`", goroutineName)
|
|
defer HandlePanic(log, goroutineName, stackTrace)
|
|
spawnedFunction()
|
|
}
|