dae/component/outbound/dialer/alive_dialer_set.go

155 lines
4.2 KiB
Go
Raw Normal View History

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
}
}
}