fix deadlocks

This commit is contained in:
Siyuan 2025-10-07 18:59:19 +00:00
parent 45b55fe89f
commit 656df6c910
5 changed files with 129 additions and 10 deletions

View File

@ -3,6 +3,7 @@ package timesync
import ( import (
"context" "context"
"math/rand/v2" "math/rand/v2"
"net"
"strconv" "strconv"
"time" "time"
@ -37,7 +38,47 @@ var DefaultNTPServerHostnames = []string{
"pool.ntp.org", "pool.ntp.org",
} }
func (t *TimeSync) filterNTPServers(ntpServers []string) ([]string, error) {
if len(ntpServers) == 0 {
return nil, nil
}
hasIPv4, err := t.preCheckIPv4()
if err != nil {
t.l.Error().Err(err).Msg("failed to check IPv4")
return nil, err
}
hasIPv6, err := t.preCheckIPv6()
if err != nil {
t.l.Error().Err(err).Msg("failed to check IPv6")
return nil, err
}
filteredServers := []string{}
for _, server := range ntpServers {
ip := net.ParseIP(server)
t.l.Trace().Str("server", server).Interface("ip", ip).Msg("checking NTP server")
if ip == nil {
continue
}
if hasIPv4 && ip.To4() != nil {
filteredServers = append(filteredServers, server)
}
if hasIPv6 && ip.To16() != nil {
filteredServers = append(filteredServers, server)
}
}
return filteredServers, nil
}
func (t *TimeSync) queryNetworkTime(ntpServers []string) (now *time.Time, offset *time.Duration) { func (t *TimeSync) queryNetworkTime(ntpServers []string) (now *time.Time, offset *time.Duration) {
ntpServers, err := t.filterNTPServers(ntpServers)
if err != nil {
t.l.Error().Err(err).Msg("failed to filter NTP servers")
return nil, nil
}
chunkSize := int(t.networkConfig.TimeSyncParallel.ValueOr(4)) chunkSize := int(t.networkConfig.TimeSyncParallel.ValueOr(4))
t.l.Info().Strs("servers", ntpServers).Int("chunkSize", chunkSize).Msg("querying NTP servers") t.l.Info().Strs("servers", ntpServers).Int("chunkSize", chunkSize).Msg("querying NTP servers")

View File

@ -24,6 +24,8 @@ var (
timeSyncRetryInterval = 0 * time.Second timeSyncRetryInterval = 0 * time.Second
) )
type PreCheckFunc func() (bool, error)
type TimeSync struct { type TimeSync struct {
syncLock *sync.Mutex syncLock *sync.Mutex
l *zerolog.Logger l *zerolog.Logger
@ -37,11 +39,15 @@ type TimeSync struct {
syncSuccess bool syncSuccess bool
preCheckFunc func() (bool, error) preCheckFunc PreCheckFunc
preCheckIPv4 PreCheckFunc
preCheckIPv6 PreCheckFunc
} }
type TimeSyncOptions struct { type TimeSyncOptions struct {
PreCheckFunc func() (bool, error) PreCheckFunc PreCheckFunc
PreCheckIPv4 PreCheckFunc
PreCheckIPv6 PreCheckFunc
Logger *zerolog.Logger Logger *zerolog.Logger
NetworkConfig *types.NetworkConfig NetworkConfig *types.NetworkConfig
} }
@ -69,6 +75,8 @@ func NewTimeSync(opts *TimeSyncOptions) *TimeSync {
rtcDevicePath: rtcDevice, rtcDevicePath: rtcDevice,
rtcLock: &sync.Mutex{}, rtcLock: &sync.Mutex{},
preCheckFunc: opts.PreCheckFunc, preCheckFunc: opts.PreCheckFunc,
preCheckIPv4: opts.PreCheckIPv4,
preCheckIPv6: opts.PreCheckIPv6,
networkConfig: opts.NetworkConfig, networkConfig: opts.NetworkConfig,
} }
@ -112,7 +120,13 @@ func (t *TimeSync) getSyncMode() SyncMode {
} }
} }
t.l.Debug().Strs("Ordering", syncMode.Ordering).Bool("Ntp", syncMode.Ntp).Bool("Http", syncMode.Http).Bool("NtpUseFallback", syncMode.NtpUseFallback).Bool("HttpUseFallback", syncMode.HttpUseFallback).Msg("sync mode") t.l.Debug().
Strs("Ordering", syncMode.Ordering).
Bool("Ntp", syncMode.Ntp).
Bool("Http", syncMode.Http).
Bool("NtpUseFallback", syncMode.NtpUseFallback).
Bool("HttpUseFallback", syncMode.HttpUseFallback).
Msg("sync mode")
return syncMode return syncMode
} }

View File

