2023-01-23 18:54:21 +07:00
|
|
|
/*
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2023-01-28 01:35:18 +07:00
|
|
|
* Copyright (c) since 2023, mzz2017 <mzz@tuta.io>
|
2023-01-23 18:54:21 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
package dialer
|
|
|
|
|
|
|
|
import (
|
2023-01-23 19:01:24 +07:00
|
|
|
"github.com/v2rayA/dae/common/consts"
|
2023-01-23 18:54:21 +07:00
|
|
|
"github.com/mzz2017/softwind/pkg/fastrand"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type minLatency struct {
|
|
|
|
latency time.Duration
|
|
|
|
dialer *Dialer
|
|
|
|
}
|
|
|
|
|
|
|
|
// AliveDialerSet assumes mapping between index and dialer MUST remain unchanged.
|
|
|
|
//
|
|
|
|
// It is thread-safe.
|
|
|
|
type AliveDialerSet struct {
|
|
|
|
log *logrus.Logger
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
dialerToIndex map[*Dialer]int // *Dialer -> index of inorderedAliveDialerSet
|
|
|
|
dialerToLatency map[*Dialer]time.Duration
|
|
|
|
inorderedAliveDialerSet []*Dialer
|
|
|
|
|
|
|
|
selectionPolicy consts.DialerSelectionPolicy
|
|
|
|
minLatency minLatency
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAliveDialerSet(
|
|
|
|
log *logrus.Logger,
|
|
|
|
selectionPolicy consts.DialerSelectionPolicy,
|
|
|
|
dialers []*Dialer,
|
|
|
|
setAlive bool,
|
|
|
|
) *AliveDialerSet {
|
|
|
|
a := &AliveDialerSet{
|
|
|
|
log: log,
|
|
|
|
dialerToIndex: make(map[*Dialer]int),
|
|
|
|
dialerToLatency: make(map[*Dialer]time.Duration),
|
|
|
|
inorderedAliveDialerSet: make([]*Dialer, 0, len(dialers)),
|
|
|
|
selectionPolicy: selectionPolicy,
|
|
|
|
minLatency: minLatency{
|
|
|
|
// Initiate the latency with a very big value.
|
|
|
|
latency: time.Hour,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if setAlive && len(dialers) > 0 {
|
|
|
|
// Use first dialer if no dialer has alive state.
|
|
|
|
a.minLatency.dialer = dialers[0]
|
|
|
|
}
|
|
|
|
for _, d := range dialers {
|
|
|
|
a.dialerToIndex[d] = -1
|
|
|
|
}
|
|
|
|
for _, d := range dialers {
|
|
|
|
a.SetAlive(d, setAlive)
|
|
|
|
}
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AliveDialerSet) GetRand() *Dialer {
|
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
if len(a.inorderedAliveDialerSet) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
ind := fastrand.Intn(len(a.inorderedAliveDialerSet))
|
|
|
|
return a.inorderedAliveDialerSet[ind]
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetMinLatency acquires correct selectionPolicy.
|
|
|
|
func (a *AliveDialerSet) GetMinLatency() *Dialer {
|
|
|
|
return a.minLatency.dialer
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetAlive should be invoked when dialer every time latency and alive state changes.
|
|
|
|
func (a *AliveDialerSet) SetAlive(dialer *Dialer, alive bool) {
|
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
var (
|
|
|
|
latency time.Duration
|
|
|
|
hasLatency bool
|
|
|
|
)
|
|
|
|
|
|
|
|
switch a.selectionPolicy {
|
|
|
|
case consts.DialerSelectionPolicy_MinLastLatency:
|
|
|
|
latency, hasLatency = dialer.Latencies10.LastLatency()
|
|
|
|
case consts.DialerSelectionPolicy_MinAverage10Latencies:
|
|
|
|
latency, hasLatency = dialer.Latencies10.AvgLatency()
|
|
|
|
}
|
|
|
|
|
|
|
|
if alive {
|
|
|
|
index := a.dialerToIndex[dialer]
|
|
|
|
if index >= 0 {
|
|
|
|
// This dialer is already alive.
|
|
|
|
} else {
|
|
|
|
// Not alive -> alive.
|
|
|
|
a.dialerToIndex[dialer] = len(a.inorderedAliveDialerSet)
|
|
|
|
a.inorderedAliveDialerSet = append(a.inorderedAliveDialerSet, dialer)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
index := a.dialerToIndex[dialer]
|
|
|
|
if index >= 0 {
|
|
|
|
// Alive -> not alive.
|
|
|
|
// Remove the dialer from inorderedAliveDialerSet.
|
|
|
|
if index >= len(a.inorderedAliveDialerSet) {
|
|
|
|
a.log.Panicf("index:%v >= len(a.inorderedAliveDialerSet):%v", index, len(a.inorderedAliveDialerSet))
|
|
|
|
}
|
|
|
|
a.dialerToIndex[dialer] = -1
|
|
|
|
if index < len(a.inorderedAliveDialerSet)-1 {
|
|
|
|
// Swap this element with the last element.
|
|
|
|
dialerToSwap := a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1]
|
|
|
|
if dialer == dialerToSwap {
|
|
|
|
a.log.Panicf("dialer[%p] == dialerToSwap[%p]", dialer, dialerToSwap)
|
|
|
|
}
|
|
|
|
|
|
|
|
a.dialerToIndex[dialerToSwap] = index
|
|
|
|
a.inorderedAliveDialerSet[index], a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1] =
|
|
|
|
a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1], a.inorderedAliveDialerSet[index]
|
|
|
|
}
|
|
|
|
// Pop the last element.
|
|
|
|
a.inorderedAliveDialerSet = a.inorderedAliveDialerSet[:len(a.inorderedAliveDialerSet)-1]
|
|
|
|
} else {
|
|
|
|
// This dialer is already not alive.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasLatency {
|
|
|
|
a.dialerToLatency[dialer] = latency
|
|
|
|
if latency < a.minLatency.latency {
|
|
|
|
a.minLatency.latency = latency
|
|
|
|
a.minLatency.dialer = dialer
|
|
|
|
} else if a.minLatency.dialer == dialer {
|
|
|
|
a.minLatency.latency = time.Hour
|
|
|
|
a.minLatency.dialer = nil
|
|
|
|
a.calcMinLatency()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AliveDialerSet) calcMinLatency() {
|
|
|
|
for _, d := range a.inorderedAliveDialerSet {
|
|
|
|
latency := a.dialerToLatency[d]
|
|
|
|
if latency < a.minLatency.latency {
|
|
|
|
a.minLatency.latency = latency
|
|
|
|
a.minLatency.dialer = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|