aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go')
-rw-r--r--Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go148
1 files changed, 83 insertions, 65 deletions
diff --git a/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go b/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go
index 225b7cd5e..0372848ff 100644
--- a/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go
+++ b/Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/db_snapshot.go
@@ -7,8 +7,11 @@
package leveldb
import (
+ "container/list"
+ "fmt"
"runtime"
"sync"
+ "sync/atomic"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
@@ -18,51 +21,41 @@ import (
type snapshotElement struct {
seq uint64
ref int
- // Next and previous pointers in the doubly-linked list of elements.
- next, prev *snapshotElement
-}
-
-// Initialize the snapshot.
-func (db *DB) initSnapshot() {
- db.snapsRoot.next = &db.snapsRoot
- db.snapsRoot.prev = &db.snapsRoot
+ e *list.Element
}
// Acquires a snapshot, based on latest sequence.
func (db *DB) acquireSnapshot() *snapshotElement {
db.snapsMu.Lock()
+ defer db.snapsMu.Unlock()
+
seq := db.getSeq()
- elem := db.snapsRoot.prev
- if elem == &db.snapsRoot || elem.seq != seq {
- at := db.snapsRoot.prev
- next := at.next
- elem = &snapshotElement{
- seq: seq,
- prev: at,
- next: next,
+
+ if e := db.snapsList.Back(); e != nil {
+ se := e.Value.(*snapshotElement)
+ if se.seq == seq {
+ se.ref++
+ return se
+ } else if seq < se.seq {
+ panic("leveldb: sequence number is not increasing")
}
- at.next = elem
- next.prev = elem
}
- elem.ref++
- db.snapsMu.Unlock()
- return elem
+ se := &snapshotElement{seq: seq, ref: 1}
+ se.e = db.snapsList.PushBack(se)
+ return se
}
// Releases given snapshot element.
-func (db *DB) releaseSnapshot(elem *snapshotElement) {
- if !db.isClosed() {
- db.snapsMu.Lock()
- elem.ref--
- if elem.ref == 0 {
- elem.prev.next = elem.next
- elem.next.prev = elem.prev
- elem.next = nil
- elem.prev = nil
- } else if elem.ref < 0 {
- panic("leveldb: Snapshot: negative element reference")
- }
- db.snapsMu.Unlock()
+func (db *DB) releaseSnapshot(se *snapshotElement) {
+ db.snapsMu.Lock()
+ defer db.snapsMu.Unlock()
+
+ se.ref--
+ if se.ref == 0 {
+ db.snapsList.Remove(se.e)
+ se.e = nil
+ } else if se.ref < 0 {
+ panic("leveldb: Snapshot: negative element reference")
}
}
@@ -70,10 +63,11 @@ func (db *DB) releaseSnapshot(elem *snapshotElement) {
func (db *DB) minSeq() uint64 {
db.snapsMu.Lock()
defer db.snapsMu.Unlock()
- elem := db.snapsRoot.prev
- if elem != &db.snapsRoot {
- return elem.seq
+
+ if e := db.snapsList.Front(); e != nil {
+ return e.Value.(*snapshotElement).seq
}
+
return db.getSeq()
}
@@ -81,38 +75,59 @@ func (db *DB) minSeq() uint64 {
type Snapshot struct {
db *DB
elem *snapshotElement
- mu sync.Mutex
+ mu sync.RWMutex
released bool
}
// Creates new snapshot object.
func (db *DB) newSnapshot() *Snapshot {
- p := &Snapshot{
+ snap := &Snapshot{
db: db,
elem: db.acquireSnapshot(),
}
- runtime.SetFinalizer(p, (*Snapshot).Release)
- return p
+ atomic.AddInt32(&db.aliveSnaps, 1)
+ runtime.SetFinalizer(snap, (*Snapshot).Release)
+ return snap
+}
+
+func (snap *Snapshot) String() string {
+ return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
}
// Get gets the value for the given key. It returns ErrNotFound if
-// the DB does not contain the key.
+// the DB does not contains the key.
//
// The caller should not modify the contents of the returned slice, but
// it is safe to modify the contents of the argument after Get returns.
-func (p *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
- db := p.db
- err = db.ok()
+func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
+ err = snap.db.ok()
if err != nil {
return
}
- p.mu.Lock()
- defer p.mu.Unlock()
- if p.released {
+ snap.mu.RLock()
+ defer snap.mu.RUnlock()
+ if snap.released {
err = ErrSnapshotReleased
return
}
- return db.get(key, p.elem.seq, ro)
+ return snap.db.get(key, snap.elem.seq, ro)
+}
+
+// Has returns true if the DB does contains the given key.
+//
+// It is safe to modify the contents of the argument after Get returns.
+func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
+ err = snap.db.ok()
+ if err != nil {
+ return
+ }
+ snap.mu.RLock()
+ defer snap.mu.RUnlock()
+ if snap.released {
+ err = ErrSnapshotReleased
+ return
+ }
+ return snap.db.has(key, snap.elem.seq, ro)
}
// NewIterator returns an iterator for the snapshot of the uderlying DB.
@@ -132,17 +147,18 @@ func (p *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error
// iterator would be still valid until released.
//
// Also read Iterator documentation of the leveldb/iterator package.
-func (p *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
- db := p.db
- if err := db.ok(); err != nil {
+func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
+ if err := snap.db.ok(); err != nil {
return iterator.NewEmptyIterator(err)
}
- p.mu.Lock()
- defer p.mu.Unlock()
- if p.released {
+ snap.mu.Lock()
+ defer snap.mu.Unlock()
+ if snap.released {
return iterator.NewEmptyIterator(ErrSnapshotReleased)
}
- return db.newIterator(p.elem.seq, slice, ro)
+ // Since iterator already hold version ref, it doesn't need to
+ // hold snapshot ref.
+ return snap.db.newIterator(snap.elem.seq, slice, ro)
}
// Release releases the snapshot. This will not release any returned
@@ -150,16 +166,18 @@ func (p *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.
// underlying DB is closed.
//
// Other methods should not be called after the snapshot has been released.
-func (p *Snapshot) Release() {
- p.mu.Lock()
- if !p.released {
+func (snap *Snapshot) Release() {
+ snap.mu.Lock()
+ defer snap.mu.Unlock()
+
+ if !snap.released {
// Clear the finalizer.
- runtime.SetFinalizer(p, nil)
+ runtime.SetFinalizer(snap, nil)
- p.released = true
- p.db.releaseSnapshot(p.elem)
- p.db = nil
- p.elem = nil
+ snap.released = true
+ snap.db.releaseSnapshot(snap.elem)
+ atomic.AddInt32(&snap.db.aliveSnaps, -1)
+ snap.db = nil
+ snap.elem = nil
}
- p.mu.Unlock()
}