diff --git a/peer/lrunoncecache.go b/peer/lrunoncecache.go deleted file mode 100644 index 2749f3c0..00000000 --- a/peer/lrunoncecache.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2016 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package peer - -import ( - "bytes" - "container/list" - "fmt" - "sync" -) - -// lruNonceCache provides a concurrency safe cache that is limited to a maximum -// number of items with eviction for the oldest entry when the limit is -// exceeded. -type lruNonceCache struct { - mtx sync.Mutex - nonceCache map[uint64]*list.Element // nearly O(1) lookups - nonceList *list.List // O(1) insert, update, delete - limit uint -} - -// String returns the cache as a human-readable string. -// -// This function is safe for concurrent access. -func (m *lruNonceCache) String() string { - m.mtx.Lock() - defer m.mtx.Unlock() - - lastEntryNum := len(m.nonceCache) - 1 - curEntry := 0 - buf := bytes.NewBufferString("[") - for nonce := range m.nonceCache { - buf.WriteString(fmt.Sprintf("%d", nonce)) - if curEntry < lastEntryNum { - buf.WriteString(", ") - } - curEntry++ - } - buf.WriteString("]") - - return fmt.Sprintf("<%d>%s", m.limit, buf.String()) -} - -// Exists returns whether or not the passed nonce is in the cache. -// -// This function is safe for concurrent access. -func (m *lruNonceCache) Exists(nonce uint64) bool { - m.mtx.Lock() - _, exists := m.nonceCache[nonce] - m.mtx.Unlock() - - return exists -} - -// Add adds the passed nonce to the cache and handles eviction of the oldest item -// if adding the new item would exceed the max limit. Adding an existing item -// makes it the most recently used item. -// -// This function is safe for concurrent access. -func (m *lruNonceCache) Add(nonce uint64) { - m.mtx.Lock() - defer m.mtx.Unlock() - - // When the limit is zero, nothing can be added to the cache, so just - // return. - if m.limit == 0 { - return - } - - // When the entry already exists move it to the front of the list - // thereby marking it most recently used. - if node, exists := m.nonceCache[nonce]; exists { - m.nonceList.MoveToFront(node) - return - } - - // Evict the least recently used entry (back of the list) if the the new - // entry would exceed the size limit for the cache. Also reuse the list - // node so a new one doesn't have to be allocated. - if uint(len(m.nonceCache))+1 > m.limit { - node := m.nonceList.Back() - lru := node.Value.(uint64) - - // Evict least recently used item. - delete(m.nonceCache, lru) - - // Reuse the list node of the item that was just evicted for the - // new item. - node.Value = nonce - m.nonceList.MoveToFront(node) - m.nonceCache[nonce] = node - return - } - - // The limit hasn't been reached yet, so just add the new item. - node := m.nonceList.PushFront(nonce) - m.nonceCache[nonce] = node -} - -// Delete deletes the passed nonce from the cache (if it exists). -// -// This function is safe for concurrent access. -func (m *lruNonceCache) Delete(nonce uint64) { - m.mtx.Lock() - if node, exists := m.nonceCache[nonce]; exists { - m.nonceList.Remove(node) - delete(m.nonceCache, nonce) - } - m.mtx.Unlock() -} - -// newLruNonceCache returns a new nonce cache that is limited to the number of -// entries specified by limit. When the number of entries exceeds the limit, -// the oldest (least recently used) entry will be removed to make room for the -// new entry. -func newLruNonceCache(limit uint) *lruNonceCache { - m := lruNonceCache{ - nonceCache: make(map[uint64]*list.Element), - nonceList: list.New(), - limit: limit, - } - return &m -} diff --git a/peer/lrunoncecache_test.go b/peer/lrunoncecache_test.go deleted file mode 100644 index e8607d61..00000000 --- a/peer/lrunoncecache_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2016 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package peer - -import ( - "fmt" - "testing" -) - -// TestLruNonceCache ensures the lruNonceCache behaves as expected including -// limiting, eviction of least-recently used entries, specific entry removal, -// and existence tests. -func TestLruNonceCache(t *testing.T) { - // Create a bunch of fake nonces to use in testing the lru nonce code. - numNonces := 10 - nonces := make([]uint64, 0, numNonces) - for i := 0; i < numNonces; i++ { - nonces = append(nonces, uint64(i)) - } - - tests := []struct { - name string - limit int - }{ - {name: "limit 0", limit: 0}, - {name: "limit 1", limit: 1}, - {name: "limit 5", limit: 5}, - {name: "limit 7", limit: 7}, - {name: "limit one less than available", limit: numNonces - 1}, - {name: "limit all available", limit: numNonces}, - } - -testLoop: - for i, test := range tests { - // Create a new lru nonce cache limited by the specified test - // limit and add all of the test nonces. This will cause - // evicition since there are more test nonces than the limits. - lruNonceCache := newLruNonceCache(uint(test.limit)) - for j := 0; j < numNonces; j++ { - lruNonceCache.Add(nonces[j]) - } - - // Ensure the limited number of most recent entries in the list - // exist. - for j := numNonces - test.limit; j < numNonces; j++ { - if !lruNonceCache.Exists(nonces[j]) { - t.Errorf("Exists #%d (%s) entry %d does not "+ - "exist", i, test.name, nonces[j]) - continue testLoop - } - } - - // Ensure the entries before the limited number of most recent - // entries in the list do not exist. - for j := 0; j < numNonces-test.limit; j++ { - if lruNonceCache.Exists(nonces[j]) { - t.Errorf("Exists #%d (%s) entry %d exists", i, - test.name, nonces[j]) - continue testLoop - } - } - - // Readd the entry that should currently be the least-recently - // used entry so it becomes the most-recently used entry, then - // force an eviction by adding an entry that doesn't exist and - // ensure the evicted entry is the new least-recently used - // entry. - // - // This check needs at least 2 entries. - if test.limit > 1 { - origLruIndex := numNonces - test.limit - lruNonceCache.Add(nonces[origLruIndex]) - - lruNonceCache.Add(uint64(numNonces) + 1) - - // Ensure the original lru entry still exists since it - // was updated and should've have become the lru entry. - if !lruNonceCache.Exists(nonces[origLruIndex]) { - t.Errorf("LRU #%d (%s) entry %d does not exist", - i, test.name, nonces[origLruIndex]) - continue testLoop - } - - // Ensure the entry that should've become the new lru - // entry was evicted. - newLruIndex := origLruIndex + 1 - if lruNonceCache.Exists(nonces[newLruIndex]) { - t.Errorf("LRU #%d (%s) entry %d exists", i, - test.name, nonces[newLruIndex]) - continue testLoop - } - } - - // Delete all of the entries in the list, including those that - // don't exist in the cache, and ensure they no longer exist. - for j := 0; j < numNonces; j++ { - lruNonceCache.Delete(nonces[j]) - if lruNonceCache.Exists(nonces[j]) { - t.Errorf("Delete #%d (%s) entry %d exists", i, - test.name, nonces[j]) - continue testLoop - } - } - } -} - -// TestLruNonceCacheStringer tests the stringized output for the lruNonceCache type. -func TestLruNonceCacheStringer(t *testing.T) { - // Create a couple of fake nonces to use in testing the lru nonce - // stringer code. - nonce1 := uint64(10) - nonce2 := uint64(20) - - // Create new lru nonce cache and add the nonces. - lruNonceCache := newLruNonceCache(uint(2)) - lruNonceCache.Add(nonce1) - lruNonceCache.Add(nonce2) - - // Ensure the stringer gives the expected result. Since cache iteration - // is not ordered, either entry could be first, so account for both - // cases. - wantStr1 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce1, nonce2) - wantStr2 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce2, nonce1) - gotStr := lruNonceCache.String() - if gotStr != wantStr1 && gotStr != wantStr2 { - t.Fatalf("unexpected string representation - got %q, want %q "+ - "or %q", gotStr, wantStr1, wantStr2) - } -} - -// BenchmarkLruNonceList performs basic benchmarks on the most recently used -// nonce handling. -func BenchmarkLruNonceList(b *testing.B) { - // Create a bunch of fake nonces to use in benchmarking the lru nonce - // code. - b.StopTimer() - numNonces := 100000 - nonces := make([]uint64, 0, numNonces) - for i := 0; i < numNonces; i++ { - nonces = append(nonces, uint64(i)) - } - b.StartTimer() - - // Benchmark the add plus evicition code. - limit := 20000 - lruNonceCache := newLruNonceCache(uint(limit)) - for i := 0; i < b.N; i++ { - lruNonceCache.Add(nonces[i%numNonces]) - } -} diff --git a/peer/peer.go b/peer/peer.go index 758ab398..0fa8b67d 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -79,7 +79,7 @@ var ( // sentNonces houses the unique nonces that are generated when pushing // version messages that are used to detect self connections. - sentNonces = newLruNonceCache(50) + sentNonces = lru.NewCache(50) // allowSelfConns is only used to allow the tests to bypass the self // connection detecting and disconnect logic since they intentionally @@ -1805,7 +1805,7 @@ func (p *Peer) readRemoteVersionMsg() error { } // Detect self connections. - if !allowSelfConns && sentNonces.Exists(msg.Nonce) { + if !allowSelfConns && sentNonces.Contains(msg.Nonce) { return errors.New("disconnecting peer connected to self") }