mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* [NOD-1223] Delete unused files/packages. * [NOD-1223] Move signal and limits to the os package. * [NOD-1223] Put database and dbaccess into the db package. * [NOD-1223] Fold the logs package into the logger package. * [NOD-1223] Rename domainmessage to appmessage. * [NOD-1223] Rename to/from DomainMessage to AppMessage. * [NOD-1223] Move appmessage to the app packge. * [NOD-1223] Move protocol to the app packge. * [NOD-1223] Move the network package to the infrastructure packge. * [NOD-1223] Rename cmd to executables. * [NOD-1223] Fix go.doc in the logger package.
86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
package panics
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/kaspanet/kaspad/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.Print("Exiting...")
|
|
os.Exit(1)
|
|
fmt.Print("After 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.Debugf("Started goroutine `%s`", goroutineName)
|
|
defer utilLog.Debugf("Ended goroutine `%s`", goroutineName)
|
|
defer HandlePanic(log, goroutineName, stackTrace)
|
|
spawnedFunction()
|
|
}
|