2016-04-13 21:36:23 +03:00
package safe
import (
2017-02-02 23:07:44 +03:00
"context"
2016-12-08 15:32:12 +03:00
"fmt"
2016-04-13 21:36:23 +03:00
"runtime/debug"
"sync"
2017-04-30 15:39:49 +03:00
2020-02-26 12:36:05 +03:00
"github.com/cenkalti/backoff/v4"
2020-09-16 16:46:04 +03:00
"github.com/traefik/traefik/v2/pkg/log"
2016-04-13 21:36:23 +03:00
)
2016-08-18 14:03:10 +03:00
type routineCtx func ( ctx context . Context )
2020-05-11 13:06:07 +03:00
// Pool is a pool of go routines.
2016-04-13 21:36:23 +03:00
type Pool struct {
2020-02-03 19:56:04 +03:00
waitGroup sync . WaitGroup
ctx context . Context
cancel context . CancelFunc
2016-08-18 14:03:10 +03:00
}
2020-05-11 13:06:07 +03:00
// NewPool creates a Pool.
2016-08-18 15:20:11 +03:00
func NewPool ( parentCtx context . Context ) * Pool {
2020-02-03 19:56:04 +03:00
ctx , cancel := context . WithCancel ( parentCtx )
2016-08-18 14:03:10 +03:00
return & Pool {
2020-02-03 19:56:04 +03:00
ctx : ctx ,
cancel : cancel ,
2016-08-18 14:03:10 +03:00
}
}
2020-05-11 13:06:07 +03:00
// GoCtx starts a recoverable goroutine with a context.
2016-08-18 14:03:10 +03:00
func ( p * Pool ) GoCtx ( goroutine routineCtx ) {
p . waitGroup . Add ( 1 )
Go ( func ( ) {
2019-08-26 11:54:05 +03:00
defer p . waitGroup . Done ( )
2016-08-18 14:03:10 +03:00
goroutine ( p . ctx )
} )
2016-04-13 21:36:23 +03:00
}
2020-05-11 13:06:07 +03:00
// Stop stops all started routines, waiting for their termination.
2016-04-13 21:36:23 +03:00
func ( p * Pool ) Stop ( ) {
2016-08-18 14:03:10 +03:00
p . cancel ( )
2016-04-13 21:36:23 +03:00
p . waitGroup . Wait ( )
2016-11-15 22:14:11 +03:00
}
2020-05-11 13:06:07 +03:00
// Go starts a recoverable goroutine.
2016-04-13 21:36:23 +03:00
func Go ( goroutine func ( ) ) {
GoWithRecover ( goroutine , defaultRecoverGoroutine )
}
2020-05-11 13:06:07 +03:00
// GoWithRecover starts a recoverable goroutine using given customRecover() function.
2016-04-13 21:36:23 +03:00
func GoWithRecover ( goroutine func ( ) , customRecover func ( err interface { } ) ) {
go func ( ) {
defer func ( ) {
if err := recover ( ) ; err != nil {
customRecover ( err )
}
} ( )
goroutine ( )
} ( )
}
func defaultRecoverGoroutine ( err interface { } ) {
2019-09-13 20:28:04 +03:00
logger := log . WithoutContext ( )
logger . Errorf ( "Error in Go routine: %s" , err )
logger . Errorf ( "Stack: %s" , debug . Stack ( ) )
2016-04-13 21:36:23 +03:00
}
2016-12-08 15:32:12 +03:00
2020-05-11 13:06:07 +03:00
// OperationWithRecover wrap a backoff operation in a Recover.
2016-12-08 15:32:12 +03:00
func OperationWithRecover ( operation backoff . Operation ) backoff . Operation {
return func ( ) ( err error ) {
defer func ( ) {
if res := recover ( ) ; res != nil {
2016-12-08 15:32:52 +03:00
defaultRecoverGoroutine ( res )
2020-05-11 13:06:07 +03:00
err = fmt . Errorf ( "panic in operation: %w" , err )
2016-12-08 15:32:12 +03:00
}
} ( )
2016-12-08 15:32:52 +03:00
return operation ( )
2016-12-08 15:32:12 +03:00
}
}