From 9f9dd2404281e3cf09d76076e20d9f88455cbbc9 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Mon, 3 Jun 2019 23:14:41 +0200 Subject: [PATCH] feat: cache interface addresses for 1 minute This can be quite an overhead in cases of high connection rates. The main overhead is thread blocking syscall causing a lot of context switching. License: MIT Signed-off-by: Jakub Sztandera --- swarm.go | 5 +++++ swarm_addr.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/swarm.go b/swarm.go index b14e9cf..642faea 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 c86d58b..10179fa 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) } -- GitLab