mirror of https://github.com/go-gost/gost.git
fix: use atomic int for multi-worker exit code and clean up cancellation
- Replace bare int with atomic.Int32 to fix data race on ret - Replace wg.Add/go/defer wg.Done pattern with wg.Go (Go 1.22+) - Pass context by value instead of pointer - Add ctx.Err() guard to suppress fatal on cancellation errors - Add ProcessState nil check before accessing ExitCode - Move cancel() after wg.Wait() to avoid race where a successful worker's deferred cancel kills siblings and triggers log.Fatalpull/888/head
parent
b628475871
commit
b985e5138c
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
|
|
@ -48,39 +49,43 @@ func init() {
|
||||||
if strings.Contains(args, " -- ") {
|
if strings.Contains(args, " -- ") {
|
||||||
var (
|
var (
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
ret int
|
ret atomic.Int32
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
wargsList := strings.Split(" "+args+" ", " -- ")
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
for wid, wargs := range strings.Split(" "+args+" ", " -- ") {
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
wg.Add(1)
|
|
||||||
go func(wid int, wargs string) {
|
for wid, wargs := range wargsList {
|
||||||
defer wg.Done()
|
wg.Go(func() {
|
||||||
defer cancel()
|
worker(wid, strings.Split(strings.TrimSpace(wargs), " "), ctx, &ret)
|
||||||
worker(wid, strings.Split(wargs, " "), &ctx, &ret)
|
})
|
||||||
}(wid, strings.TrimSpace(wargs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
cancel()
|
||||||
|
|
||||||
os.Exit(ret)
|
os.Exit(int(ret.Load()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func worker(id int, args []string, ctx *context.Context, ret *int) {
|
func worker(id int, args []string, ctx context.Context, ret *atomic.Int32) {
|
||||||
cmd := exec.CommandContext(*ctx, os.Args[0], args...)
|
cmd := exec.CommandContext(ctx, os.Args[0], args...)
|
||||||
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("_GOST_ID=%d", id))
|
cmd.Env = append(os.Environ(), fmt.Sprintf("_GOST_ID=%d", id))
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Fatal(err)
|
// Context cancellation is expected when one worker exits early.
|
||||||
|
// Only log fatal on other errors.
|
||||||
|
if ctx.Err() == nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if cmd.ProcessState.Exited() {
|
if cmd.ProcessState != nil && cmd.ProcessState.Exited() {
|
||||||
*ret = cmd.ProcessState.ExitCode()
|
ret.Store(int32(cmd.ProcessState.ExitCode()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue