leveldb: version now locks individual files (fixes #161)

This way version doesn't need to locks successive versions.
So that intermediate files generated during compaction can
be released right away, even if old versions still being used.
diff --git a/leveldb/session.go b/leveldb/session.go
index f3e7477..ad68a87 100644
--- a/leveldb/session.go
+++ b/leveldb/session.go
@@ -47,6 +47,7 @@
 	o        *cachedOptions
 	icmp     *iComparer
 	tops     *tOps
+	fileRef  map[int64]int
 
 	manifest       *journal.Writer
 	manifestWriter storage.Writer
@@ -69,6 +70,7 @@
 	s = &session{
 		stor:     stor,
 		storLock: storLock,
+		fileRef:  make(map[int64]int),
 	}
 	s.setOptions(o)
 	s.tops = newTableOps(s)
diff --git a/leveldb/session_util.go b/leveldb/session_util.go
index 34ad617..9232893 100644
--- a/leveldb/session_util.go
+++ b/leveldb/session_util.go
@@ -39,6 +39,18 @@
 	return storage.FileDesc{storage.TypeTemp, num}
 }
 
+func (s *session) addFileRef(fd storage.FileDesc, ref int) int {
+	ref += s.fileRef[fd.Num]
+	if ref > 0 {
+		s.fileRef[fd.Num] = ref
+	} else if ref == 0 {
+		delete(s.fileRef, fd.Num)
+	} else {
+		panic(fmt.Sprintf("negative ref: %v", fd))
+	}
+	return ref
+}
+
 // Session state.
 
 // Get current version. This will incr version ref, must call
@@ -46,7 +58,7 @@
 func (s *session) version() *version {
 	s.vmu.Lock()
 	defer s.vmu.Unlock()
-	s.stVersion.ref++
+	s.stVersion.incref()
 	return s.stVersion
 }
 
@@ -59,14 +71,15 @@
 // Set current version to v.
 func (s *session) setVersion(v *version) {
 	s.vmu.Lock()
-	v.ref = 1 // Holds by session.
-	if old := s.stVersion; old != nil {
-		v.ref++ // Holds by old version.
-		old.next = v
-		old.releaseNB()
+	defer s.vmu.Unlock()
+	// Hold by session. It is important to call this first before releasing
+	// current version, otherwise the still used files might get released.
+	v.incref()
+	if s.stVersion != nil {
+		// Release current version.
+		s.stVersion.releaseNB()
 	}
 	s.stVersion = v
-	s.vmu.Unlock()
 }
 
 // Get current unused file number.
diff --git a/leveldb/version.go b/leveldb/version.go
index c60f12c..73f272a 100644
--- a/leveldb/version.go
+++ b/leveldb/version.go
@@ -34,44 +34,48 @@
 
 	cSeek unsafe.Pointer
 
-	closing bool
-	ref     int
-	// Succeeding version.
-	next *version
+	closing  bool
+	ref      int
+	released bool
 }
 
 func newVersion(s *session) *version {
 	return &version{s: s}
 }
 
+func (v *version) incref() {
+	if v.released {
+		panic("already released")
+	}
+
+	v.ref++
+	if v.ref == 1 {
+		// Incr file ref.
+		for _, tt := range v.levels {
+			for _, t := range tt {
+				v.s.addFileRef(t.fd, 1)
+			}
+		}
+	}
+}
+
 func (v *version) releaseNB() {
 	v.ref--
 	if v.ref > 0 {
 		return
-	}
-	if v.ref < 0 {
+	} else if v.ref < 0 {
 		panic("negative version ref")
 	}
 
-	nextTables := make(map[int64]bool)
-	for _, tt := range v.next.levels {
-		for _, t := range tt {
-			num := t.fd.Num
-			nextTables[num] = true
-		}
-	}
-
 	for _, tt := range v.levels {
 		for _, t := range tt {
-			num := t.fd.Num
-			if _, ok := nextTables[num]; !ok {
+			if v.s.addFileRef(t.fd, -1) == 0 {
 				v.s.tops.remove(t)
 			}
 		}
 	}
 
-	v.next.releaseNB()
-	v.next = nil
+	v.released = true
 }
 
 func (v *version) release() {