Files
go-meta-listener/listener.go

90 lines
1.9 KiB
Go

package meta
import (
"fmt"
"net"
)
// Accept implements the net.Listener Accept method.
// It returns the next connection from any of the managed listeners.
func (ml *MetaListener) Accept() (net.Conn, error) {
// Check if already closed before entering the select loop
ml.mu.RLock()
if ml.isClosed {
ml.mu.RUnlock()
return nil, ErrListenerClosed
}
ml.mu.RUnlock()
for {
select {
case result, ok := <-ml.connCh:
if !ok {
return nil, ErrListenerClosed
}
// Access RemoteAddr() directly on the connection
return result, nil
case <-ml.closeCh:
// Double-check the closed state under lock to ensure consistency
closed := ml.isClosed
if closed {
return nil, ErrListenerClosed
}
continue
}
}
}
// Close implements the net.Listener Close method.
// It closes all managed listeners and releases resources.
func (ml *MetaListener) Close() error {
ml.mu.Lock()
if ml.isClosed {
ml.mu.Unlock()
return nil
}
log.Printf("Closing MetaListener with %d listeners", len(ml.listeners))
ml.isClosed = true
// Signal all goroutines to stop
close(ml.closeCh)
// Close all listeners
var errs []error
for id, listener := range ml.listeners {
if err := listener.Close(); err != nil {
log.Printf("Error closing %s listener: %v", id, err)
errs = append(errs, err)
}
}
ml.mu.Unlock()
// Wait for all listener goroutines to exit
ml.listenerWg.Wait()
log.Printf("All listener goroutines have exited")
// Return combined errors if any
if len(errs) > 0 {
return fmt.Errorf("errors closing listeners: %v", errs)
}
return nil
}
// Addr implements the net.Listener Addr method.
// It returns a MetaAddr representing all managed listeners.
func (ml *MetaListener) Addr() net.Addr {
ml.mu.RLock()
defer ml.mu.RUnlock()
addresses := make([]net.Addr, 0, len(ml.listeners))
for _, listener := range ml.listeners {
addresses = append(addresses, listener.Addr())
}
return &MetaAddr{addresses: addresses}
}