package concurrent

import (
	
	
	
	
	
	
	
)

// HandlePanic logs goroutine panic by default
var HandlePanic = func( interface{},  string) {
	ErrorLogger.Println(fmt.Sprintf("%s panic: %v", , ))
	ErrorLogger.Println(string(debug.Stack()))
}

// UnboundedExecutor is a executor without limits on counts of alive goroutines
// it tracks the goroutine started by it, and can cancel them when shutdown
type UnboundedExecutor struct {
	ctx                   context.Context
	cancel                context.CancelFunc
	activeGoroutinesMutex *sync.Mutex
	activeGoroutines      map[string]int
	HandlePanic           func(recovered interface{}, funcName string)
}

// GlobalUnboundedExecutor has the life cycle of the program itself
// any goroutine want to be shutdown before main exit can be started from this executor
// GlobalUnboundedExecutor expects the main function to call stop
// it does not magically knows the main function exits
var GlobalUnboundedExecutor = NewUnboundedExecutor()

// NewUnboundedExecutor creates a new UnboundedExecutor,
// UnboundedExecutor can not be created by &UnboundedExecutor{}
// HandlePanic can be set with a callback to override global HandlePanic
func () *UnboundedExecutor {
	,  := context.WithCancel(context.TODO())
	return &UnboundedExecutor{
		ctx:                   ,
		cancel:                ,
		activeGoroutinesMutex: &sync.Mutex{},
		activeGoroutines:      map[string]int{},
	}
}

// Go starts a new goroutine and tracks its lifecycle.
// Panic will be recovered and logged automatically, except for StopSignal
func ( *UnboundedExecutor) ( func( context.Context)) {
	 := reflect.ValueOf().Pointer()
	 := runtime.FuncForPC()
	 := .Name()
	,  := .FileLine()
	.activeGoroutinesMutex.Lock()
	defer .activeGoroutinesMutex.Unlock()
	 := fmt.Sprintf("%s:%d", , )
	.activeGoroutines[] += 1
	go func() {
		defer func() {
			 := recover()
			// if you want to quit a goroutine without trigger HandlePanic
			// use runtime.Goexit() to quit
			if  != nil {
				if .HandlePanic == nil {
					HandlePanic(, )
				} else {
					.HandlePanic(, )
				}
			}
			.activeGoroutinesMutex.Lock()
			.activeGoroutines[] -= 1
			.activeGoroutinesMutex.Unlock()
		}()
		(.ctx)
	}()
}

// Stop cancel all goroutines started by this executor without wait
func ( *UnboundedExecutor) () {
	.cancel()
}

// StopAndWaitForever cancel all goroutines started by this executor and
// wait until all goroutines exited
func ( *UnboundedExecutor) () {
	.StopAndWait(context.Background())
}

// StopAndWait cancel all goroutines started by this executor and wait.
// Wait can be cancelled by the context passed in.
func ( *UnboundedExecutor) ( context.Context) {
	.cancel()
	for {
		 := time.NewTimer(time.Millisecond * 100)
		select {
		case <-.C:
			if .checkNoActiveGoroutines() {
				return
			}
		case <-.Done():
			return
		}
	}
}

func ( *UnboundedExecutor) () bool {
	.activeGoroutinesMutex.Lock()
	defer .activeGoroutinesMutex.Unlock()
	for ,  := range .activeGoroutines {
		if  > 0 {
			InfoLogger.Println("UnboundedExecutor is still waiting goroutines to quit",
				"startFrom", ,
				"count", )
			return false
		}
	}
	return true
}