proxy-pool/internal/platform/lifecycle/group.go

73 lines
1.4 KiB
Go

package lifecycle
import (
"context"
"errors"
"reflect"
)
var (
ErrInvalidGroup = errors.New("invalid lifecycle group")
ErrRunnerStopped = errors.New("lifecycle runner stopped")
)
type Runner interface {
Run(context.Context) error
}
type Group struct {
runners []Runner
}
func NewGroup(runners ...Runner) (*Group, error) {
if len(runners) == 0 {
return nil, ErrInvalidGroup
}
owned := make([]Runner, len(runners))
for index, runner := range runners {
if isNilRunner(runner) {
return nil, ErrInvalidGroup
}
owned[index] = runner
}
return &Group{runners: owned}, nil
}
func (group *Group) Run(ctx context.Context) error {
if group == nil || ctx == nil || len(group.runners) == 0 {
return ErrInvalidGroup
}
groupCtx, cancel := context.WithCancel(ctx)
defer cancel()
done := make(chan error, len(group.runners))
for _, runner := range group.runners {
go func() { done <- runner.Run(groupCtx) }()
}
first := <-done
cancel()
for range len(group.runners) - 1 {
<-done
}
if ctx.Err() != nil {
return ctx.Err()
}
if first == nil {
return ErrRunnerStopped
}
return first
}
func isNilRunner(runner Runner) bool {
if runner == nil {
return true
}
reflected := reflect.ValueOf(runner)
switch reflected.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return reflected.IsNil()
default:
return false
}
}