func (c *linkAddrCache) checkLinkRequest(now time.Time, k tcpip.FullAddress, attempt int) bool {
	c.cache.Lock()
	defer c.cache.Unlock()
	entry, ok := c.cache.table[k]
	if !ok {
		return true
	}
	switch s := entry.s; s {
	case ready, failed:
	case incomplete:
		if attempt+1 < c.resolutionAttempts {
			return false
		}
		entry.changeState(failed, now.Add(c.ageLimit))
	default:
		panic(fmt.Sprintf("invalid cache entry state: %s", s))
	}
	return true
}
checkLinkRequest checks whether previous attempt to resolve address has succeeded
and mark the entry accordingly, e.g. ready, failed, etc. Return true if request
can stop, false if another request should be sent.
package stack
import (
	"fmt"
	"sync"
	"time"
	"github.com/google/netstack/sleep"
	"github.com/google/netstack/tcpip"
)
const linkAddrCacheSize = 512  
type linkAddrCache struct {
	ageLimit time.Duration
	resolutionTimeout time.Duration
	resolutionAttempts int
	cache struct {
		sync.Mutex
		table map[tcpip.FullAddress]*linkAddrEntry
		lru   linkAddrEntryList
	}
}
type entryState int
const (
	incomplete entryState = iota
	ready
	failed
)
func (s entryState) String() string {
	switch s {
	case incomplete:
		return "incomplete"
	case ready:
		return "ready"
	case failed:
		return "failed"
	default:
		return fmt.Sprintf("unknown(%d)", s)
	}
}
type linkAddrEntry struct {
	linkAddrEntryEntry
	addr       tcpip.FullAddress
	linkAddr   tcpip.LinkAddress
	expiration time.Time
	s          entryState
	wakers map[*sleep.Waker]struct{}
	done chan struct{}
}
func (e *linkAddrEntry) changeState(ns entryState, expiration time.Time) {
	if e.s == incomplete && ns != incomplete {
		for w := range e.wakers {
			w.Assert()
		}
		e.wakers = nil
		if ch := e.done; ch != nil {
			close(ch)
		}
		e.done = nil
	}
	if expiration.IsZero() || expiration.After(e.expiration) {
		e.expiration = expiration
	}
	e.s = ns
}
func (e *linkAddrEntry) removeWaker(w *sleep.Waker) {
	delete(e.wakers, w)
}
func (c *linkAddrCache) add(k tcpip.FullAddress, v tcpip.LinkAddress) {
	expiration := time.Now().Add(c.ageLimit)
	c.cache.Lock()
	entry := c.getOrCreateEntryLocked(k)
	entry.linkAddr = v
	entry.changeState(ready, expiration)
	c.cache.Unlock()
}
func (c *linkAddrCache) getOrCreateEntryLocked(k tcpip.FullAddress) *linkAddrEntry {
	if entry, ok := c.cache.table[k]; ok {
		c.cache.lru.Remove(entry)
		c.cache.lru.PushFront(entry)
		return entry
	}
	var entry *linkAddrEntry
	if len(c.cache.table) == linkAddrCacheSize {
		entry = c.cache.lru.Back()
		delete(c.cache.table, entry.addr)
		c.cache.lru.Remove(entry)
		entry.changeState(failed, time.Time{})
	} else {
		entry = new(linkAddrEntry)
	}
	*entry = linkAddrEntry{
		addr: k,
		s:    incomplete,
	}
	c.cache.table[k] = entry
	c.cache.lru.PushFront(entry)
	return entry
}
func (c *linkAddrCache) get(k tcpip.FullAddress, linkRes LinkAddressResolver, localAddr tcpip.Address, linkEP LinkEndpoint, waker *sleep.Waker) (tcpip.LinkAddress, <-chan struct{}, *tcpip.Error) {
	if linkRes != nil {
		if addr, ok := linkRes.ResolveStaticAddress(k.Addr); ok {
			return addr, nil, nil
		}
	}
	c.cache.Lock()
	defer c.cache.Unlock()
	entry := c.getOrCreateEntryLocked(k)
	switch s := entry.s; s {
	case ready, failed:
		if !time.Now().After(entry.expiration) {
			switch s {
			case ready:
				return entry.linkAddr, nil, nil
			case failed:
				return entry.linkAddr, nil, tcpip.ErrNoLinkAddress
			default:
				panic(fmt.Sprintf("invalid cache entry state: %s", s))
			}
		}
		entry.changeState(incomplete, time.Time{})
		fallthrough
	case incomplete:
		if waker != nil {
			if entry.wakers == nil {
				entry.wakers = make(map[*sleep.Waker]struct{})
			}
			entry.wakers[waker] = struct{}{}
		}
		if entry.done == nil {
			if linkRes == nil {
				return entry.linkAddr, nil, tcpip.ErrNoLinkAddress
			}
			entry.done = make(chan struct{})
			go c.startAddressResolution(k, linkRes, localAddr, linkEP, entry.done)
		}
		return entry.linkAddr, entry.done, tcpip.ErrWouldBlock
	default:
		panic(fmt.Sprintf("invalid cache entry state: %s", s))
	}
}
func (c *linkAddrCache) removeWaker(k tcpip.FullAddress, waker *sleep.Waker) {
	c.cache.Lock()
	defer c.cache.Unlock()
	if entry, ok := c.cache.table[k]; ok {
		entry.removeWaker(waker)
	}
}
func (c *linkAddrCache) startAddressResolution(k tcpip.FullAddress, linkRes LinkAddressResolver, localAddr tcpip.Address, linkEP LinkEndpoint, done <-chan struct{}) {
	for i := 0; ; i++ {
		linkRes.LinkAddressRequest(k.Addr, localAddr, linkEP)
		select {
		case now := <-time.After(c.resolutionTimeout):
			if stop := c.checkLinkRequest(now, k, i); stop {
				return
			}
		case <-done:
			return
		}
	}
}