func (tab *Table) 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)
}
loop schedules refresh, revalidate runs and coordinates shutdown.
package discover
import (
	crand "crypto/rand"
	"encoding/binary"
	"fmt"
	mrand "math/rand"
	"net"
	"sort"
	"sync"
	"time"
	"github.com/ThinkiumGroup/go-common"
	"github.com/ThinkiumGroup/go-common/log"
	"github.com/ThinkiumGroup/go-thinkium/config"
)
const (
	alpha           = 3   
	bucketSize      = 16  
	maxReplacements = 10  
	hashBits          = len(common.Hash{}) * 8
	nBuckets          = hashBits / 15        
	bucketMinDistance = hashBits - nBuckets  
	bucketIPLimit, bucketSubnet = 2, 24  
	tableIPLimit, tableSubnet   = 10, 24
	maxFindnodeFailures = 5  
	refreshInterval     = 30 * time.Minute
	revalidateInterval  = 10 * time.Second
	copyNodesInterval   = 10 * time.Minute
	seedMinTableTime    = 1 * time.Hour
	seedCount           = 30
	seedMaxAge          = 5 * 24 * time.Hour
)
type Table struct {
	mutex   sync.Mutex  
	chainId common.ChainID
	bootId  common.ChainID
	netType common.NetType
	buckets [nBuckets]*bucket  
	nursery []*Node            
	rand    *mrand.Rand        
	ips     DistinctNetSet
	db         *nodeDB  
	refreshReq chan chan struct{}
	initDone   chan struct{}
	closeReq   chan struct{}
	closed     chan struct{}
	nodeAddedHook func(*Node)  
	discv Discovery
	self  *Node  
}
type bucket struct {
	entries      []*Node  
	replacements []*Node  
	ips          DistinctNetSet
}
func newTable(d Discovery, self *Node, cfg UDPConfig) (*Table, error) {
	db, err := newNodeDB(cfg.NodeDBPath, nodeDBVersion, self.ID)
	if err != nil {
		return nil, err
	}
	tab := &Table{
		chainId:    cfg.ChainID,
		bootId:     cfg.BootId,
		netType:    cfg.NetType,
		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
	}
	for i := range tab.buckets {
		tab.buckets[i] = &bucket{
			ips: DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit},
		}
	}
	tab.seedRand()
	tab.loadSeedNodes()
	return tab, nil
}
func (tab *Table) seedRand() {
	var b [8]byte
	crand.Read(b[:])
	tab.mutex.Lock()
	tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:])))
	tab.mutex.Unlock()
}
func (tab *Table) Self() *Node {
	return tab.self
}
func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
	if !tab.isInitDone() {
		return 0
	}
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	var buckets [][]*Node
	for _, b := range &tab.buckets {
		if len(b.entries) > 0 {
			buckets = append(buckets, b.entries)
		}
	}
	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 *Table) Close() {
	select {
	case <-tab.closed:
	case tab.closeReq <- struct{}{}:
		<-tab.closed  
	}
}
func (tab *Table) 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 *Table) isInitDone() bool {
	select {
	case <-tab.initDone:
		return true
	default:
		return false
	}
}
func (tab *Table) Resolve(targetID common.NodeID) *Node {
	hash := common.Hash256(targetID[:])
	tab.mutex.Lock()
	cl := tab.closest(hash, 1)
	tab.mutex.Unlock()
	if len(cl.entries) > 0 && cl.entries[0].ID == targetID {
		return cl.entries[0]
	}
	result := tab.Lookup(targetID)
	for _, n := range result {
		if n.ID == targetID {
			return n
		}
	}
	return nil
}
func (tab *Table) Lookup(targetID interface{}) []*Node {
	return tab.lookup(targetID.(common.NodeID), true)
}
func (tab *Table) GetDataNodes() []*ChainDataNodes {
	return nil
}
func (tab *Table) GetAccessChains() common.ChainIDs {
	return nil
}
func (tab *Table) SetTmpNodes(dataNodes []*ChainDataNodes) {
}
func (tab *Table) SwitchToTmpNodes() {
}
func (tab *Table) lookup(targetID common.NodeID, refreshIfEmpty bool) []*Node {
	var (
		target         = common.Hash256(targetID[:])
		asked          = make(map[common.NodeID]bool)
		seen           = make(map[common.NodeID]bool)
		reply          = make(chan []*Node, alpha)
		pendingQueries = 0
		result         *nodesByDistance
	)
	asked[tab.self.ID] = true
	for {
		tab.mutex.Lock()
		result = tab.closest(target, bucketSize)
		tab.mutex.Unlock()
		if len(result.entries) > 0 || !refreshIfEmpty {
			break
		}
		<-tab.refresh()
		refreshIfEmpty = false
	}
	for {
		for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
			n := result.entries[i]
			if !asked[n.ID] {
				asked[n.ID] = true
				pendingQueries++
				go tab.findnode(n, targetID, reply)
			}
		}
		if pendingQueries == 0 {
			break
		}
		for _, n := range <-reply {
			if n != nil && !seen[n.ID] {
				seen[n.ID] = true
				result.push(n, bucketSize)
			}
		}
		pendingQueries--
	}
	return result.entries
}
func (tab *Table) findnode(n *Node, targetID common.NodeID, reply chan<- []*Node) {
	fails := tab.db.findFails(n.ID)
	r, err := tab.discv.FindNode(n.ID, n.UdpAddr(), targetID)
	if err == errEmptyTable {
		tab.Close()
		return
	}
	if err != nil || len(r) == 0 {
		fails++
		tab.db.updateFindFails(n.ID, fails)
		if fails >= maxFindnodeFailures {
			tab.delete(n)
		}
	} else if fails > 0 {
		tab.db.updateFindFails(n.ID, fails-1)
	}
	for _, n := range r[tab.bootId] {
		tab.add(n)
	}
	reply <- r[tab.bootId]
}
func (tab *Table) refresh() <-chan struct{} {
	done := make(chan struct{})
	select {
	case tab.refreshReq <- done:
	case <-tab.closed:
		close(done)
	}
	return done
}