@ -152,30 +152,63 @@ func (im *InterfaceManager) link() (*link.Link, error) {
// IsUp returns true if the interface is up // IsUp returns true if the interface is up
func (im *InterfaceManager) IsUp() bool { func (im *InterfaceManager) IsUp() bool {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.Up return im.state.Up
} }
func (im *InterfaceManager) IsOnline() bool { func (im *InterfaceManager) IsOnline() bool {
return im.IsUp() im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.Online
}
func (im *InterfaceManager) IPv4Ready() bool {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.IPv4Ready
}
func (im *InterfaceManager) IPv6Ready() bool {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.IPv6Ready
} }
func (im *InterfaceManager) GetIPv4Addresses() []string { func (im *InterfaceManager) GetIPv4Addresses() []string {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.IPv4Addresses return im.state.IPv4Addresses
} }
func (im *InterfaceManager) GetIPv4Address() string { func (im *InterfaceManager) GetIPv4Address() string {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.IPv4Address return im.state.IPv4Address
} }
func (im *InterfaceManager) GetIPv6Address() string { func (im *InterfaceManager) GetIPv6Address() string {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
return im.state.IPv6Address return im.state.IPv6Address
} }
func (im *InterfaceManager) GetIPv6Addresses() []string { func (im *InterfaceManager) GetIPv6Addresses() []string {
im.stateMu.RLock()
defer im.stateMu.RUnlock()
addresses := []string{} addresses := []string{}
for _, addr := range im.state.IPv6Addresses { for _, addr := range im.state.IPv6Addresses {
addresses = append(addresses, addr.Address.String()) addresses = append(addresses, addr.Address.String())
} }
return []string{} return []string{}
} }
@ -569,11 +602,11 @@ func (im *InterfaceManager) updateInterfaceState() error {
hasAddrs = true hasAddrs = true
} }
im.stateMu.Lock()
defer im.stateMu.Unlock()
// Check if state changed // Check if state changed
stateChanged := false stateChanged := false
// We should release the lock before calling the callbacks
// to avoid deadlocks
im.stateMu.Lock()
if im.state.Up != isUp { if im.state.Up != isUp {
im.state.Up = isUp im.state.Up = isUp
stateChanged = true stateChanged = true
@ -594,6 +627,7 @@ func (im *InterfaceManager) updateInterfaceState() error {
} }
im.state.LastUpdated = time.Now() im.state.LastUpdated = time.Now()
im.stateMu.Unlock()
// Notify callback if state changed // Notify callback if state changed
if stateChanged && im.onStateChange != nil { if stateChanged && im.onStateChange != nil {
@ -661,9 +695,8 @@ func (im *InterfaceManager) updateIPAddresses(nl *link.Link) error {
// updateStateFromDHCPLease updates the state from a DHCP lease // updateStateFromDHCPLease updates the state from a DHCP lease
func (im *InterfaceManager) updateStateFromDHCPLease(lease *types.DHCPLease) { func (im *InterfaceManager) updateStateFromDHCPLease(lease *types.DHCPLease) {
im.stateMu.Lock() im.stateMu.Lock()
defer im.stateMu.Unlock()
im.state.DHCPLease4 = lease im.state.DHCPLease4 = lease
im.stateMu.Unlock()
// Update resolv.conf with DNS information // Update resolv.conf with DNS information
if im.resolvConf != nil { if im.resolvConf != nil {

View File

@ -12,7 +12,12 @@ func (nm *NetworkManager) IsOnline() bool {
} }
func (nm *NetworkManager) IsUp() bool { func (nm *NetworkManager) IsUp() bool {
return nm.IsOnline() for _, iface := range nm.interfaces {
if iface.IsUp() {
return true
}
}
return false
} }
func (nm *NetworkManager) GetHostname() string { func (nm *NetworkManager) GetHostname() string {
@ -74,6 +79,20 @@ func (nm *NetworkManager) GetMACAddress() string {
return "" return ""
} }
func (nm *NetworkManager) IPv4Ready() bool {
for _, iface := range nm.interfaces {
return iface.IPv4Ready()
}
return false
}
func (nm *NetworkManager) IPv6Ready() bool {
for _, iface := range nm.interfaces {
return iface.IPv6Ready()
}
return false
}
func (nm *NetworkManager) IPv4String() string { func (nm *NetworkManager) IPv4String() string {
return nm.GetIPv4Address() return nm.GetIPv4Address()
} }

View File

@ -43,6 +43,18 @@ func initTimeSync() {
timeSync = timesync.NewTimeSync(&timesync.TimeSyncOptions{ timeSync = timesync.NewTimeSync(&timesync.TimeSyncOptions{
Logger: timesyncLogger, Logger: timesyncLogger,
NetworkConfig: config.NetworkConfig, NetworkConfig: config.NetworkConfig,
PreCheckIPv4: func() (bool, error) {
if !networkManager.IPv4Ready() {
return false, nil
}
return true, nil
},
PreCheckIPv6: func() (bool, error) {
if !networkManager.IPv6Ready() {
return false, nil
}
return true, nil
},
PreCheckFunc: func() (bool, error) { PreCheckFunc: func() (bool, error) {
if !networkManager.IsOnline() { if !networkManager.IsOnline() {
return false, nil return false, nil