Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi: Introduce lru module. #1683

Merged
merged 6 commits into from
Mar 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions addrmgr/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/addrmgr

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/chaincfg/chainhash v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions blockchain/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/blockchain

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dchest/siphash v1.2.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions blockchain/stake/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/blockchain/stake

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/chaincfg v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions certgen/go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module github.com/decred/dcrd/certgen

go 1.11
2 changes: 2 additions & 0 deletions chaincfg/chainhash/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/decred/dcrd/chaincfg/chainhash

go 1.11

require github.com/dchest/blake256 v1.0.0
2 changes: 2 additions & 0 deletions chaincfg/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/chaincfg

go 1.11

require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/chaincfg/chainhash v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions connmgr/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/connmgr

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/chaincfg v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions database/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/database

go 1.11

require (
github.com/btcsuite/goleveldb v1.0.0
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions dcrec/edwards/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/decred/dcrd/dcrec/edwards

go 1.11

require github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412
2 changes: 2 additions & 0 deletions dcrec/go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module github.com/decred/dcrd/dcrec

go 1.11
2 changes: 2 additions & 0 deletions dcrec/secp256k1/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/dcrec/secp256k1

go 1.11

require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/chaincfg/chainhash v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions dcrjson/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/decred/dcrd/dcrjson/v2

go 1.11

require github.com/decred/dcrd/chaincfg/chainhash v1.0.1
2 changes: 2 additions & 0 deletions dcrutil/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/dcrutil

go 1.11

require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/base58 v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ The following versioned modules are provided by dcrd repository:
* [fees](https://github.com/decred/dcrd/tree/master/fees) - Provides methods for
tracking and estimating fee rates for new transactions to be mined into the
network
* [lru](https://github.com/decred/dcrd/tree/master/lru) - Implements a generic
concurrent safe least-recently-used cache with near O(1) perf

<a name="ModuleHierarchy" />

Expand Down
2 changes: 2 additions & 0 deletions docs/assets/module_hierarchy.gv
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ digraph {
blockchain [label="blockchain" fillcolor=orchid]
mempool [label="mempool/v2" fillcolor=slategray]
mining [label="mining" fillcolor=chartreuse]
lru [label="lru" fillcolor=royalblue3]
peer [label="peer" fillcolor=khaki]
rpcclient [label="rpcclient/v2" fillcolor=mediumseagreen]
fees [label="fees" fillcolor=darkolivegreen2]
Expand Down Expand Up @@ -48,6 +49,7 @@ digraph {
gcs -> blockchain [dir=back color=gold]
blockchain -> mining [dir=back color=orchid]
mining -> mempool [dir=back color=chartreuse]
lru -> peer [dir=back color=royalblue3]
blockchain -> peer [dir=back color=orchid]
blockchain -> rpcclient [dir=back color=orchid]
dcrjson -> rpcclient [dir=back color=indianred]
Expand Down
136 changes: 73 additions & 63 deletions docs/assets/module_hierarchy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions fees/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/fees

go 1.11

require (
github.com/btcsuite/goleveldb v1.0.0
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions gcs/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/gcs

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dchest/blake256 v1.0.0
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd

go 1.11

require (
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd
github.com/btcsuite/winsvc v1.0.0
Expand All @@ -18,6 +20,7 @@ require (
github.com/decred/dcrd/fees v1.0.0
github.com/decred/dcrd/gcs v1.0.2
github.com/decred/dcrd/hdkeychain v1.1.1
github.com/decred/dcrd/lru v1.0.0
github.com/decred/dcrd/mempool/v2 v2.0.0
github.com/decred/dcrd/mining v1.1.0
github.com/decred/dcrd/peer v1.1.0
Expand Down Expand Up @@ -51,6 +54,7 @@ replace (
github.com/decred/dcrd/gcs => ./gcs
github.com/decred/dcrd/hdkeychain => ./hdkeychain
github.com/decred/dcrd/limits => ./limits
github.com/decred/dcrd/lru => ./lru
github.com/decred/dcrd/mempool/v2 => ./mempool
github.com/decred/dcrd/mining => ./mining
github.com/decred/dcrd/peer => ./peer
Expand Down
2 changes: 2 additions & 0 deletions hdkeychain/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/decred/dcrd/hdkeychain

go 1.11

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/base58 v1.0.0
Expand Down
40 changes: 40 additions & 0 deletions lru/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
lru
===

[![Build Status](http://img.shields.io/travis/decred/dcrd.svg)](https://travis-ci.org/decred/dcrd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/decred/dcrd/lru)

Package lru implements a generic least-recently-used cache with near O(1) perf.

## LRU Cache

A least-recently-used (LRU) cache is a cache that holds a limited number of
items with an eviction policy such that when the capacity of the cache is
exceeded, the least-recently-used item is automatically removed when inserting a
new item. The meaining of used in this implementation is either accessing the
item via a lookup or adding the item into the cache, including when the item
already exists.

## External Use

This package has intentionally been designed so it can be used as a standalone
package for any projects needing to make use of a well-test and conccurrent safe
least-recently-used cache with near O(1) performance characteristics for
lookups, inserts, and deletions.

## Installation and Updating

```bash
$ go get -u github.com/decred/dcrd/lru
```

## Examples

* [Basic Usage](http://godoc.org/github.com/decred/dcrd/lru#example-package--BasicUsage)
Demonstrates creating a new cache instance, inserting items into the cache,
causing an eviction of the least-recently-used item, and removing an item.

## License

Package lru is licensed under the [copyfree](http://copyfree.org) ISC License.
104 changes: 104 additions & 0 deletions lru/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) 2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package lru

import (
"container/list"
"sync"
)

// Cache provides a concurrency safe least-recently-used cache with nearly O(1)
// lookups, inserts, and deletions. The cache is limited to a maximum number of
// items with eviction for the oldest entry when the limit is exceeded.
//
// The NewCache function must be used to create a usable cache since the zero
// value of this struct is not valid.
type Cache struct {
mtx sync.Mutex
cache map[interface{}]*list.Element // nearly O(1) lookups
list *list.List // O(1) insert, update, delete
limit uint
}

// Contains returns whether or not the passed item is a member of the cache.
//
// This function is safe for concurrent access.
func (m *Cache) Contains(item interface{}) bool {
m.mtx.Lock()
node, exists := m.cache[item]
if exists {
m.list.MoveToFront(node)
}
m.mtx.Unlock()

return exists
}

// Add adds the passed item 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 *Cache) Add(item interface{}) {
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.cache[item]; exists {
m.list.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.cache))+1 > m.limit {
node := m.list.Back()
lru := node.Value

// Evict least recently used item.
delete(m.cache, lru)

// Reuse the list node of the item that was just evicted for the new
// item.
node.Value = item
m.list.MoveToFront(node)
m.cache[item] = node
return
}

// The limit hasn't been reached yet, so just add the new item.
node := m.list.PushFront(item)
m.cache[item] = node
}

// Delete deletes the passed item from the cache (if it exists).
//
// This function is safe for concurrent access.
func (m *Cache) Delete(item interface{}) {
m.mtx.Lock()
if node, exists := m.cache[item]; exists {
m.list.Remove(node)
delete(m.cache, item)
}
m.mtx.Unlock()
}

// Cache returns an initialized and empty LRU cache. See the documentation for
// Cache for more details.
func NewCache(limit uint) Cache {
return Cache{
cache: make(map[interface{}]*list.Element),
list: list.New(),
limit: limit,
}
}
Loading