fix(config): use three-state atomic for rebootstrap to prevent data race

Replace boolean rebootstrap flag with a three-state atomic integer to
prevent concurrent SetupTransport calls during rebootstrap. The atomic
state machine ensures only one goroutine can proceed from "started" to
"in progress", eliminating the need for a mutex while maintaining
thread safety.

States: NotStarted -> Started -> InProgress -> NotStarted

Note that the race condition is still acceptable because any additional
transports created during the race are functional. Once the connection
is established, the unused transports are safely handled by the garbage
collector.
This commit is contained in:
Cuong Manh Le
2026-01-07 17:11:38 +07:00
committed by Cuong Manh Le
parent 1f4c47318e
commit 2e8a0f00a0
2 changed files with 53 additions and 3 deletions

View File

@@ -82,6 +82,10 @@ const (
endpointPrefixQUIC = "quic://"
endpointPrefixH3 = "h3://"
endpointPrefixSdns = "sdns://"
rebootstrapNotStarted = 0
rebootstrapStarted = 1
rebootstrapInProgress = 2
)
var (
@@ -264,7 +268,7 @@ type UpstreamConfig struct {
Discoverable *bool `mapstructure:"discoverable" toml:"discoverable"`
g singleflight.Group
rebootstrap atomic.Bool
rebootstrap atomic.Int64
bootstrapIPs []string
bootstrapIPs4 []string
bootstrapIPs6 []string
@@ -497,7 +501,7 @@ func (uc *UpstreamConfig) ReBootstrap() {
return
}
_, _, _ = uc.g.Do("ReBootstrap", func() (any, error) {
if uc.rebootstrap.CompareAndSwap(false, true) {
if uc.rebootstrap.CompareAndSwap(rebootstrapNotStarted, rebootstrapStarted) {
ProxyLogger.Load().Debug().Msgf("re-bootstrapping upstream ip for %v", uc)
}
return true, nil
@@ -542,8 +546,9 @@ func (uc *UpstreamConfig) ensureSetupTransport() {
uc.transportOnce.Do(func() {
uc.SetupTransport()
})
if uc.rebootstrap.CompareAndSwap(true, false) {
if uc.rebootstrap.CompareAndSwap(rebootstrapStarted, rebootstrapInProgress) {
uc.SetupTransport()
uc.rebootstrap.Store(rebootstrapNotStarted)
}
}