mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-16 09:36:07 +00:00
f2f11bc042
Start writes p.version and p.apiPort (via refreshVersion/refreshAPIPort) after flipping the process to running, while GetXrayVersion and GetAPIPort read them lock-free from the status and traffic poll goroutines. The struct mutex deliberately excluded these fields, so a restart racing a poll was a real data race — a torn read of the version string header can crash. Extend the mutex to cover version and apiPort, doing the blocking version probe before taking the lock.
103 lines
1.9 KiB
Go
103 lines
1.9 KiB
Go
package xray
|
|
|
|
import (
|
|
"errors"
|
|
"os/exec"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestProcessLifecycleFieldsRaceSafe drives the lifecycle fields (cmd, done,
|
|
// exitErr) the way Start/startCommand and the waitForCommand goroutine do, while
|
|
// the status getters read them concurrently. Run with -race: any unsynchronized
|
|
// access to those fields is reported as a data race.
|
|
func TestProcessLifecycleFieldsRaceSafe(t *testing.T) {
|
|
p := &process{logWriter: NewLogWriter()}
|
|
|
|
var wg sync.WaitGroup
|
|
stop := make(chan struct{})
|
|
|
|
// Writer: churn cmd/done/exitErr like Start + waitForCommand.
|
|
wg.Go(func() {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
p.mu.Lock()
|
|
p.cmd = &exec.Cmd{}
|
|
p.done = make(chan struct{})
|
|
p.mu.Unlock()
|
|
p.setExitErr(errors.New("boom"))
|
|
}
|
|
})
|
|
|
|
// Readers: the concurrent status getters.
|
|
for range 4 {
|
|
wg.Go(func() {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
_ = p.IsRunning()
|
|
_ = p.GetErr()
|
|
_ = p.GetResult()
|
|
}
|
|
})
|
|
}
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
close(stop)
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestProcessVersionAPIPortRaceSafe writes version/apiPort the way Start's
|
|
// refresh helpers do while GetXrayVersion/GetAPIPort read them concurrently.
|
|
// Run with -race: an unsynchronized access to either field is reported.
|
|
func TestProcessVersionAPIPortRaceSafe(t *testing.T) {
|
|
inner := &process{
|
|
logWriter: NewLogWriter(),
|
|
config: &Config{InboundConfigs: []InboundConfig{{Tag: "api", Port: 12345}}},
|
|
}
|
|
p := &Process{inner}
|
|
|
|
var wg sync.WaitGroup
|
|
stop := make(chan struct{})
|
|
|
|
wg.Go(func() {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
p.refreshAPIPort()
|
|
inner.mu.Lock()
|
|
inner.version = "v1.2.3"
|
|
inner.mu.Unlock()
|
|
}
|
|
})
|
|
|
|
for range 4 {
|
|
wg.Go(func() {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
_ = p.GetAPIPort()
|
|
_ = p.GetXrayVersion()
|
|
}
|
|
})
|
|
}
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
close(stop)
|
|
wg.Wait()
|
|
}
|