refactor: enhance AutoInterface with done channel and improved locking for concurrency
This commit is contained in:
@@ -53,12 +53,13 @@ type AutoInterface struct {
|
|||||||
timedOutInterfaces map[string]time.Time
|
timedOutInterfaces map[string]time.Time
|
||||||
allowedInterfaces []string
|
allowedInterfaces []string
|
||||||
ignoredInterfaces []string
|
ignoredInterfaces []string
|
||||||
mutex sync.RWMutex
|
|
||||||
outboundConn *net.UDPConn
|
outboundConn *net.UDPConn
|
||||||
announceInterval time.Duration
|
announceInterval time.Duration
|
||||||
peerJobInterval time.Duration
|
peerJobInterval time.Duration
|
||||||
peeringTimeout time.Duration
|
peeringTimeout time.Duration
|
||||||
mcastEchoTimeout time.Duration
|
mcastEchoTimeout time.Duration
|
||||||
|
done chan struct{}
|
||||||
|
stopOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdoptedInterface struct {
|
type AdoptedInterface struct {
|
||||||
@@ -135,6 +136,7 @@ func NewAutoInterface(name string, config *common.InterfaceConfig) (*AutoInterfa
|
|||||||
peerJobInterval: PEER_JOB_INTERVAL,
|
peerJobInterval: PEER_JOB_INTERVAL,
|
||||||
peeringTimeout: PEERING_TIMEOUT,
|
peeringTimeout: PEERING_TIMEOUT,
|
||||||
mcastEchoTimeout: MCAST_ECHO_TIMEOUT,
|
mcastEchoTimeout: MCAST_ECHO_TIMEOUT,
|
||||||
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log(debug.DEBUG_INFO, "AutoInterface configured", "name", name, "group", groupID, "mcast_addr", mcastAddr)
|
debug.Log(debug.DEBUG_INFO, "AutoInterface configured", "name", name, "group", groupID, "mcast_addr", mcastAddr)
|
||||||
@@ -170,6 +172,20 @@ func normalizeMulticastType(mtype string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ai *AutoInterface) Start() error {
|
func (ai *AutoInterface) Start() error {
|
||||||
|
ai.Mutex.Lock()
|
||||||
|
// Only recreate done if it's nil or was closed
|
||||||
|
select {
|
||||||
|
case <-ai.done:
|
||||||
|
ai.done = make(chan struct{})
|
||||||
|
ai.stopOnce = sync.Once{}
|
||||||
|
default:
|
||||||
|
if ai.done == nil {
|
||||||
|
ai.done = make(chan struct{})
|
||||||
|
ai.stopOnce = sync.Once{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ai.Mutex.Unlock()
|
||||||
|
|
||||||
interfaces, err := net.Interfaces()
|
interfaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list interfaces: %v", err)
|
return fmt.Errorf("failed to list interfaces: %v", err)
|
||||||
@@ -186,7 +202,9 @@ func (ai *AutoInterface) Start() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ai.configureInterface(&iface); err != nil {
|
ifaceCopy := iface
|
||||||
|
// bearer:disable go_gosec_memory_memory_aliasing
|
||||||
|
if err := ai.configureInterface(&ifaceCopy); err != nil {
|
||||||
debug.Log(debug.DEBUG_VERBOSE, "Failed to configure interface", "name", iface.Name, "error", err)
|
debug.Log(debug.DEBUG_VERBOSE, "Failed to configure interface", "name", iface.Name, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -262,7 +280,7 @@ func (ai *AutoInterface) configureInterface(iface *net.Interface) error {
|
|||||||
return fmt.Errorf("no link-local IPv6 address found")
|
return fmt.Errorf("no link-local IPv6 address found")
|
||||||
}
|
}
|
||||||
|
|
||||||
ai.mutex.Lock()
|
ai.Mutex.Lock()
|
||||||
ai.adoptedInterfaces[iface.Name] = &AdoptedInterface{
|
ai.adoptedInterfaces[iface.Name] = &AdoptedInterface{
|
||||||
name: iface.Name,
|
name: iface.Name,
|
||||||
linkLocalAddr: linkLocalAddr,
|
linkLocalAddr: linkLocalAddr,
|
||||||
@@ -270,7 +288,7 @@ func (ai *AutoInterface) configureInterface(iface *net.Interface) error {
|
|||||||
}
|
}
|
||||||
ai.linkLocalAddrs = append(ai.linkLocalAddrs, linkLocalAddr)
|
ai.linkLocalAddrs = append(ai.linkLocalAddrs, linkLocalAddr)
|
||||||
ai.multicastEchoes[iface.Name] = time.Now()
|
ai.multicastEchoes[iface.Name] = time.Now()
|
||||||
ai.mutex.Unlock()
|
ai.Mutex.Unlock()
|
||||||
|
|
||||||
if err := ai.startDiscoveryListener(iface); err != nil {
|
if err := ai.startDiscoveryListener(iface); err != nil {
|
||||||
return fmt.Errorf("failed to start discovery listener: %v", err)
|
return fmt.Errorf("failed to start discovery listener: %v", err)
|
||||||
@@ -296,13 +314,13 @@ func (ai *AutoInterface) startDiscoveryListener(iface *net.Interface) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.SetReadBuffer(1024); err != nil {
|
if err := conn.SetReadBuffer(common.NUM_1024); err != nil {
|
||||||
debug.Log(debug.DEBUG_ERROR, "Failed to set discovery read buffer", "error", err)
|
debug.Log(debug.DEBUG_ERROR, "Failed to set discovery read buffer", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ai.mutex.Lock()
|
ai.Mutex.Lock()
|
||||||
ai.discoveryServers[iface.Name] = conn
|
ai.discoveryServers[iface.Name] = conn
|
||||||
ai.mutex.Unlock()
|
ai.Mutex.Unlock()
|
||||||
|
|
||||||
go ai.handleDiscovery(conn, iface.Name)
|
go ai.handleDiscovery(conn, iface.Name)
|
||||||
debug.Log(debug.DEBUG_VERBOSE, "Discovery listener started", "interface", iface.Name, "addr", ai.mcastDiscoveryAddr)
|
debug.Log(debug.DEBUG_VERBOSE, "Discovery listener started", "interface", iface.Name, "addr", ai.mcastDiscoveryAddr)
|
||||||
@@ -331,9 +349,9 @@ func (ai *AutoInterface) startDataListener(iface *net.Interface) error {
|
|||||||
debug.Log(debug.DEBUG_ERROR, "Failed to set data read buffer", "error", err)
|
debug.Log(debug.DEBUG_ERROR, "Failed to set data read buffer", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ai.mutex.Lock()
|
ai.Mutex.Lock()
|
||||||
ai.interfaceServers[iface.Name] = conn
|
ai.interfaceServers[iface.Name] = conn
|
||||||
ai.mutex.Unlock()
|
ai.Mutex.Unlock()
|
||||||
|
|
||||||
go ai.handleData(conn, iface.Name)
|
go ai.handleData(conn, iface.Name)
|
||||||
debug.Log(debug.DEBUG_VERBOSE, "Data listener started", "interface", iface.Name, "addr", addr)
|
debug.Log(debug.DEBUG_VERBOSE, "Data listener started", "interface", iface.Name, "addr", addr)
|
||||||
@@ -341,8 +359,18 @@ func (ai *AutoInterface) startDataListener(iface *net.Interface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ai *AutoInterface) handleDiscovery(conn *net.UDPConn, ifaceName string) {
|
func (ai *AutoInterface) handleDiscovery(conn *net.UDPConn, ifaceName string) {
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, common.NUM_1024)
|
||||||
for {
|
for {
|
||||||
|
ai.Mutex.RLock()
|
||||||
|
done := ai.done
|
||||||
|
ai.Mutex.RUnlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
n, remoteAddr, err := conn.ReadFromUDP(buf)
|
n, remoteAddr, err := conn.ReadFromUDP(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ai.IsOnline() {
|
if ai.IsOnline() {
|
||||||
@@ -365,6 +393,16 @@ func (ai *AutoInterface) handleDiscovery(conn *net.UDPConn, ifaceName string) {
|
|||||||
func (ai *AutoInterface) handleData(conn *net.UDPConn, ifaceName string) {
|
func (ai *AutoInterface) handleData(conn *net.UDPConn, ifaceName string) {
|
||||||
buf := make([]byte, ai.GetMTU())
|
buf := make([]byte, ai.GetMTU())
|
||||||
for {
|
for {
|
||||||
|
ai.Mutex.RLock()
|
||||||
|
done := ai.done
|
||||||
|
ai.Mutex.RUnlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
n, _, err := conn.ReadFromUDP(buf)
|
n, _, err := conn.ReadFromUDP(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ai.IsOnline() {
|
if ai.IsOnline() {
|
||||||
@@ -380,8 +418,8 @@ func (ai *AutoInterface) handleData(conn *net.UDPConn, ifaceName string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ai *AutoInterface) handlePeerAnnounce(addr *net.UDPAddr, ifaceName string) {
|
func (ai *AutoInterface) handlePeerAnnounce(addr *net.UDPAddr, ifaceName string) {
|
||||||
ai.mutex.Lock()
|
ai.Mutex.Lock()
|
||||||
defer ai.mutex.Unlock()
|
defer ai.Mutex.Unlock()
|
||||||
|
|
||||||
peerIP := addr.IP.String()
|
peerIP := addr.IP.String()
|
||||||
|
|
||||||
@@ -412,17 +450,22 @@ func (ai *AutoInterface) announceLoop() {
|
|||||||
ticker := time.NewTicker(ai.announceInterval)
|
ticker := time.NewTicker(ai.announceInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for range ticker.C {
|
for {
|
||||||
if !ai.IsOnline() {
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
if !ai.IsOnline() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ai.sendPeerAnnounce()
|
||||||
|
case <-ai.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ai.sendPeerAnnounce()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ai *AutoInterface) sendPeerAnnounce() {
|
func (ai *AutoInterface) sendPeerAnnounce() {
|
||||||
ai.mutex.RLock()
|
ai.Mutex.RLock()
|
||||||
defer ai.mutex.RUnlock()
|
defer ai.Mutex.RUnlock()
|
||||||
|
|
||||||
for ifaceName, adoptedIface := range ai.adoptedInterfaces {
|
for ifaceName, adoptedIface := range ai.adoptedInterfaces {
|
||||||
mcastAddr := &net.UDPAddr{
|
mcastAddr := &net.UDPAddr{
|
||||||
@@ -452,33 +495,38 @@ func (ai *AutoInterface) peerJobs() {
|
|||||||
ticker := time.NewTicker(ai.peerJobInterval)
|
ticker := time.NewTicker(ai.peerJobInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for range ticker.C {
|
for {
|
||||||
if !ai.IsOnline() {
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
if !ai.IsOnline() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ai.Mutex.Lock()
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for peerKey, peer := range ai.peers {
|
||||||
|
if now.Sub(peer.lastHeard) > ai.peeringTimeout {
|
||||||
|
delete(ai.peers, peerKey)
|
||||||
|
debug.Log(debug.DEBUG_VERBOSE, "Removed timed out peer", "peer", peerKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ifaceName, echoTime := range ai.multicastEchoes {
|
||||||
|
if now.Sub(echoTime) > ai.mcastEchoTimeout {
|
||||||
|
if _, exists := ai.timedOutInterfaces[ifaceName]; !exists {
|
||||||
|
debug.Log(debug.DEBUG_INFO, "Interface timed out", "interface", ifaceName)
|
||||||
|
ai.timedOutInterfaces[ifaceName] = now
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete(ai.timedOutInterfaces, ifaceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ai.Mutex.Unlock()
|
||||||
|
case <-ai.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ai.mutex.Lock()
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
for peerKey, peer := range ai.peers {
|
|
||||||
if now.Sub(peer.lastHeard) > ai.peeringTimeout {
|
|
||||||
delete(ai.peers, peerKey)
|
|
||||||
debug.Log(debug.DEBUG_VERBOSE, "Removed timed out peer", "peer", peerKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ifaceName, echoTime := range ai.multicastEchoes {
|
|
||||||
if now.Sub(echoTime) > ai.mcastEchoTimeout {
|
|
||||||
if _, exists := ai.timedOutInterfaces[ifaceName]; !exists {
|
|
||||||
debug.Log(debug.DEBUG_INFO, "Interface timed out", "interface", ifaceName)
|
|
||||||
ai.timedOutInterfaces[ifaceName] = now
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete(ai.timedOutInterfaces, ifaceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ai.mutex.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,8 +535,8 @@ func (ai *AutoInterface) Send(data []byte, address string) error {
|
|||||||
return fmt.Errorf("interface offline")
|
return fmt.Errorf("interface offline")
|
||||||
}
|
}
|
||||||
|
|
||||||
ai.mutex.RLock()
|
ai.Mutex.RLock()
|
||||||
defer ai.mutex.RUnlock()
|
defer ai.Mutex.RUnlock()
|
||||||
|
|
||||||
if len(ai.peers) == 0 {
|
if len(ai.peers) == 0 {
|
||||||
debug.Log(debug.DEBUG_TRACE, "No peers available for sending")
|
debug.Log(debug.DEBUG_TRACE, "No peers available for sending")
|
||||||
@@ -526,9 +574,7 @@ func (ai *AutoInterface) Send(data []byte, address string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ai *AutoInterface) Stop() error {
|
func (ai *AutoInterface) Stop() error {
|
||||||
ai.mutex.Lock()
|
ai.Mutex.Lock()
|
||||||
defer ai.mutex.Unlock()
|
|
||||||
|
|
||||||
ai.Online = false
|
ai.Online = false
|
||||||
ai.IN = false
|
ai.IN = false
|
||||||
ai.OUT = false
|
ai.OUT = false
|
||||||
@@ -544,6 +590,13 @@ func (ai *AutoInterface) Stop() error {
|
|||||||
if ai.outboundConn != nil {
|
if ai.outboundConn != nil {
|
||||||
ai.outboundConn.Close() // #nosec G104
|
ai.outboundConn.Close() // #nosec G104
|
||||||
}
|
}
|
||||||
|
ai.Mutex.Unlock()
|
||||||
|
|
||||||
|
ai.stopOnce.Do(func() {
|
||||||
|
if ai.done != nil {
|
||||||
|
close(ai.done)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
debug.Log(debug.DEBUG_INFO, "AutoInterface stopped")
|
debug.Log(debug.DEBUG_INFO, "AutoInterface stopped")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user