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 (
"context"
"math/rand/v2"
"net"
"strconv"
"time"
@ -37,7 +38,47 @@ var DefaultNTPServerHostnames = []string{
"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) {
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))
t.l.Info().Strs("servers", ntpServers).Int("chunkSize", chunkSize).Msg("querying NTP servers")

View File

@ -24,6 +24,8 @@ var (
timeSyncRetryInterval = 0 * time.Second
)
type PreCheckFunc func() (bool, error)
type TimeSync struct {
syncLock *sync.Mutex
l *zerolog.Logger
@ -37,11 +39,15 @@ type TimeSync struct {
syncSuccess bool
preCheckFunc func() (bool, error)
preCheckFunc PreCheckFunc
preCheckIPv4 PreCheckFunc
preCheckIPv6 PreCheckFunc
}
type TimeSyncOptions struct {
PreCheckFunc func() (bool, error)
PreCheckFunc PreCheckFunc
PreCheckIPv4 PreCheckFunc
PreCheckIPv6 PreCheckFunc
Logger *zerolog.Logger
NetworkConfig *types.NetworkConfig
}
@ -69,6 +75,8 @@ func NewTimeSync(opts *TimeSyncOptions) *TimeSync {
rtcDevicePath: rtcDevice,
rtcLock: &sync.Mutex{},
preCheckFunc: opts.PreCheckFunc,
preCheckIPv4: opts.PreCheckIPv4,
preCheckIPv6: opts.PreCheckIPv6,
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
}

View File

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

View File

@ -12,7 +12,12 @@ func (nm *NetworkManager) IsOnline() 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 {
@ -74,6 +79,20 @@ func (nm *NetworkManager) GetMACAddress() string {
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 {
return nm.GetIPv4Address()
}

View File

@ -43,6 +43,18 @@ func initTimeSync() {
timeSync = timesync.NewTimeSync(&timesync.TimeSyncOptions{
Logger: timesyncLogger,
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) {
if !networkManager.IsOnline() {
return false, nil