mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-10 15:20:56 +00:00
Update goleveldb
This commit is contained in:
parent
b0408ef5c6
commit
435d3958f4
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/calmh/xdr",
|
"ImportPath": "github.com/calmh/xdr",
|
||||||
"Rev": "e1714bbe4764b15490fcc8ebd25d4bd9ea50a4b9"
|
"Rev": "a597b63b87d6140f79084c8aab214b4d533833a1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/juju/ratelimit",
|
"ImportPath": "github.com/juju/ratelimit",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||||
"Rev": "17fd8940e0f778c27793a25bff8c48ddd7bf53ac"
|
"Rev": "59d87758aeaab5ab6ed289c773349500228a1557"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vitrun/qart/coding",
|
"ImportPath": "github.com/vitrun/qart/coding",
|
||||||
|
4
Godeps/_workspace/src/github.com/calmh/xdr/reader_ipdr.go
generated
vendored
4
Godeps/_workspace/src/github.com/calmh/xdr/reader_ipdr.go
generated
vendored
@ -22,7 +22,7 @@ func (r *Reader) ReadUint8() uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
dl.Printf("rd uint8=%d (0x%08x)", r.b[0], r.b[0])
|
dl.Printf("rd uint8=%d (0x%02x)", r.b[0], r.b[0])
|
||||||
}
|
}
|
||||||
return r.b[0]
|
return r.b[0]
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func (r *Reader) ReadUint16() uint16 {
|
|||||||
v := uint16(r.b[1]) | uint16(r.b[0])<<8
|
v := uint16(r.b[1]) | uint16(r.b[0])<<8
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
dl.Printf("rd uint16=%d (0x%08x)", v, v)
|
dl.Printf("rd uint16=%d (0x%04x)", v, v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
4
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/bench_test.go
generated
vendored
4
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/bench_test.go
generated
vendored
@ -249,7 +249,9 @@ func (p *dbBench) newIter() iterator.Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *dbBench) close() {
|
func (p *dbBench) close() {
|
||||||
p.b.Log(p.db.s.tops.bpool)
|
if bp, err := p.db.GetProperty("leveldb.blockpool"); err == nil {
|
||||||
|
p.b.Log("Block pool stats: ", bp)
|
||||||
|
}
|
||||||
p.db.Close()
|
p.db.Close()
|
||||||
p.stor.Close()
|
p.stor.Close()
|
||||||
os.RemoveAll(benchDB)
|
os.RemoveAll(benchDB)
|
||||||
|
134
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache.go
generated
vendored
134
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache.go
generated
vendored
@ -11,84 +11,106 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetFunc used by Namespace.Get method to create a cache object. SetFunc
|
// SetFunc is the function that will be called by Namespace.Get to create
|
||||||
// may return ok false, in that case the cache object will not be created.
|
// a cache object, if charge is less than one than the cache object will
|
||||||
type SetFunc func() (ok bool, value interface{}, charge int, fin SetFin)
|
// not be registered to cache tree, if value is nil then the cache object
|
||||||
|
// will not be created.
|
||||||
|
type SetFunc func() (charge int, value interface{})
|
||||||
|
|
||||||
// SetFin will be called when corresponding cache object are released.
|
// DelFin is the function that will be called as the result of a delete operation.
|
||||||
type SetFin func()
|
// Exist == true is indication that the object is exist, and pending == true is
|
||||||
|
// indication of deletion already happen but haven't done yet (wait for all handles
|
||||||
|
// to be released). And exist == false means the object doesn't exist.
|
||||||
|
type DelFin func(exist, pending bool)
|
||||||
|
|
||||||
// DelFin will be called when corresponding cache object are released.
|
// PurgeFin is the function that will be called as the result of a purge operation.
|
||||||
// DelFin will be called after SetFin. The exist is true if the corresponding
|
type PurgeFin func(ns, key uint64)
|
||||||
// cache object is actually exist in the cache tree.
|
|
||||||
type DelFin func(exist bool)
|
|
||||||
|
|
||||||
// PurgeFin will be called when corresponding cache object are released.
|
|
||||||
// PurgeFin will be called after SetFin. If PurgeFin present DelFin will
|
|
||||||
// not be executed but passed to the PurgeFin, it is up to the caller
|
|
||||||
// to call it or not.
|
|
||||||
type PurgeFin func(ns, key uint64, delfin DelFin)
|
|
||||||
|
|
||||||
// Cache is a cache tree. A cache instance must be goroutine-safe.
|
// Cache is a cache tree. A cache instance must be goroutine-safe.
|
||||||
type Cache interface {
|
type Cache interface {
|
||||||
// SetCapacity sets cache capacity.
|
// SetCapacity sets cache tree capacity.
|
||||||
SetCapacity(capacity int)
|
SetCapacity(capacity int)
|
||||||
|
|
||||||
// GetNamespace gets or creates a cache namespace for the given id.
|
// Capacity returns cache tree capacity.
|
||||||
|
Capacity() int
|
||||||
|
|
||||||
|
// Used returns used cache tree capacity.
|
||||||
|
Used() int
|
||||||
|
|
||||||
|
// Size returns entire alive cache objects size.
|
||||||
|
Size() int
|
||||||
|
|
||||||
|
// GetNamespace gets cache namespace with the given id.
|
||||||
|
// GetNamespace is never return nil.
|
||||||
GetNamespace(id uint64) Namespace
|
GetNamespace(id uint64) Namespace
|
||||||
|
|
||||||
// Purge purges all cache namespaces, read Namespace.Purge method documentation.
|
// Purge purges all cache namespace from this cache tree.
|
||||||
|
// This is behave the same as calling Namespace.Purge method on all cache namespace.
|
||||||
Purge(fin PurgeFin)
|
Purge(fin PurgeFin)
|
||||||
|
|
||||||
// Zap zaps all cache namespaces, read Namespace.Zap method documentation.
|
// Zap detaches all cache namespace from this cache tree.
|
||||||
Zap(closed bool)
|
// This is behave the same as calling Namespace.Zap method on all cache namespace.
|
||||||
|
Zap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
|
// Namespace is a cache namespace. A namespace instance must be goroutine-safe.
|
||||||
type Namespace interface {
|
type Namespace interface {
|
||||||
// Get gets cache object for the given key. The given SetFunc (if not nil) will
|
// Get gets cache object with the given key.
|
||||||
// be called if the given key does not exist.
|
// If cache object is not found and setf is not nil, Get will atomically creates
|
||||||
// If the given key does not exist, SetFunc is nil or SetFunc return ok false, Get
|
// the cache object by calling setf. Otherwise Get will returns nil.
|
||||||
// will return ok false.
|
|
||||||
Get(key uint64, setf SetFunc) (obj Object, ok bool)
|
|
||||||
|
|
||||||
// Get deletes cache object for the given key. If exist the cache object will
|
|
||||||
// be deleted later when all of its handles have been released (i.e. no one use
|
|
||||||
// it anymore) and the given DelFin (if not nil) will finally be executed. If
|
|
||||||
// such cache object does not exist the given DelFin will be executed anyway.
|
|
||||||
//
|
//
|
||||||
// Delete returns true if such cache object exist.
|
// The returned cache handle should be released after use by calling Release
|
||||||
|
// method.
|
||||||
|
Get(key uint64, setf SetFunc) Handle
|
||||||
|
|
||||||
|
// Delete removes cache object with the given key from cache tree.
|
||||||
|
// A deleted cache object will be released as soon as all of its handles have
|
||||||
|
// been released.
|
||||||
|
// Delete only happen once, subsequent delete will consider cache object doesn't
|
||||||
|
// exist, even if the cache object ins't released yet.
|
||||||
|
//
|
||||||
|
// If not nil, fin will be called if the cache object doesn't exist or when
|
||||||
|
// finally be released.
|
||||||
|
//
|
||||||
|
// Delete returns true if such cache object exist and never been deleted.
|
||||||
Delete(key uint64, fin DelFin) bool
|
Delete(key uint64, fin DelFin) bool
|
||||||
|
|
||||||
// Purge deletes all cache objects, read Delete method documentation.
|
// Purge removes all cache objects within this namespace from cache tree.
|
||||||
|
// This is the same as doing delete on all cache objects.
|
||||||
|
//
|
||||||
|
// If not nil, fin will be called on all cache objects when its finally be
|
||||||
|
// released.
|
||||||
Purge(fin PurgeFin)
|
Purge(fin PurgeFin)
|
||||||
|
|
||||||
// Zap detaches the namespace from the cache tree and delete all its cache
|
// Zap detaches namespace from cache tree and release all its cache objects.
|
||||||
// objects. The cache objects deletion and finalizers execution are happen
|
// A zapped namespace can never be filled again.
|
||||||
// immediately, even if its existing handles haven't yet been released.
|
// Calling Get on zapped namespace will always return nil.
|
||||||
// A zapped namespace can't never be filled again.
|
Zap()
|
||||||
// If closed is false then the Get function will always call the given SetFunc
|
|
||||||
// if it is not nil, but resultant of the SetFunc will not be cached.
|
|
||||||
Zap(closed bool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object is a cache object.
|
// Handle is a cache handle.
|
||||||
type Object interface {
|
type Handle interface {
|
||||||
// Release releases the cache object. Other methods should not be called
|
// Release releases this cache handle. This method can be safely called mutiple
|
||||||
// after the cache object has been released.
|
// times.
|
||||||
Release()
|
Release()
|
||||||
|
|
||||||
// Value returns value of the cache object.
|
// Value returns value of this cache handle.
|
||||||
|
// Value will returns nil after this cache handle have be released.
|
||||||
Value() interface{}
|
Value() interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DelNotExist = iota
|
||||||
|
DelExist
|
||||||
|
DelPendig
|
||||||
|
)
|
||||||
|
|
||||||
// Namespace state.
|
// Namespace state.
|
||||||
type nsState int
|
type nsState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nsEffective nsState = iota
|
nsEffective nsState = iota
|
||||||
nsZapped
|
nsZapped
|
||||||
nsClosed
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node state.
|
// Node state.
|
||||||
@ -97,29 +119,29 @@ type nodeState int
|
|||||||
const (
|
const (
|
||||||
nodeEffective nodeState = iota
|
nodeEffective nodeState = iota
|
||||||
nodeEvicted
|
nodeEvicted
|
||||||
nodeRemoved
|
nodeDeleted
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake object.
|
// Fake handle.
|
||||||
type fakeObject struct {
|
type fakeHandle struct {
|
||||||
value interface{}
|
value interface{}
|
||||||
fin func()
|
fin func()
|
||||||
once uint32
|
once uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *fakeObject) Value() interface{} {
|
func (h *fakeHandle) Value() interface{} {
|
||||||
if atomic.LoadUint32(&o.once) == 0 {
|
if atomic.LoadUint32(&h.once) == 0 {
|
||||||
return o.value
|
return h.value
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *fakeObject) Release() {
|
func (h *fakeHandle) Release() {
|
||||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if o.fin != nil {
|
if h.fin != nil {
|
||||||
o.fin()
|
h.fin()
|
||||||
o.fin = nil
|
h.fin = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
439
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache_test.go
generated
vendored
439
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/cache_test.go
generated
vendored
@ -7,15 +7,35 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func set(ns Namespace, key uint64, value interface{}, charge int, fin func()) Object {
|
type releaserFunc struct {
|
||||||
obj, _ := ns.Get(key, func() (bool, interface{}, int, SetFin) {
|
fn func()
|
||||||
return true, value, charge, fin
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r releaserFunc) Release() {
|
||||||
|
if r.fn != nil {
|
||||||
|
r.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(ns Namespace, key uint64, value interface{}, charge int, relf func()) Handle {
|
||||||
|
return ns.Get(key, func() (int, interface{}) {
|
||||||
|
if relf != nil {
|
||||||
|
return charge, releaserFunc{relf, value}
|
||||||
|
} else {
|
||||||
|
return charge, value
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCache_HitMiss(t *testing.T) {
|
func TestCache_HitMiss(t *testing.T) {
|
||||||
@ -43,29 +63,31 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
setfin++
|
setfin++
|
||||||
}).Release()
|
}).Release()
|
||||||
for j, y := range cases {
|
for j, y := range cases {
|
||||||
r, ok := ns.Get(y.key, nil)
|
h := ns.Get(y.key, nil)
|
||||||
if j <= i {
|
if j <= i {
|
||||||
// should hit
|
// should hit
|
||||||
if !ok {
|
if h == nil {
|
||||||
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
||||||
} else if r.Value().(string) != y.value {
|
} else {
|
||||||
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, r.Value().(string), y.value)
|
if x := h.Value().(releaserFunc).value.(string); x != y.value {
|
||||||
|
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, x, y.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// should miss
|
// should miss
|
||||||
if ok {
|
if h != nil {
|
||||||
t.Errorf("case '%d' iteration '%d' is hit , value '%s'", i, j, r.Value().(string))
|
t.Errorf("case '%d' iteration '%d' is hit , value '%s'", i, j, h.Value().(releaserFunc).value.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ok {
|
if h != nil {
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, x := range cases {
|
for i, x := range cases {
|
||||||
finalizerOk := false
|
finalizerOk := false
|
||||||
ns.Delete(x.key, func(exist bool) {
|
ns.Delete(x.key, func(exist, pending bool) {
|
||||||
finalizerOk = true
|
finalizerOk = true
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -74,22 +96,24 @@ func TestCache_HitMiss(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for j, y := range cases {
|
for j, y := range cases {
|
||||||
r, ok := ns.Get(y.key, nil)
|
h := ns.Get(y.key, nil)
|
||||||
if j > i {
|
if j > i {
|
||||||
// should hit
|
// should hit
|
||||||
if !ok {
|
if h == nil {
|
||||||
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
t.Errorf("case '%d' iteration '%d' is miss", i, j)
|
||||||
} else if r.Value().(string) != y.value {
|
} else {
|
||||||
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, r.Value().(string), y.value)
|
if x := h.Value().(releaserFunc).value.(string); x != y.value {
|
||||||
|
t.Errorf("case '%d' iteration '%d' has invalid value got '%s', want '%s'", i, j, x, y.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// should miss
|
// should miss
|
||||||
if ok {
|
if h != nil {
|
||||||
t.Errorf("case '%d' iteration '%d' is hit, value '%s'", i, j, r.Value().(string))
|
t.Errorf("case '%d' iteration '%d' is hit, value '%s'", i, j, h.Value().(releaserFunc).value.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ok {
|
if h != nil {
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,42 +131,42 @@ func TestLRUCache_Eviction(t *testing.T) {
|
|||||||
set(ns, 3, 3, 1, nil).Release()
|
set(ns, 3, 3, 1, nil).Release()
|
||||||
set(ns, 4, 4, 1, nil).Release()
|
set(ns, 4, 4, 1, nil).Release()
|
||||||
set(ns, 5, 5, 1, nil).Release()
|
set(ns, 5, 5, 1, nil).Release()
|
||||||
if r, ok := ns.Get(2, nil); ok { // 1,3,4,5,2
|
if h := ns.Get(2, nil); h != nil { // 1,3,4,5,2
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
set(ns, 9, 9, 10, nil).Release() // 5,2,9
|
set(ns, 9, 9, 10, nil).Release() // 5,2,9
|
||||||
|
|
||||||
for _, x := range []uint64{9, 2, 5, 1} {
|
for _, key := range []uint64{9, 2, 5, 1} {
|
||||||
r, ok := ns.Get(x, nil)
|
h := ns.Get(key, nil)
|
||||||
if !ok {
|
if h == nil {
|
||||||
t.Errorf("miss for key '%d'", x)
|
t.Errorf("miss for key '%d'", key)
|
||||||
} else {
|
} else {
|
||||||
if r.Value().(int) != int(x) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
||||||
}
|
}
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o1.Release()
|
o1.Release()
|
||||||
for _, x := range []uint64{1, 2, 5} {
|
for _, key := range []uint64{1, 2, 5} {
|
||||||
r, ok := ns.Get(x, nil)
|
h := ns.Get(key, nil)
|
||||||
if !ok {
|
if h == nil {
|
||||||
t.Errorf("miss for key '%d'", x)
|
t.Errorf("miss for key '%d'", key)
|
||||||
} else {
|
} else {
|
||||||
if r.Value().(int) != int(x) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
||||||
}
|
}
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, x := range []uint64{3, 4, 9} {
|
for _, key := range []uint64{3, 4, 9} {
|
||||||
r, ok := ns.Get(x, nil)
|
h := ns.Get(key, nil)
|
||||||
if ok {
|
if h != nil {
|
||||||
t.Errorf("hit for key '%d'", x)
|
t.Errorf("hit for key '%d'", key)
|
||||||
if r.Value().(int) != int(x) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
||||||
}
|
}
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,16 +177,15 @@ func TestLRUCache_SetGet(t *testing.T) {
|
|||||||
for i := 0; i < 200; i++ {
|
for i := 0; i < 200; i++ {
|
||||||
n := uint64(rand.Intn(99999) % 20)
|
n := uint64(rand.Intn(99999) % 20)
|
||||||
set(ns, n, n, 1, nil).Release()
|
set(ns, n, n, 1, nil).Release()
|
||||||
if p, ok := ns.Get(n, nil); ok {
|
if h := ns.Get(n, nil); h != nil {
|
||||||
if p.Value() == nil {
|
if h.Value() == nil {
|
||||||
t.Errorf("key '%d' contains nil value", n)
|
t.Errorf("key '%d' contains nil value", n)
|
||||||
} else {
|
} else {
|
||||||
got := p.Value().(uint64)
|
if x := h.Value().(uint64); x != n {
|
||||||
if got != n {
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, x)
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", n, n, got)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Release()
|
h.Release()
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("key '%d' doesn't exist", n)
|
t.Errorf("key '%d' doesn't exist", n)
|
||||||
}
|
}
|
||||||
@ -176,31 +199,319 @@ func TestLRUCache_Purge(t *testing.T) {
|
|||||||
o2 := set(ns1, 2, 2, 1, nil)
|
o2 := set(ns1, 2, 2, 1, nil)
|
||||||
ns1.Purge(nil)
|
ns1.Purge(nil)
|
||||||
set(ns1, 3, 3, 1, nil).Release()
|
set(ns1, 3, 3, 1, nil).Release()
|
||||||
for _, x := range []uint64{1, 2, 3} {
|
for _, key := range []uint64{1, 2, 3} {
|
||||||
r, ok := ns1.Get(x, nil)
|
h := ns1.Get(key, nil)
|
||||||
if !ok {
|
if h == nil {
|
||||||
t.Errorf("miss for key '%d'", x)
|
t.Errorf("miss for key '%d'", key)
|
||||||
} else {
|
} else {
|
||||||
if r.Value().(int) != int(x) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
||||||
}
|
}
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o1.Release()
|
o1.Release()
|
||||||
o2.Release()
|
o2.Release()
|
||||||
for _, x := range []uint64{1, 2} {
|
for _, key := range []uint64{1, 2} {
|
||||||
r, ok := ns1.Get(x, nil)
|
h := ns1.Get(key, nil)
|
||||||
if ok {
|
if h != nil {
|
||||||
t.Errorf("hit for key '%d'", x)
|
t.Errorf("hit for key '%d'", key)
|
||||||
if r.Value().(int) != int(x) {
|
if x := h.Value().(int); x != int(key) {
|
||||||
t.Errorf("invalid value for key '%d' want '%d', got '%d'", x, x, r.Value().(int))
|
t.Errorf("invalid value for key '%d' want '%d', got '%d'", key, key, x)
|
||||||
}
|
}
|
||||||
r.Release()
|
h.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testingCacheObjectCounter struct {
|
||||||
|
created uint32
|
||||||
|
released uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testingCacheObjectCounter) createOne() {
|
||||||
|
atomic.AddUint32(&c.created, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testingCacheObjectCounter) releaseOne() {
|
||||||
|
atomic.AddUint32(&c.released, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testingCacheObject struct {
|
||||||
|
t *testing.T
|
||||||
|
cnt *testingCacheObjectCounter
|
||||||
|
|
||||||
|
ns, key uint64
|
||||||
|
|
||||||
|
releaseCalled uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *testingCacheObject) Release() {
|
||||||
|
if atomic.CompareAndSwapUint32(&x.releaseCalled, 0, 1) {
|
||||||
|
x.cnt.releaseOne()
|
||||||
|
} else {
|
||||||
|
x.t.Errorf("duplicate setfin NS#%d KEY#%s", x.ns, x.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLRUCache_Finalizer(t *testing.T) {
|
||||||
|
const (
|
||||||
|
capacity = 100
|
||||||
|
goroutines = 100
|
||||||
|
iterations = 10000
|
||||||
|
keymax = 8000
|
||||||
|
)
|
||||||
|
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
defer runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
cnt := &testingCacheObjectCounter{}
|
||||||
|
|
||||||
|
c := NewLRUCache(capacity)
|
||||||
|
|
||||||
|
type instance struct {
|
||||||
|
seed int64
|
||||||
|
rnd *rand.Rand
|
||||||
|
ns uint64
|
||||||
|
effective int32
|
||||||
|
handles []Handle
|
||||||
|
handlesMap map[uint64]int
|
||||||
|
|
||||||
|
delete bool
|
||||||
|
purge bool
|
||||||
|
zap bool
|
||||||
|
wantDel int32
|
||||||
|
delfinCalledAll int32
|
||||||
|
delfinCalledEff int32
|
||||||
|
purgefinCalled int32
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceGet := func(p *instance, ns Namespace, key uint64) {
|
||||||
|
h := ns.Get(key, func() (charge int, value interface{}) {
|
||||||
|
to := &testingCacheObject{
|
||||||
|
t: t, cnt: cnt,
|
||||||
|
ns: p.ns,
|
||||||
|
key: key,
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&p.effective, 1)
|
||||||
|
cnt.createOne()
|
||||||
|
return 1, releaserFunc{func() {
|
||||||
|
to.Release()
|
||||||
|
atomic.AddInt32(&p.effective, -1)
|
||||||
|
}, to}
|
||||||
|
})
|
||||||
|
p.handles = append(p.handles, h)
|
||||||
|
p.handlesMap[key] = p.handlesMap[key] + 1
|
||||||
|
}
|
||||||
|
instanceRelease := func(p *instance, ns Namespace, i int) {
|
||||||
|
h := p.handles[i]
|
||||||
|
key := h.Value().(releaserFunc).value.(*testingCacheObject).key
|
||||||
|
if n := p.handlesMap[key]; n == 0 {
|
||||||
|
t.Fatal("key ref == 0")
|
||||||
|
} else if n > 1 {
|
||||||
|
p.handlesMap[key] = n - 1
|
||||||
|
} else {
|
||||||
|
delete(p.handlesMap, key)
|
||||||
|
}
|
||||||
|
h.Release()
|
||||||
|
p.handles = append(p.handles[:i], p.handles[i+1:]...)
|
||||||
|
p.handles[len(p.handles) : len(p.handles)+1][0] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
seeds := make([]int64, goroutines)
|
||||||
|
instances := make([]instance, goroutines)
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
p.handlesMap = make(map[uint64]int)
|
||||||
|
if seeds[i] == 0 {
|
||||||
|
seeds[i] = time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
p.seed = seeds[i]
|
||||||
|
p.rnd = rand.New(rand.NewSource(p.seed))
|
||||||
|
p.ns = uint64(i)
|
||||||
|
p.delete = i%6 == 0
|
||||||
|
p.purge = i%8 == 0
|
||||||
|
p.zap = i%12 == 0 || i%3 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
seedsStr := make([]string, len(seeds))
|
||||||
|
for i, seed := range seeds {
|
||||||
|
seedsStr[i] = fmt.Sprint(seed)
|
||||||
|
}
|
||||||
|
t.Logf("seeds := []int64{%s}", strings.Join(seedsStr, ", "))
|
||||||
|
|
||||||
|
// Get and release.
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p *instance) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ns := c.GetNamespace(p.ns)
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
if len(p.handles) == 0 || p.rnd.Int()%2 == 0 {
|
||||||
|
instanceGet(p, ns, uint64(p.rnd.Intn(keymax)))
|
||||||
|
} else {
|
||||||
|
instanceRelease(p, ns, p.rnd.Intn(len(p.handles)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(p)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if used, cap := c.Used(), c.Capacity(); used > cap {
|
||||||
|
t.Errorf("Used > capacity, used=%d cap=%d", used, cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check effective objects.
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
if int(p.effective) < len(p.handlesMap) {
|
||||||
|
t.Errorf("#%d effective objects < acquired handle, eo=%d ah=%d", i, p.effective, len(p.handlesMap))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if want := int(cnt.created - cnt.released); c.Size() != want {
|
||||||
|
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete and purge.
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
p.wantDel = p.effective
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p *instance) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ns := c.GetNamespace(p.ns)
|
||||||
|
|
||||||
|
if p.delete {
|
||||||
|
for key := uint64(0); key < keymax; key++ {
|
||||||
|
_, wantExist := p.handlesMap[key]
|
||||||
|
gotExist := ns.Delete(key, func(exist, pending bool) {
|
||||||
|
atomic.AddInt32(&p.delfinCalledAll, 1)
|
||||||
|
if exist {
|
||||||
|
atomic.AddInt32(&p.delfinCalledEff, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if !gotExist && wantExist {
|
||||||
|
t.Errorf("delete on NS#%d KEY#%d not found", p.ns, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var delfinCalled int
|
||||||
|
for key := uint64(0); key < keymax; key++ {
|
||||||
|
func(key uint64) {
|
||||||
|
gotExist := ns.Delete(key, func(exist, pending bool) {
|
||||||
|
if exist && !pending {
|
||||||
|
t.Errorf("delete fin on NS#%d KEY#%d exist and not pending for deletion", p.ns, key)
|
||||||
|
}
|
||||||
|
delfinCalled++
|
||||||
|
})
|
||||||
|
if gotExist {
|
||||||
|
t.Errorf("delete on NS#%d KEY#%d found", p.ns, key)
|
||||||
|
}
|
||||||
|
}(key)
|
||||||
|
}
|
||||||
|
if delfinCalled != keymax {
|
||||||
|
t.Errorf("(2) #%d not all delete fin called, diff=%d", p.ns, keymax-delfinCalled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.purge {
|
||||||
|
ns.Purge(func(ns, key uint64) {
|
||||||
|
atomic.AddInt32(&p.purgefinCalled, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}(p)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if want := int(cnt.created - cnt.released); c.Size() != want {
|
||||||
|
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release.
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
|
||||||
|
if !p.zap {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p *instance) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ns := c.GetNamespace(p.ns)
|
||||||
|
for i := len(p.handles) - 1; i >= 0; i-- {
|
||||||
|
instanceRelease(p, ns, i)
|
||||||
|
}
|
||||||
|
}(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if want := int(cnt.created - cnt.released); c.Size() != want {
|
||||||
|
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zap.
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
|
||||||
|
if p.zap {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(p *instance) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
ns := c.GetNamespace(p.ns)
|
||||||
|
ns.Zap()
|
||||||
|
|
||||||
|
p.handles = nil
|
||||||
|
p.handlesMap = nil
|
||||||
|
}(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if want := int(cnt.created - cnt.released); c.Size() != want {
|
||||||
|
t.Errorf("Invalid cache size, want=%d got=%d", want, c.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
if notrel, used := int(cnt.created-cnt.released), c.Used(); notrel != used {
|
||||||
|
t.Errorf("Invalid used value, want=%d got=%d", notrel, used)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Purge(nil)
|
||||||
|
|
||||||
|
for i := range instances {
|
||||||
|
p := &instances[i]
|
||||||
|
|
||||||
|
if p.delete {
|
||||||
|
if p.delfinCalledAll != keymax {
|
||||||
|
t.Errorf("#%d not all delete fin called, purge=%v zap=%v diff=%d", p.ns, p.purge, p.zap, keymax-p.delfinCalledAll)
|
||||||
|
}
|
||||||
|
if p.delfinCalledEff != p.wantDel {
|
||||||
|
t.Errorf("#%d not all effective delete fin called, diff=%d", p.ns, p.wantDel-p.delfinCalledEff)
|
||||||
|
}
|
||||||
|
if p.purge && p.purgefinCalled > 0 {
|
||||||
|
t.Errorf("#%d some purge fin called, delete=%v zap=%v n=%d", p.ns, p.delete, p.zap, p.purgefinCalled)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if p.purge {
|
||||||
|
if p.purgefinCalled != p.wantDel {
|
||||||
|
t.Errorf("#%d not all purge fin called, delete=%v zap=%v diff=%d", p.ns, p.delete, p.zap, p.wantDel-p.purgefinCalled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnt.created != cnt.released {
|
||||||
|
t.Errorf("Some cache object weren't released, created=%d released=%d", cnt.created, cnt.released)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkLRUCache_SetRelease(b *testing.B) {
|
func BenchmarkLRUCache_SetRelease(b *testing.B) {
|
||||||
capacity := b.N / 100
|
capacity := b.N / 100
|
||||||
if capacity <= 0 {
|
if capacity <= 0 {
|
||||||
|
246
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/empty_cache.go
generated
vendored
246
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/empty_cache.go
generated
vendored
@ -1,246 +0,0 @@
|
|||||||
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
package cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type emptyCache struct {
|
|
||||||
sync.Mutex
|
|
||||||
table map[uint64]*emptyNS
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEmptyCache creates a new initialized empty cache.
|
|
||||||
func NewEmptyCache() Cache {
|
|
||||||
return &emptyCache{
|
|
||||||
table: make(map[uint64]*emptyNS),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *emptyCache) GetNamespace(id uint64) Namespace {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
if ns, ok := c.table[id]; ok {
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
ns := &emptyNS{
|
|
||||||
cache: c,
|
|
||||||
id: id,
|
|
||||||
table: make(map[uint64]*emptyNode),
|
|
||||||
}
|
|
||||||
c.table[id] = ns
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *emptyCache) Purge(fin PurgeFin) {
|
|
||||||
c.Lock()
|
|
||||||
for _, ns := range c.table {
|
|
||||||
ns.purgeNB(fin)
|
|
||||||
}
|
|
||||||
c.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *emptyCache) Zap(closed bool) {
|
|
||||||
c.Lock()
|
|
||||||
for _, ns := range c.table {
|
|
||||||
ns.zapNB(closed)
|
|
||||||
}
|
|
||||||
c.table = make(map[uint64]*emptyNS)
|
|
||||||
c.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*emptyCache) SetCapacity(capacity int) {}
|
|
||||||
|
|
||||||
type emptyNS struct {
|
|
||||||
cache *emptyCache
|
|
||||||
id uint64
|
|
||||||
table map[uint64]*emptyNode
|
|
||||||
state nsState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) Get(key uint64, setf SetFunc) (o Object, ok bool) {
|
|
||||||
ns.cache.Lock()
|
|
||||||
|
|
||||||
switch ns.state {
|
|
||||||
case nsZapped:
|
|
||||||
ns.cache.Unlock()
|
|
||||||
if setf == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var value interface{}
|
|
||||||
var fin func()
|
|
||||||
ok, value, _, fin = setf()
|
|
||||||
if ok {
|
|
||||||
o = &fakeObject{
|
|
||||||
value: value,
|
|
||||||
fin: fin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case nsClosed:
|
|
||||||
ns.cache.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, ok := ns.table[key]
|
|
||||||
if ok {
|
|
||||||
n.ref++
|
|
||||||
} else {
|
|
||||||
if setf == nil {
|
|
||||||
ns.cache.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var value interface{}
|
|
||||||
var fin func()
|
|
||||||
ok, value, _, fin = setf()
|
|
||||||
if !ok {
|
|
||||||
ns.cache.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n = &emptyNode{
|
|
||||||
ns: ns,
|
|
||||||
key: key,
|
|
||||||
value: value,
|
|
||||||
setfin: fin,
|
|
||||||
ref: 1,
|
|
||||||
}
|
|
||||||
ns.table[key] = n
|
|
||||||
}
|
|
||||||
|
|
||||||
ns.cache.Unlock()
|
|
||||||
o = &emptyObject{node: n}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) Delete(key uint64, fin DelFin) bool {
|
|
||||||
ns.cache.Lock()
|
|
||||||
|
|
||||||
if ns.state != nsEffective {
|
|
||||||
ns.cache.Unlock()
|
|
||||||
if fin != nil {
|
|
||||||
fin(false)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
n, ok := ns.table[key]
|
|
||||||
if !ok {
|
|
||||||
ns.cache.Unlock()
|
|
||||||
if fin != nil {
|
|
||||||
fin(false)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
n.delfin = fin
|
|
||||||
ns.cache.Unlock()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) purgeNB(fin PurgeFin) {
|
|
||||||
if ns.state != nsEffective {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, n := range ns.table {
|
|
||||||
n.purgefin = fin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) Purge(fin PurgeFin) {
|
|
||||||
ns.cache.Lock()
|
|
||||||
ns.purgeNB(fin)
|
|
||||||
ns.cache.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) zapNB(closed bool) {
|
|
||||||
if ns.state != nsEffective {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, n := range ns.table {
|
|
||||||
n.execFin()
|
|
||||||
}
|
|
||||||
if closed {
|
|
||||||
ns.state = nsClosed
|
|
||||||
} else {
|
|
||||||
ns.state = nsZapped
|
|
||||||
}
|
|
||||||
ns.table = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *emptyNS) Zap(closed bool) {
|
|
||||||
ns.cache.Lock()
|
|
||||||
ns.zapNB(closed)
|
|
||||||
delete(ns.cache.table, ns.id)
|
|
||||||
ns.cache.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyNode struct {
|
|
||||||
ns *emptyNS
|
|
||||||
key uint64
|
|
||||||
value interface{}
|
|
||||||
ref int
|
|
||||||
setfin SetFin
|
|
||||||
delfin DelFin
|
|
||||||
purgefin PurgeFin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *emptyNode) execFin() {
|
|
||||||
if n.setfin != nil {
|
|
||||||
n.setfin()
|
|
||||||
n.setfin = nil
|
|
||||||
}
|
|
||||||
if n.purgefin != nil {
|
|
||||||
n.purgefin(n.ns.id, n.key, n.delfin)
|
|
||||||
n.delfin = nil
|
|
||||||
n.purgefin = nil
|
|
||||||
} else if n.delfin != nil {
|
|
||||||
n.delfin(true)
|
|
||||||
n.delfin = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *emptyNode) evict() {
|
|
||||||
n.ns.cache.Lock()
|
|
||||||
n.ref--
|
|
||||||
if n.ref == 0 {
|
|
||||||
if n.ns.state == nsEffective {
|
|
||||||
// Remove elem.
|
|
||||||
delete(n.ns.table, n.key)
|
|
||||||
// Execute finalizer.
|
|
||||||
n.execFin()
|
|
||||||
}
|
|
||||||
} else if n.ref < 0 {
|
|
||||||
panic("leveldb/cache: emptyNode: negative node reference")
|
|
||||||
}
|
|
||||||
n.ns.cache.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type emptyObject struct {
|
|
||||||
node *emptyNode
|
|
||||||
once uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *emptyObject) Value() interface{} {
|
|
||||||
if atomic.LoadUint32(&o.once) == 0 {
|
|
||||||
return o.node.value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *emptyObject) Release() {
|
|
||||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
o.node.evict()
|
|
||||||
o.node = nil
|
|
||||||
}
|
|
284
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru_cache.go
generated
vendored
284
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/cache/lru_cache.go
generated
vendored
@ -9,16 +9,17 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// lruCache represent a LRU cache state.
|
// lruCache represent a LRU cache state.
|
||||||
type lruCache struct {
|
type lruCache struct {
|
||||||
sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
recent lruNode
|
recent lruNode
|
||||||
table map[uint64]*lruNs
|
table map[uint64]*lruNs
|
||||||
capacity int
|
capacity int
|
||||||
size int
|
used, size int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLRUCache creates a new initialized LRU cache with the given capacity.
|
// NewLRUCache creates a new initialized LRU cache with the given capacity.
|
||||||
@ -32,57 +33,75 @@ func NewLRUCache(capacity int) Cache {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *lruCache) Capacity() int {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *lruCache) Used() int {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.used
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *lruCache) Size() int {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.size
|
||||||
|
}
|
||||||
|
|
||||||
// SetCapacity set cache capacity.
|
// SetCapacity set cache capacity.
|
||||||
func (c *lruCache) SetCapacity(capacity int) {
|
func (c *lruCache) SetCapacity(capacity int) {
|
||||||
c.Lock()
|
c.mu.Lock()
|
||||||
c.capacity = capacity
|
c.capacity = capacity
|
||||||
c.evict()
|
c.evict()
|
||||||
c.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespace return namespace object for given id.
|
// GetNamespace return namespace object for given id.
|
||||||
func (c *lruCache) GetNamespace(id uint64) Namespace {
|
func (c *lruCache) GetNamespace(id uint64) Namespace {
|
||||||
c.Lock()
|
c.mu.Lock()
|
||||||
defer c.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
if p, ok := c.table[id]; ok {
|
if ns, ok := c.table[id]; ok {
|
||||||
return p
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
p := &lruNs{
|
ns := &lruNs{
|
||||||
lru: c,
|
lru: c,
|
||||||
id: id,
|
id: id,
|
||||||
table: make(map[uint64]*lruNode),
|
table: make(map[uint64]*lruNode),
|
||||||
}
|
}
|
||||||
c.table[id] = p
|
c.table[id] = ns
|
||||||
return p
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge purge entire cache.
|
// Purge purge entire cache.
|
||||||
func (c *lruCache) Purge(fin PurgeFin) {
|
func (c *lruCache) Purge(fin PurgeFin) {
|
||||||
c.Lock()
|
c.mu.Lock()
|
||||||
for _, ns := range c.table {
|
for _, ns := range c.table {
|
||||||
ns.purgeNB(fin)
|
ns.purgeNB(fin)
|
||||||
}
|
}
|
||||||
c.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *lruCache) Zap(closed bool) {
|
func (c *lruCache) Zap() {
|
||||||
c.Lock()
|
c.mu.Lock()
|
||||||
for _, ns := range c.table {
|
for _, ns := range c.table {
|
||||||
ns.zapNB(closed)
|
ns.zapNB()
|
||||||
}
|
}
|
||||||
c.table = make(map[uint64]*lruNs)
|
c.table = make(map[uint64]*lruNs)
|
||||||
c.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *lruCache) evict() {
|
func (c *lruCache) evict() {
|
||||||
top := &c.recent
|
top := &c.recent
|
||||||
for n := c.recent.rPrev; c.size > c.capacity && n != top; {
|
for n := c.recent.rPrev; c.used > c.capacity && n != top; {
|
||||||
n.state = nodeEvicted
|
n.state = nodeEvicted
|
||||||
n.rRemove()
|
n.rRemove()
|
||||||
n.evictNB()
|
n.derefNB()
|
||||||
c.size -= n.charge
|
c.used -= n.charge
|
||||||
n = c.recent.rPrev
|
n = c.recent.rPrev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,170 +113,157 @@ type lruNs struct {
|
|||||||
state nsState
|
state nsState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) Get(key uint64, setf SetFunc) (o Object, ok bool) {
|
func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
|
||||||
lru := ns.lru
|
ns.lru.mu.Lock()
|
||||||
lru.Lock()
|
|
||||||
|
|
||||||
switch ns.state {
|
if ns.state != nsEffective {
|
||||||
case nsZapped:
|
ns.lru.mu.Unlock()
|
||||||
lru.Unlock()
|
return nil
|
||||||
if setf == nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var value interface{}
|
node, ok := ns.table[key]
|
||||||
var fin func()
|
|
||||||
ok, value, _, fin = setf()
|
|
||||||
if ok {
|
if ok {
|
||||||
o = &fakeObject{
|
switch node.state {
|
||||||
value: value,
|
|
||||||
fin: fin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case nsClosed:
|
|
||||||
lru.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n, ok := ns.table[key]
|
|
||||||
if ok {
|
|
||||||
switch n.state {
|
|
||||||
case nodeEvicted:
|
case nodeEvicted:
|
||||||
// Insert to recent list.
|
// Insert to recent list.
|
||||||
n.state = nodeEffective
|
node.state = nodeEffective
|
||||||
n.ref++
|
node.ref++
|
||||||
lru.size += n.charge
|
ns.lru.used += node.charge
|
||||||
lru.evict()
|
ns.lru.evict()
|
||||||
fallthrough
|
fallthrough
|
||||||
case nodeEffective:
|
case nodeEffective:
|
||||||
// Bump to front
|
// Bump to front.
|
||||||
n.rRemove()
|
node.rRemove()
|
||||||
n.rInsert(&lru.recent)
|
node.rInsert(&ns.lru.recent)
|
||||||
}
|
}
|
||||||
n.ref++
|
node.ref++
|
||||||
} else {
|
} else {
|
||||||
if setf == nil {
|
if setf == nil {
|
||||||
lru.Unlock()
|
ns.lru.mu.Unlock()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var value interface{}
|
charge, value := setf()
|
||||||
var charge int
|
if value == nil {
|
||||||
var fin func()
|
ns.lru.mu.Unlock()
|
||||||
ok, value, charge, fin = setf()
|
return nil
|
||||||
if !ok {
|
|
||||||
lru.Unlock()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
n = &lruNode{
|
node = &lruNode{
|
||||||
ns: ns,
|
ns: ns,
|
||||||
key: key,
|
key: key,
|
||||||
value: value,
|
value: value,
|
||||||
charge: charge,
|
charge: charge,
|
||||||
setfin: fin,
|
ref: 1,
|
||||||
ref: 2,
|
|
||||||
}
|
}
|
||||||
ns.table[key] = n
|
ns.table[key] = node
|
||||||
n.rInsert(&lru.recent)
|
|
||||||
|
|
||||||
lru.size += charge
|
if charge > 0 {
|
||||||
lru.evict()
|
node.ref++
|
||||||
|
node.rInsert(&ns.lru.recent)
|
||||||
|
ns.lru.used += charge
|
||||||
|
ns.lru.size += charge
|
||||||
|
ns.lru.evict()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lru.Unlock()
|
ns.lru.mu.Unlock()
|
||||||
o = &lruObject{node: n}
|
return &lruHandle{node: node}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
|
func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
|
||||||
lru := ns.lru
|
ns.lru.mu.Lock()
|
||||||
lru.Lock()
|
|
||||||
|
|
||||||
if ns.state != nsEffective {
|
if ns.state != nsEffective {
|
||||||
lru.Unlock()
|
|
||||||
if fin != nil {
|
if fin != nil {
|
||||||
fin(false)
|
fin(false, false)
|
||||||
}
|
}
|
||||||
|
ns.lru.mu.Unlock()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
n, ok := ns.table[key]
|
node, exist := ns.table[key]
|
||||||
if !ok {
|
if !exist {
|
||||||
lru.Unlock()
|
|
||||||
if fin != nil {
|
if fin != nil {
|
||||||
fin(false)
|
fin(false, false)
|
||||||
}
|
}
|
||||||
|
ns.lru.mu.Unlock()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
n.delfin = fin
|
switch node.state {
|
||||||
switch n.state {
|
case nodeDeleted:
|
||||||
case nodeRemoved:
|
if fin != nil {
|
||||||
lru.Unlock()
|
fin(true, true)
|
||||||
|
}
|
||||||
|
ns.lru.mu.Unlock()
|
||||||
return false
|
return false
|
||||||
case nodeEffective:
|
case nodeEffective:
|
||||||
lru.size -= n.charge
|
ns.lru.used -= node.charge
|
||||||
n.rRemove()
|
node.state = nodeDeleted
|
||||||
n.evictNB()
|
node.delfin = fin
|
||||||
|
node.rRemove()
|
||||||
|
node.derefNB()
|
||||||
|
default:
|
||||||
|
node.state = nodeDeleted
|
||||||
|
node.delfin = fin
|
||||||
}
|
}
|
||||||
n.state = nodeRemoved
|
|
||||||
|
|
||||||
lru.Unlock()
|
ns.lru.mu.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) purgeNB(fin PurgeFin) {
|
func (ns *lruNs) purgeNB(fin PurgeFin) {
|
||||||
lru := ns.lru
|
|
||||||
if ns.state != nsEffective {
|
if ns.state != nsEffective {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range ns.table {
|
for _, node := range ns.table {
|
||||||
n.purgefin = fin
|
switch node.state {
|
||||||
if n.state == nodeEffective {
|
case nodeDeleted:
|
||||||
lru.size -= n.charge
|
case nodeEffective:
|
||||||
n.rRemove()
|
ns.lru.used -= node.charge
|
||||||
n.evictNB()
|
node.state = nodeDeleted
|
||||||
|
node.purgefin = fin
|
||||||
|
node.rRemove()
|
||||||
|
node.derefNB()
|
||||||
|
default:
|
||||||
|
node.state = nodeDeleted
|
||||||
|
node.purgefin = fin
|
||||||
}
|
}
|
||||||
n.state = nodeRemoved
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) Purge(fin PurgeFin) {
|
func (ns *lruNs) Purge(fin PurgeFin) {
|
||||||
ns.lru.Lock()
|
ns.lru.mu.Lock()
|
||||||
ns.purgeNB(fin)
|
ns.purgeNB(fin)
|
||||||
ns.lru.Unlock()
|
ns.lru.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) zapNB(closed bool) {
|
func (ns *lruNs) zapNB() {
|
||||||
lru := ns.lru
|
|
||||||
if ns.state != nsEffective {
|
if ns.state != nsEffective {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if closed {
|
|
||||||
ns.state = nsClosed
|
|
||||||
} else {
|
|
||||||
ns.state = nsZapped
|
ns.state = nsZapped
|
||||||
|
|
||||||
|
for _, node := range ns.table {
|
||||||
|
if node.state == nodeEffective {
|
||||||
|
ns.lru.used -= node.charge
|
||||||
|
node.rRemove()
|
||||||
}
|
}
|
||||||
for _, n := range ns.table {
|
ns.lru.size -= node.charge
|
||||||
if n.state == nodeEffective {
|
node.state = nodeDeleted
|
||||||
lru.size -= n.charge
|
node.fin()
|
||||||
n.rRemove()
|
|
||||||
}
|
|
||||||
n.state = nodeRemoved
|
|
||||||
n.execFin()
|
|
||||||
}
|
}
|
||||||
ns.table = nil
|
ns.table = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *lruNs) Zap(closed bool) {
|
func (ns *lruNs) Zap() {
|
||||||
ns.lru.Lock()
|
ns.lru.mu.Lock()
|
||||||
ns.zapNB(closed)
|
ns.zapNB()
|
||||||
delete(ns.lru.table, ns.id)
|
delete(ns.lru.table, ns.id)
|
||||||
ns.lru.Unlock()
|
ns.lru.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
type lruNode struct {
|
type lruNode struct {
|
||||||
@ -270,7 +276,6 @@ type lruNode struct {
|
|||||||
charge int
|
charge int
|
||||||
ref int
|
ref int
|
||||||
state nodeState
|
state nodeState
|
||||||
setfin SetFin
|
|
||||||
delfin DelFin
|
delfin DelFin
|
||||||
purgefin PurgeFin
|
purgefin PurgeFin
|
||||||
}
|
}
|
||||||
@ -284,7 +289,6 @@ func (n *lruNode) rInsert(at *lruNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *lruNode) rRemove() bool {
|
func (n *lruNode) rRemove() bool {
|
||||||
// only remove if not already removed
|
|
||||||
if n.rPrev == nil {
|
if n.rPrev == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -297,58 +301,56 @@ func (n *lruNode) rRemove() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *lruNode) execFin() {
|
func (n *lruNode) fin() {
|
||||||
if n.setfin != nil {
|
if r, ok := n.value.(util.Releaser); ok {
|
||||||
n.setfin()
|
r.Release()
|
||||||
n.setfin = nil
|
|
||||||
}
|
}
|
||||||
if n.purgefin != nil {
|
if n.purgefin != nil {
|
||||||
n.purgefin(n.ns.id, n.key, n.delfin)
|
n.purgefin(n.ns.id, n.key)
|
||||||
n.delfin = nil
|
n.delfin = nil
|
||||||
n.purgefin = nil
|
n.purgefin = nil
|
||||||
} else if n.delfin != nil {
|
} else if n.delfin != nil {
|
||||||
n.delfin(true)
|
n.delfin(true, false)
|
||||||
n.delfin = nil
|
n.delfin = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *lruNode) evictNB() {
|
func (n *lruNode) derefNB() {
|
||||||
n.ref--
|
n.ref--
|
||||||
if n.ref == 0 {
|
if n.ref == 0 {
|
||||||
if n.ns.state == nsEffective {
|
if n.ns.state == nsEffective {
|
||||||
// remove elem
|
// Remove elemement.
|
||||||
delete(n.ns.table, n.key)
|
delete(n.ns.table, n.key)
|
||||||
// execute finalizer
|
n.ns.lru.size -= n.charge
|
||||||
n.execFin()
|
n.fin()
|
||||||
}
|
}
|
||||||
} else if n.ref < 0 {
|
} else if n.ref < 0 {
|
||||||
panic("leveldb/cache: lruCache: negative node reference")
|
panic("leveldb/cache: lruCache: negative node reference")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *lruNode) evict() {
|
func (n *lruNode) deref() {
|
||||||
n.ns.lru.Lock()
|
n.ns.lru.mu.Lock()
|
||||||
n.evictNB()
|
n.derefNB()
|
||||||
n.ns.lru.Unlock()
|
n.ns.lru.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
type lruObject struct {
|
type lruHandle struct {
|
||||||
node *lruNode
|
node *lruNode
|
||||||
once uint32
|
once uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *lruObject) Value() interface{} {
|
func (h *lruHandle) Value() interface{} {
|
||||||
if atomic.LoadUint32(&o.once) == 0 {
|
if atomic.LoadUint32(&h.once) == 0 {
|
||||||
return o.node.value
|
return h.node.value
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *lruObject) Release() {
|
func (h *lruHandle) Release() {
|
||||||
if !atomic.CompareAndSwapUint32(&o.once, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&h.once, 0, 1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
h.node.deref()
|
||||||
o.node.evict()
|
h.node = nil
|
||||||
o.node = nil
|
|
||||||
}
|
}
|
||||||
|
12
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db.go
generated
vendored
12
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db.go
generated
vendored
@ -655,6 +655,8 @@ func (db *DB) GetSnapshot() (*Snapshot, error) {
|
|||||||
// Returns statistics of the underlying DB.
|
// Returns statistics of the underlying DB.
|
||||||
// leveldb.sstables
|
// leveldb.sstables
|
||||||
// Returns sstables list for each level.
|
// Returns sstables list for each level.
|
||||||
|
// leveldb.blockpool
|
||||||
|
// Returns block pool stats.
|
||||||
func (db *DB) GetProperty(name string) (value string, err error) {
|
func (db *DB) GetProperty(name string) (value string, err error) {
|
||||||
err = db.ok()
|
err = db.ok()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -700,6 +702,16 @@ func (db *DB) GetProperty(name string) (value string, err error) {
|
|||||||
value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.file.Num(), t.size, t.imin, t.imax)
|
value += fmt.Sprintf("%d:%d[%q .. %q]\n", t.file.Num(), t.size, t.imin, t.imax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case p == "blockpool":
|
||||||
|
value = fmt.Sprintf("%v", db.s.tops.bpool)
|
||||||
|
case p == "cachedblock":
|
||||||
|
if bc := db.s.o.GetBlockCache(); bc != nil {
|
||||||
|
value = fmt.Sprintf("%d", bc.Size())
|
||||||
|
} else {
|
||||||
|
value = "<nil>"
|
||||||
|
}
|
||||||
|
case p == "openedtables":
|
||||||
|
value = fmt.Sprintf("%d", db.s.tops.cache.Size())
|
||||||
default:
|
default:
|
||||||
err = errors.New("leveldb: GetProperty: unknown property: " + name)
|
err = errors.New("leveldb: GetProperty: unknown property: " + name)
|
||||||
}
|
}
|
||||||
|
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/doc.go
generated
vendored
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/doc.go
generated
vendored
@ -37,6 +37,16 @@
|
|||||||
// err = iter.Error()
|
// err = iter.Error()
|
||||||
// ...
|
// ...
|
||||||
//
|
//
|
||||||
|
// Iterate over subset of database content with a particular prefix:
|
||||||
|
// iter := db.NewIterator(util.BytesPrefix([]byte("foo-")), nil)
|
||||||
|
// for iter.Next() {
|
||||||
|
// // Use key/value.
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// iter.Release()
|
||||||
|
// err = iter.Error()
|
||||||
|
// ...
|
||||||
|
//
|
||||||
// Seek-then-Iterate:
|
// Seek-then-Iterate:
|
||||||
//
|
//
|
||||||
// iter := db.NewIterator(nil, nil)
|
// iter := db.NewIterator(nil, nil)
|
||||||
|
5
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt/options.go
generated
vendored
5
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/opt/options.go
generated
vendored
@ -31,9 +31,12 @@ const (
|
|||||||
type noCache struct{}
|
type noCache struct{}
|
||||||
|
|
||||||
func (noCache) SetCapacity(capacity int) {}
|
func (noCache) SetCapacity(capacity int) {}
|
||||||
|
func (noCache) Capacity() int { return 0 }
|
||||||
|
func (noCache) Used() int { return 0 }
|
||||||
|
func (noCache) Size() int { return 0 }
|
||||||
func (noCache) GetNamespace(id uint64) cache.Namespace { return nil }
|
func (noCache) GetNamespace(id uint64) cache.Namespace { return nil }
|
||||||
func (noCache) Purge(fin cache.PurgeFin) {}
|
func (noCache) Purge(fin cache.PurgeFin) {}
|
||||||
func (noCache) Zap(closed bool) {}
|
func (noCache) Zap() {}
|
||||||
|
|
||||||
var NoCache cache.Cache = noCache{}
|
var NoCache cache.Cache = noCache{}
|
||||||
|
|
||||||
|
65
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table.go
generated
vendored
65
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table.go
generated
vendored
@ -7,6 +7,7 @@
|
|||||||
package leveldb
|
package leveldb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ func (t *tOps) create() (*tWriter, error) {
|
|||||||
func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
|
func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
|
||||||
w, err := t.create()
|
w, err := t.create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return f, n, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -322,33 +323,33 @@ func (t *tOps) createFrom(src iterator.Iterator) (f *tFile, n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens table. It returns a cache object, which should
|
type tableWrapper struct {
|
||||||
|
*table.Reader
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr tableWrapper) Release() {
|
||||||
|
tr.closer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens table. It returns a cache handle, which should
|
||||||
// be released after use.
|
// be released after use.
|
||||||
func (t *tOps) open(f *tFile) (c cache.Object, err error) {
|
func (t *tOps) open(f *tFile) (ch cache.Handle, err error) {
|
||||||
num := f.file.Num()
|
num := f.file.Num()
|
||||||
c, ok := t.cacheNS.Get(num, func() (ok bool, value interface{}, charge int, fin cache.SetFin) {
|
ch = t.cacheNS.Get(num, func() (charge int, value interface{}) {
|
||||||
var r storage.Reader
|
var r storage.Reader
|
||||||
r, err = f.file.Open()
|
r, err = f.file.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
o := t.s.o
|
var bcacheNS cache.Namespace
|
||||||
|
if bc := t.s.o.GetBlockCache(); bc != nil {
|
||||||
var cacheNS cache.Namespace
|
bcacheNS = bc.GetNamespace(num)
|
||||||
if bc := o.GetBlockCache(); bc != nil {
|
|
||||||
cacheNS = bc.GetNamespace(num)
|
|
||||||
}
|
}
|
||||||
|
return 1, tableWrapper{table.NewReader(r, int64(f.size), bcacheNS, t.bpool, t.s.o), r}
|
||||||
ok = true
|
|
||||||
value = table.NewReader(r, int64(f.size), cacheNS, t.bpool, o)
|
|
||||||
charge = 1
|
|
||||||
fin = func() {
|
|
||||||
r.Close()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
})
|
})
|
||||||
if !ok && err == nil {
|
if ch == nil && err == nil {
|
||||||
err = ErrClosed
|
err = ErrClosed
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -357,34 +358,34 @@ func (t *tOps) open(f *tFile) (c cache.Object, err error) {
|
|||||||
// Finds key/value pair whose key is greater than or equal to the
|
// Finds key/value pair whose key is greater than or equal to the
|
||||||
// given key.
|
// given key.
|
||||||
func (t *tOps) find(f *tFile, key []byte, ro *opt.ReadOptions) (rkey, rvalue []byte, err error) {
|
func (t *tOps) find(f *tFile, key []byte, ro *opt.ReadOptions) (rkey, rvalue []byte, err error) {
|
||||||
c, err := t.open(f)
|
ch, err := t.open(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
defer c.Release()
|
defer ch.Release()
|
||||||
return c.Value().(*table.Reader).Find(key, ro)
|
return ch.Value().(tableWrapper).Find(key, ro)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns approximate offset of the given key.
|
// Returns approximate offset of the given key.
|
||||||
func (t *tOps) offsetOf(f *tFile, key []byte) (offset uint64, err error) {
|
func (t *tOps) offsetOf(f *tFile, key []byte) (offset uint64, err error) {
|
||||||
c, err := t.open(f)
|
ch, err := t.open(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_offset, err := c.Value().(*table.Reader).OffsetOf(key)
|
_offset, err := ch.Value().(tableWrapper).OffsetOf(key)
|
||||||
offset = uint64(_offset)
|
offset = uint64(_offset)
|
||||||
c.Release()
|
ch.Release()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an iterator from the given table.
|
// Creates an iterator from the given table.
|
||||||
func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
||||||
c, err := t.open(f)
|
ch, err := t.open(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iterator.NewEmptyIterator(err)
|
return iterator.NewEmptyIterator(err)
|
||||||
}
|
}
|
||||||
iter := c.Value().(*table.Reader).NewIterator(slice, ro)
|
iter := ch.Value().(tableWrapper).NewIterator(slice, ro)
|
||||||
iter.SetReleaser(c)
|
iter.SetReleaser(ch)
|
||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,14 +393,16 @@ func (t *tOps) newIterator(f *tFile, slice *util.Range, ro *opt.ReadOptions) ite
|
|||||||
// no one use the the table.
|
// no one use the the table.
|
||||||
func (t *tOps) remove(f *tFile) {
|
func (t *tOps) remove(f *tFile) {
|
||||||
num := f.file.Num()
|
num := f.file.Num()
|
||||||
t.cacheNS.Delete(num, func(exist bool) {
|
t.cacheNS.Delete(num, func(exist, pending bool) {
|
||||||
|
if !pending {
|
||||||
if err := f.file.Remove(); err != nil {
|
if err := f.file.Remove(); err != nil {
|
||||||
t.s.logf("table@remove removing @%d %q", num, err)
|
t.s.logf("table@remove removing @%d %q", num, err)
|
||||||
} else {
|
} else {
|
||||||
t.s.logf("table@remove removed @%d", num)
|
t.s.logf("table@remove removed @%d", num)
|
||||||
}
|
}
|
||||||
if bc := t.s.o.GetBlockCache(); bc != nil {
|
if bc := t.s.o.GetBlockCache(); bc != nil {
|
||||||
bc.GetNamespace(num).Zap(false)
|
bc.GetNamespace(num).Zap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -407,7 +410,7 @@ func (t *tOps) remove(f *tFile) {
|
|||||||
// Closes the table ops instance. It will close all tables,
|
// Closes the table ops instance. It will close all tables,
|
||||||
// regadless still used or not.
|
// regadless still used or not.
|
||||||
func (t *tOps) close() {
|
func (t *tOps) close() {
|
||||||
t.cache.Zap(true)
|
t.cache.Zap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates new initialized table ops instance.
|
// Creates new initialized table ops instance.
|
||||||
|
42
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/reader.go
generated
vendored
42
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/table/reader.go
generated
vendored
@ -37,6 +37,7 @@ func max(x, y int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type block struct {
|
type block struct {
|
||||||
|
bpool *util.BufferPool
|
||||||
cmp comparer.BasicComparer
|
cmp comparer.BasicComparer
|
||||||
data []byte
|
data []byte
|
||||||
restartsLen int
|
restartsLen int
|
||||||
@ -139,6 +140,14 @@ func (b *block) newIterator(slice *util.Range, inclLimit bool, cache util.Releas
|
|||||||
return bi
|
return bi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *block) Release() {
|
||||||
|
if b.bpool != nil {
|
||||||
|
b.bpool.Put(b.data)
|
||||||
|
b.bpool = nil
|
||||||
|
}
|
||||||
|
b.data = nil
|
||||||
|
}
|
||||||
|
|
||||||
type dir int
|
type dir int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -438,6 +447,7 @@ func (i *blockIter) Value() []byte {
|
|||||||
|
|
||||||
func (i *blockIter) Release() {
|
func (i *blockIter) Release() {
|
||||||
if i.dir > dirReleased {
|
if i.dir > dirReleased {
|
||||||
|
i.block = nil
|
||||||
i.prevNode = nil
|
i.prevNode = nil
|
||||||
i.prevKeys = nil
|
i.prevKeys = nil
|
||||||
i.key = nil
|
i.key = nil
|
||||||
@ -610,43 +620,25 @@ func (r *Reader) readFilterBlock(bh blockHandle, filter filter.Filter) (*filterB
|
|||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type releaseBlock struct {
|
|
||||||
r *Reader
|
|
||||||
b *block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r releaseBlock) Release() {
|
|
||||||
if r.b.data != nil {
|
|
||||||
r.r.bpool.Put(r.b.data)
|
|
||||||
r.b.data = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fillCache bool) iterator.Iterator {
|
func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fillCache bool) iterator.Iterator {
|
||||||
if r.cache != nil {
|
if r.cache != nil {
|
||||||
// Get/set block cache.
|
// Get/set block cache.
|
||||||
var err error
|
var err error
|
||||||
cache, ok := r.cache.Get(dataBH.offset, func() (ok bool, value interface{}, charge int, fin cache.SetFin) {
|
cache := r.cache.Get(dataBH.offset, func() (charge int, value interface{}) {
|
||||||
if !fillCache {
|
if !fillCache {
|
||||||
return
|
return 0, nil
|
||||||
}
|
}
|
||||||
var dataBlock *block
|
var dataBlock *block
|
||||||
dataBlock, err = r.readBlock(dataBH, checksum)
|
dataBlock, err = r.readBlock(dataBH, checksum)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
ok = true
|
return 0, nil
|
||||||
value = dataBlock
|
|
||||||
charge = int(dataBH.length)
|
|
||||||
fin = func() {
|
|
||||||
r.bpool.Put(dataBlock.data)
|
|
||||||
dataBlock.data = nil
|
|
||||||
}
|
}
|
||||||
}
|
return int(dataBH.length), dataBlock
|
||||||
return
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iterator.NewEmptyIterator(err)
|
return iterator.NewEmptyIterator(err)
|
||||||
}
|
}
|
||||||
if ok {
|
if cache != nil {
|
||||||
dataBlock := cache.Value().(*block)
|
dataBlock := cache.Value().(*block)
|
||||||
if !dataBlock.checksum && (r.checksum || checksum) {
|
if !dataBlock.checksum && (r.checksum || checksum) {
|
||||||
if !verifyChecksum(dataBlock.data) {
|
if !verifyChecksum(dataBlock.data) {
|
||||||
@ -662,7 +654,7 @@ func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return iterator.NewEmptyIterator(err)
|
return iterator.NewEmptyIterator(err)
|
||||||
}
|
}
|
||||||
iter := dataBlock.newIterator(slice, false, releaseBlock{r, dataBlock})
|
iter := dataBlock.newIterator(slice, false, dataBlock)
|
||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util/buffer_pool_legacy.go
generated
vendored
10
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util/buffer_pool_legacy.go
generated
vendored
@ -26,6 +26,8 @@ type BufferPool struct {
|
|||||||
baseline1 int
|
baseline1 int
|
||||||
baseline2 int
|
baseline2 int
|
||||||
|
|
||||||
|
get uint32
|
||||||
|
put uint32
|
||||||
less uint32
|
less uint32
|
||||||
equal uint32
|
equal uint32
|
||||||
greater uint32
|
greater uint32
|
||||||
@ -47,6 +49,8 @@ func (p *BufferPool) poolNum(n int) int {
|
|||||||
|
|
||||||
// Get returns buffer with length of n.
|
// Get returns buffer with length of n.
|
||||||
func (p *BufferPool) Get(n int) []byte {
|
func (p *BufferPool) Get(n int) []byte {
|
||||||
|
atomic.AddUint32(&p.get, 1)
|
||||||
|
|
||||||
poolNum := p.poolNum(n)
|
poolNum := p.poolNum(n)
|
||||||
pool := p.pool[poolNum]
|
pool := p.pool[poolNum]
|
||||||
if poolNum == 0 {
|
if poolNum == 0 {
|
||||||
@ -112,6 +116,8 @@ func (p *BufferPool) Get(n int) []byte {
|
|||||||
|
|
||||||
// Put adds given buffer to the pool.
|
// Put adds given buffer to the pool.
|
||||||
func (p *BufferPool) Put(b []byte) {
|
func (p *BufferPool) Put(b []byte) {
|
||||||
|
atomic.AddUint32(&p.put, 1)
|
||||||
|
|
||||||
pool := p.pool[p.poolNum(cap(b))]
|
pool := p.pool[p.poolNum(cap(b))]
|
||||||
select {
|
select {
|
||||||
case pool <- b:
|
case pool <- b:
|
||||||
@ -121,8 +127,8 @@ func (p *BufferPool) Put(b []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *BufferPool) String() string {
|
func (p *BufferPool) String() string {
|
||||||
return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v L·%d E·%d G·%d M·%d}",
|
return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v G·%d P·%d <·%d =·%d >·%d M·%d}",
|
||||||
p.baseline0, p.size, p.sizeMiss, p.less, p.equal, p.greater, p.miss)
|
p.baseline0, p.size, p.sizeMiss, p.get, p.put, p.less, p.equal, p.greater, p.miss)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BufferPool) drain() {
|
func (p *BufferPool) drain() {
|
||||||
|
15
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util/range.go
generated
vendored
15
Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/util/range.go
generated
vendored
@ -14,3 +14,18 @@ type Range struct {
|
|||||||
// Limit of the key range, not include in the range.
|
// Limit of the key range, not include in the range.
|
||||||
Limit []byte
|
Limit []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BytesPrefix returns key range that satisfy the given prefix.
|
||||||
|
// This only applicable for the standard 'bytes comparer'.
|
||||||
|
func BytesPrefix(prefix []byte) *Range {
|
||||||
|
var limit []byte
|
||||||
|
for i := len(prefix) - 1; i >= 0; i-- {
|
||||||
|
c := prefix[i]
|
||||||
|
if c < 0xff {
|
||||||
|
limit = make([]byte, i+1)
|
||||||
|
copy(limit, prefix)
|
||||||
|
limit[i] = c + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Range{prefix, limit}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user