func (tab *STable) add(chainid common.ChainID, n *Node) {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	b := tab.bench(chainid)
	if b == nil {
		b = new(bench)
		tab.benches.Store(chainid, b)
	}
	tab.bumpOrAdd(b, n)
}
add attempts to add the given node to its corresponding bench. If the bench has space
available, adding the node succeeds immediately. Otherwise, the node is added if the
least recently active node in the bench does not respond to a ping packetSort.

The caller must not hold tab.mutex.
package discover
import (
	"bytes"
	crand "crypto/rand"
	"encoding/binary"
	"fmt"
	mrand "math/rand"
	"net"
	"sync"
	"time"
	"github.com/ThinkiumGroup/go-common"
	"github.com/ThinkiumGroup/go-common/log"
	"github.com/ThinkiumGroup/go-thinkium/config"
)
const MaxPeersPerChain = 10
const benchSize = 128
type bench struct {
	seats []*Node
	ips   DistinctNetSet
}
func (b *bench) bump(n *Node) bool {
	if b.seats == nil {
		n.addedAt = time.Now()
		b.seats = []*Node{n}
		return true
	}
	for i := range b.seats {
		if b.seats[i].ID == n.ID {
			copy(b.seats[1:], b.seats[:i])
			b.seats[0] = n
			return true
		}
	}
	return false
}
type STable struct {
	mutex      sync.Mutex  
	chainId    common.ChainID
	bootId     common.ChainID
	netType    common.NetType
	dataNodes  []*ChainDataNodes
	tmpNodes   []*ChainDataNodes  
	benches    sync.Map           
	nursery    []*Node            
	rand       *mrand.Rand        
	ips        DistinctNetSet
	db         *nodeDB  
	refreshReq chan chan struct{}
	initDone   chan struct{}
	closeReq   chan struct{}
	closed     chan struct{}
	discv Discovery
	self  *Node  
}
func newSTable(d Discovery, self *Node, cfg UDPConfig) (*STable, error) {
	db, err := newNodeDB(cfg.NodeDBPath, nodeDBVersion, self.ID)
	if err != nil {
		return nil, err
	}
	tab := &STable{
		chainId:    cfg.ChainID,
		bootId:     cfg.BootId,
		netType:    cfg.NetType,
		dataNodes:  cfg.ChainDataNodes,
		discv:      d,
		self:       self,
		db:         db,
		refreshReq: make(chan chan struct{}),
		initDone:   make(chan struct{}),
		closeReq:   make(chan struct{}),
		closed:     make(chan struct{}),
		rand:       mrand.New(mrand.NewSource(0)),
		ips:        DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
	}
	if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
		return nil, err
	}
	tab.seedRand()
	tab.loadSeedNodes()
	return tab, nil
}
func (tab *STable) seedRand() {
	var b [8]byte
	crand.Read(b[:])
	tab.mutex.Lock()
	tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:])))
	tab.mutex.Unlock()
}
func (tab *STable) Self() *Node {
	return tab.self
}
func (tab *STable) ReadRandomNodes(buf []*Node) (n int) {
	if !tab.isInitDone() {
		return 0
	}
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	cids := GetVisitChainIds(tab.dataNodes, tab.chainId)
	if cids == nil {
		return 0
	}
	var buckets [][]*Node
	for _, cid := range cids {
		val, ok := tab.benches.Load(cid)
		if !ok {
			continue
		}
		bench := val.(*bench)
		if len(bench.seats) > 0 {
			buckets = append(buckets, bench.seats)
		}
	}
	if len(buckets) == 0 {
		return 0
	}
	for i := len(buckets) - 1; i > 0; i-- {
		j := tab.rand.Intn(len(buckets))
		buckets[i], buckets[j] = buckets[j], buckets[i]
	}
	var i, j int
	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
		b := buckets[j]
		buf[i] = &(*b[0])
		buckets[j] = b[1:]
		if len(b) == 1 {
			buckets = append(buckets[:j], buckets[j+1:]...)
		}
		if len(buckets) == 0 {
			break
		}
	}
	return i + 1
}
func (tab *STable) Close() {
	select {
	case <-tab.closed:
	case tab.closeReq <- struct{}{}:
		<-tab.closed  
	}
}
func (tab *STable) setFallbackNodes(nodes []*Node) error {
	for _, n := range nodes {
		if err := n.validateComplete(); err != nil {
			return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err)
		}
	}
	tab.nursery = make([]*Node, 0, len(nodes))
	for _, n := range nodes {
		cpy := *n
		cpy.Hash = common.Hash256(n.ID[:])
		tab.nursery = append(tab.nursery, &cpy)
	}
	return nil
}
func (tab *STable) isInitDone() bool {
	select {
	case <-tab.initDone:
		return true
	default:
		return false
	}
}
func (tab *STable) Resolve(targetID common.NodeID) *Node {
	var node *Node
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		for _, n := range b.seats {
			if bytes.Equal(n.ID[:], targetID[:]) {
				node = n
				return false
			}
		}
		return true
	})
	if node == nil {
		node = tab.Find(targetID)
	}
	return node
}
func (tab *STable) Find(target common.NodeID) *Node {
	var (
		asked          = make(map[common.NodeID]bool)
		reply          = make(chan map[common.ChainID][]*Node, MaxPeersPerChain)
		pendingQueries = 0
		result         map[common.ChainID][]*Node
	)
	asked[tab.self.ID] = true
	result = tab.benchRow(MaxPeersPerChain)
	for {
		for _, ns := range result {
			pendingQueries = 0
			for i := 0; i < len(ns) && pendingQueries < MaxPeersPerChain; i++ {
				if ns[i] == nil {
					continue
				}
				if !asked[ns[i].ID] {
					asked[ns[i].ID] = true
					pendingQueries++
					go tab.findnode(ns[i], reply)
				}
			}
			if pendingQueries == MaxPeersPerChain {
				break
			}
		}
		if pendingQueries == 0 {
			break
		}
		for _, ns := range <-reply {
			for _, n := range ns {
				if bytes.Equal(n.ID[:], target[:]) {
					return n
				}
			}
		}
		pendingQueries--
	}
	return nil
}
func (tab *STable) Lookup(target interface{}) []*Node {
	return tab.lookup(true)
}
func (tab *STable) GetDataNodes() []*ChainDataNodes {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return tab.dataNodes
}
func (tab *STable) GetAccessChains() common.ChainIDs {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return GetConnectedChains(tab.dataNodes, tab.chainId)
}
func (tab *STable) SetTmpNodes(dataNodes []*ChainDataNodes) {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	if dataNodes == nil || tab.tmpNodes != nil {
		return
	}
	h1, err := common.HashObject(tab.dataNodes)
	if err != nil {
		log.Errorf("P2P chain %d net %s hash DataNodes error %v", tab.chainId, tab.netType, err)
		return
	}
	h2, err := common.HashObject(dataNodes)
	if err != nil {
		log.Errorf("P2P chain %d net %s hash tmpNodes error %v", tab.chainId, tab.netType, err)
		return
	}
	if bytes.Equal(h1, h2) {
		if config.IsLogOn(config.NetDebugLog) {
			log.Debugf("P2P chain %d net %s ignore unchanged chains", tab.chainId, tab.netType)
		}
		return
	}
	tab.tmpNodes = dataNodes
}
func (tab *STable) SwitchToTmpNodes() {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	if tab.tmpNodes == nil {
		return
	}
	tab.dataNodes = tab.tmpNodes
	tab.tmpNodes = nil
}
func (tab *STable) lookup(refreshIfEmpty bool) []*Node {
	var (
		asked          = make(map[common.NodeID]bool)
		seen           = make(map[common.NodeID]bool)
		reply          = make(chan map[common.ChainID][]*Node, MaxPeersPerChain)
		pendingQueries = 0
		result         map[common.ChainID][]*Node
		ret            []*Node
	)
	asked[tab.self.ID] = true
	for {
		result = tab.benchRow(MaxPeersPerChain)
		if len(result) > 0 || !refreshIfEmpty {
			break
		}
		<-tab.refresh()
		refreshIfEmpty = false
	}
	chains := GetVisitChainIds(tab.dataNodes, tab.chainId)
	tmpChains := GetVisitChainIds(tab.tmpNodes, tab.chainId)
	if config.IsLogOn(config.NetDebugLog) {
		log.Debugf("P2P SORT GetVisitChainIds for chain %d net %s return chains %s tmpChains %s", tab.chainId, tab.netType, chains, tmpChains)
	}
	for {
		for _, ns := range result {
			for i := 0; i < len(ns) && pendingQueries < MaxPeersPerChain; i++ {
				n := ns[i]
				if n == nil {
					continue
				}
				if !asked[n.ID] {
					asked[n.ID] = true
					pendingQueries++
					go tab.findnode(n, reply)
				}
			}
			if pendingQueries == MaxPeersPerChain {
				break
			}
		}
		if pendingQueries == 0 {
			break
		}
		for cid, ns := range <-reply {
			for _, n := range ns {
				if n != nil && !seen[n.ID] {
					seen[n.ID] = true
					if IsIn(chains, cid) || IsIn(tmpChains, cid) {
						ret = append(ret, n)
					}
				}
			}
		}
		pendingQueries--
	}
	return ret
}
func (tab *STable) findnode(n *Node, reply chan<- map[common.ChainID][]*Node) {
	if tab == nil || n == nil {
		return
	}
	fails := tab.db.findFails(n.ID)
	r, err := tab.discv.FindNode(n.ID, n.UdpAddr(), nil)
	if err == errEmptyTable {
		tab.Close()
		return
	}
	if err != nil || len(r) == 0 {
		fails++
		tab.db.updateFindFails(n.ID, fails)
		if fails >= maxFindnodeFailures {
			tab.delete(tab.chainId, n)
		}
	} else if fails > 0 {
		tab.db.updateFindFails(n.ID, fails-1)
	}
	for c, ns := range r {
		for _, nd := range ns {
			tab.add(c, nd)
		}
	}
	reply <- r
}
func (tab *STable) refresh() <-chan struct{} {
	done := make(chan struct{})
	select {
	case tab.refreshReq <- done:
	case <-tab.closed:
		close(done)
	}
	return done
}
func (tab *STable) loop() {
	var (
		revalidate     = time.NewTimer(tab.nextRevalidateTime())
		refresh        = time.NewTicker(refreshInterval)
		copyNodes      = time.NewTicker(copyNodesInterval)
		revalidateDone = make(chan struct{})
		refreshDone    = make(chan struct{})            
		waiting        = []chan struct{}{tab.initDone}  
	)
	defer refresh.Stop()
	defer revalidate.Stop()
	defer copyNodes.Stop()
	go tab.doRefresh(refreshDone)
loop:
	for {
		select {
		case <-refresh.C:
			tab.seedRand()
			if refreshDone == nil {
				refreshDone = make(chan struct{})
				go tab.doRefresh(refreshDone)
			}
		case req := <-tab.refreshReq:
			waiting = append(waiting, req)
			if refreshDone == nil {
				refreshDone = make(chan struct{})
				go tab.doRefresh(refreshDone)
			}
		case <-refreshDone:
			for _, ch := range waiting {
				close(ch)
			}
			waiting, refreshDone = nil, nil
		case <-revalidate.C:
			go tab.doRevalidate(revalidateDone)
		case <-revalidateDone:
			revalidate.Reset(tab.nextRevalidateTime())
		case <-copyNodes.C:
			go tab.copyLiveNodes()
		case <-tab.closeReq:
			break loop
		}
	}
	if refreshDone != nil {
		<-refreshDone
	}
	for _, ch := range waiting {
		close(ch)
	}
	tab.db.close()
	close(tab.closed)
}
func (tab *STable) doRefresh(done chan struct{}) {
	defer close(done)
	tab.loadSeedNodes()
	tab.lookup(false)
}
func (tab *STable) loadSeedNodes() {
	var seeds []*Node
	seeds = append(seeds, tab.nursery...)
	for i := range seeds {
		seed := seeds[i]
		tab.add(tab.bootId, seed)
	}
	if config.IsLogOn(config.NetDebugLog) {
		log.Debugf("P2P SORT UDP ChainId %d bootId %d loadSeedNodes %s", tab.chainId, tab.bootId, seeds)
	}
}
func (tab *STable) doRevalidate(done chan<- struct{}) {
	defer func() { done <- struct{}{} }()
	lasts := tab.nodeToRevalidate()
	if len(lasts) == 0 {
		return
	}
	for c, last := range lasts {
		err := tab.discv.Ping(last.ID, last.UdpAddr())
		tab.mutex.Lock()
		b := tab.bench(c)
		if b == nil {
			tab.mutex.Unlock()
			continue
		}
		if err == nil {
			b.bump(last)
			tab.mutex.Unlock()
			continue
		}
		if r := tab.replace(b, last); r != nil {
			if config.IsLogOn(config.NetDebugLog) {
				log.Debugf("P2P Removed dead node chain %d id %s ip %s r %s rip %s", c, last.ID, last.IP, r.ID, r.IP)
			}
		} else {
			if config.IsLogOn(config.NetDebugLog) {
				log.Debugf("P2P Replaced dead node chain %d id %s ip %s", c, last.ID, last.IP)
			}
		}
		tab.mutex.Unlock()
	}
}
func (tab *STable) nodeToRevalidate() map[common.ChainID]*Node {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	ret := make(map[common.ChainID]*Node)
	tab.benches.Range(func(key, value interface{}) bool {
		cid := key.(common.ChainID)
		bench := value.(*bench)
		if bench == nil || len(bench.seats) == 0 {
			return true
		}
		ret[cid] = bench.seats[len(bench.seats)-1]
		return true
	})
	return ret
}
func (tab *STable) nextRevalidateTime() time.Duration {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return time.Duration(tab.rand.Int63n(int64(revalidateInterval)))
}
func (tab *STable) copyLiveNodes() {
}
func (tab *STable) benchRow(n int) map[common.ChainID][]*Node {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	ret := make(map[common.ChainID][]*Node)
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		if len(b.seats) > 0 {
			c := key.(common.ChainID)
			if n > len(b.seats) {
				ret[c] = b.seats
			} else {
				ret[c] = b.seats[:n]
			}
		}
		return true
	})
	return ret
}
func (tab *STable) Len() (n int) {
	return tab.len()
}
func (tab *STable) len() (n int) {
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		n += len(b.seats)
		return true
	})
	return n
}
func (tab *STable) bench(chainId common.ChainID) *bench {
	if b, ok := tab.benches.Load(chainId); ok {
		return b.(*bench)
	}
	return nil
}