Index: store.go |
diff --git a/store.go b/store.go |
index 8e8d6dba66ccc852b8be329229114dc065edb421..e1d7a0bd22c57395cb5decdff66d37f0620dbc83 100644 |
--- a/store.go |
+++ b/store.go |
@@ -15,18 +15,41 @@ import ( |
"sort" |
"sync" |
"sync/atomic" |
- "unsafe" |
) |
// A persistable store holding collections of ordered keys & values. |
type Store struct { |
+ m sync.Mutex |
+ |
// Atomic CAS'ed int64/uint64's must be at the top for 32-bit compatibility. |
- size int64 // Atomic protected; file size or next write position. |
- nodeAllocs uint64 // Atomic protected; total node allocation stats. |
- coll unsafe.Pointer // Copy-on-write map[string]*Collection. |
- file StoreFile // When nil, we're memory-only or no persistence. |
- callbacks StoreCallbacks // Optional / may be nil. |
- readOnly bool // When true, Flush()'ing is disallowed. |
+ size int64 // Atomic protected; file size or next write position. |
+ nodeAllocs uint64 // Atomic protected; total node allocation stats. |
+ coll *map[string]*Collection // Copy-on-write map[string]*Collection. |
+ file StoreFile // When nil, we're memory-only or no persistence. |
+ callbacks StoreCallbacks // Optional / may be nil. |
+ readOnly bool // When true, Flush()'ing is disallowed. |
+} |
+ |
+func (s *Store) setColl(n *map[string]*Collection) { |
+ s.m.Lock() |
+ defer s.m.Unlock() |
+ s.coll = n |
+} |
+ |
+func (s *Store) getColl() *map[string]*Collection { |
+ s.m.Lock() |
+ defer s.m.Unlock() |
+ return s.coll |
+} |
+ |
+func (s *Store) casColl(o, n *map[string]*Collection) bool { |
+ s.m.Lock() |
+ defer s.m.Unlock() |
+ if s.coll == o { |
+ s.coll = n |
+ return true |
+ } |
+ return false |
} |
// The StoreFile interface is implemented by os.File. Application |
@@ -95,7 +118,7 @@ func NewStore(file StoreFile) (*Store, error) { |
func NewStoreEx(file StoreFile, |
callbacks StoreCallbacks) (*Store, error) { |
coll := make(map[string]*Collection) |
- res := &Store{coll: unsafe.Pointer(&coll), callbacks: callbacks} |
+ res := &Store{coll: &coll, callbacks: callbacks} |
if file == nil || !reflect.ValueOf(file).Elem().IsValid() { |
return res, nil // Memory-only Store. |
} |
@@ -115,7 +138,7 @@ func (s *Store) SetCollection(name string, compare KeyCompare) *Collection { |
compare = bytes.Compare |
} |
for { |
- orig := atomic.LoadPointer(&s.coll) |
+ orig := s.getColl() |
coll := copyColl(*(*map[string]*Collection)(orig)) |
cnew := s.MakePrivateCollection(compare) |
cnew.name = name |
@@ -125,7 +148,7 @@ func (s *Store) SetCollection(name string, compare KeyCompare) *Collection { |
cnew.root = cold.rootAddRef() |
} |
coll[name] = cnew |
- if atomic.CompareAndSwapPointer(&s.coll, orig, unsafe.Pointer(&coll)) { |
+ if s.casColl(orig, &coll) { |
cold.closeCollection() |
return cnew |
} |
@@ -149,12 +172,11 @@ func (s *Store) MakePrivateCollection(compare KeyCompare) *Collection { |
// Retrieves a named Collection. |
func (s *Store) GetCollection(name string) *Collection { |
- coll := *(*map[string]*Collection)(atomic.LoadPointer(&s.coll)) |
- return coll[name] |
+ return (*s.getColl())[name] |
} |
func (s *Store) GetCollectionNames() []string { |
- return collNames(*(*map[string]*Collection)(atomic.LoadPointer(&s.coll))) |
+ return collNames(*s.getColl()) |
} |
func collNames(coll map[string]*Collection) []string { |
@@ -171,11 +193,11 @@ func collNames(coll map[string]*Collection) []string { |
// SetCollection(x) is a fast way to empty a Collection. |
func (s *Store) RemoveCollection(name string) { |
for { |
- orig := atomic.LoadPointer(&s.coll) |
+ orig := s.getColl() |
coll := copyColl(*(*map[string]*Collection)(orig)) |
cold := coll[name] |
delete(coll, name) |
- if atomic.CompareAndSwapPointer(&s.coll, orig, unsafe.Pointer(&coll)) { |
+ if s.casColl(orig, &coll) { |
cold.closeCollection() |
return |
} |
@@ -203,7 +225,7 @@ func (s *Store) Flush() error { |
if s.file == nil { |
return errors.New("no file / in-memory only, so cannot Flush()") |
} |
- coll := *(*map[string]*Collection)(atomic.LoadPointer(&s.coll)) |
+ coll := *s.getColl() |
rnls := map[string]*rootNodeLoc{} |
cnames := collNames(coll) |
for _, name := range cnames { |
@@ -231,9 +253,9 @@ func (s *Store) FlushRevert() error { |
if s.file == nil { |
return errors.New("no file / in-memory only, so cannot FlushRevert()") |
} |
- orig := atomic.LoadPointer(&s.coll) |
+ orig := s.getColl() |
coll := make(map[string]*Collection) |
- if atomic.CompareAndSwapPointer(&s.coll, orig, unsafe.Pointer(&coll)) { |
+ if s.casColl(orig, &coll) { |
for _, cold := range *(*map[string]*Collection)(orig) { |
cold.closeCollection() |
} |
@@ -256,9 +278,9 @@ func (s *Store) FlushRevert() error { |
// snapshot has its mutations and Flush() operations disabled because |
// the original store "owns" writes to the StoreFile. |
func (s *Store) Snapshot() (snapshot *Store) { |
- coll := copyColl(*(*map[string]*Collection)(atomic.LoadPointer(&s.coll))) |
+ coll := copyColl(*s.getColl()) |
res := &Store{ |
- coll: unsafe.Pointer(&coll), |
+ coll: &coll, |
file: s.file, |
size: atomic.LoadInt64(&s.size), |
readOnly: true, |
@@ -278,9 +300,8 @@ func (s *Store) Snapshot() (snapshot *Store) { |
func (s *Store) Close() { |
s.file = nil |
- cptr := atomic.LoadPointer(&s.coll) |
- if cptr == nil || |
- !atomic.CompareAndSwapPointer(&s.coll, cptr, unsafe.Pointer(nil)) { |
+ cptr := s.getColl() |
+ if cptr == nil || !s.casColl(cptr, nil) { |
return |
} |
coll := *(*map[string]*Collection)(cptr) |
@@ -299,7 +320,7 @@ func (s *Store) CopyTo(dstFile StoreFile, flushEvery int) (res *Store, err error |
if err != nil { |
return nil, err |
} |
- coll := *(*map[string]*Collection)(atomic.LoadPointer(&s.coll)) |
+ coll := *s.getColl() |
for _, name := range collNames(coll) { |
srcColl := coll[name] |
dstColl := dstStore.SetCollection(name, srcColl.compare) |
@@ -452,7 +473,7 @@ func (o *Store) readRootsScan(defaultToEmpty bool) (err error) { |
t.compare = bytes.Compare |
} |
} |
- atomic.StorePointer(&o.coll, unsafe.Pointer(&m)) |
+ o.setColl(&m) |
return nil |
} // else, perhaps value was unlucky in having MAGIC_END's. |
} // else, perhaps a gkvlite file was stored as a value. |