diff --git a/swarm.go b/swarm.go index b14e9cf0b95fd5e944346e646ec41b45012e6353..642faea8568968b2720ffdd00f7ea8b3378ff1ca 100644 --- a/swarm.go +++ b/swarm.go @@ -20,6 +20,7 @@ import ( goprocessctx "github.com/jbenet/goprocess/context" filter "github.com/libp2p/go-maddr-filter" + ma "github.com/multiformats/go-multiaddr" mafilter "github.com/whyrusleeping/multiaddr-filter" ) @@ -58,6 +59,10 @@ type Swarm struct { listeners struct { sync.RWMutex + + ifaceAddresses []ma.Multiaddr + cacheEOL time.Time + m map[transport.Listener]struct{} } diff --git a/swarm_addr.go b/swarm_addr.go index c86d58bb4de74e2f056666ff902298af78920431..10179fa3b9b159e7baed41ab9f4e2531ecdd916b 100644 --- a/swarm_addr.go +++ b/swarm_addr.go @@ -1,6 +1,8 @@ package swarm import ( + "time" + addrutil "github.com/libp2p/go-addr-util" ma "github.com/multiformats/go-multiaddr" ) @@ -9,6 +11,10 @@ import ( func (s *Swarm) ListenAddresses() []ma.Multiaddr { s.listeners.RLock() defer s.listeners.RUnlock() + return s.listenAddressesNoLock() +} + +func (s *Swarm) listenAddressesNoLock() []ma.Multiaddr { addrs := make([]ma.Multiaddr, 0, len(s.listeners.m)) for l := range s.listeners.m { addrs = append(addrs, l.Multiaddr()) @@ -16,9 +22,47 @@ func (s *Swarm) ListenAddresses() []ma.Multiaddr { return addrs } +const ifaceAddrsCacheDuration = 1 * time.Minute + // InterfaceListenAddresses returns a list of addresses at which this swarm // listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to // use the known local interfaces. func (s *Swarm) InterfaceListenAddresses() ([]ma.Multiaddr, error) { - return addrutil.ResolveUnspecifiedAddresses(s.ListenAddresses(), nil) + s.listeners.RLock() // RLock start + listenAddrs := s.listenAddressesNoLock() + + ifaceAddrs := s.listeners.ifaceAddresses + isEOL := time.Now().After(s.listeners.cacheEOL) + s.listeners.RUnlock() // RLock end + + if listenAddrs != nil && !isEOL { + // Cache is valid + return addrutil.ResolveUnspecifiedAddresses(listenAddrs, ifaceAddrs) + } + + // Cache is not valid + // Perfrom double checked locking + + s.listeners.Lock() // Lock start + + listenAddrs = s.listenAddressesNoLock() + + ifaceAddrs = s.listeners.ifaceAddresses + isEOL = time.Now().After(s.listeners.cacheEOL) + if listenAddrs == nil || isEOL { + // Cache is still invalid + var err error + ifaceAddrs, err = addrutil.InterfaceAddresses() + if err != nil { + s.listeners.Unlock() // Lock early exit + return nil, err + } + + s.listeners.ifaceAddresses = ifaceAddrs + s.listeners.cacheEOL = time.Now().Add(ifaceAddrsCacheDuration) + } + + s.listeners.Unlock() // Lock end + + return addrutil.ResolveUnspecifiedAddresses(listenAddrs, ifaceAddrs) }