aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Molina <miguel@erizocosmi.co>2017-08-11 13:21:14 +0200
committerMiguel Molina <miguel@erizocosmi.co>2017-08-11 14:29:52 +0200
commit3fd398852745b218e4346a38c98d95e5e773751d (patch)
tree1689d6c0fdbdad5a1e804b293eba0bdae9fb1a7e
parent773841734e11f0f607f6b047235f8527d475538f (diff)
downloadgo-git-3fd398852745b218e4346a38c98d95e5e773751d.tar.gz
fix race condition on ObjectLRU
Signed-off-by: Miguel Molina <miguel@erizocosmi.co>
-rw-r--r--plumbing/cache/object_lru.go11
-rw-r--r--plumbing/cache/object_test.go28
2 files changed, 39 insertions, 0 deletions
diff --git a/plumbing/cache/object_lru.go b/plumbing/cache/object_lru.go
index e4c3160..e8414ab 100644
--- a/plumbing/cache/object_lru.go
+++ b/plumbing/cache/object_lru.go
@@ -2,6 +2,7 @@ package cache
import (
"container/list"
+ "sync"
"gopkg.in/src-d/go-git.v4/plumbing"
)
@@ -14,6 +15,7 @@ type ObjectLRU struct {
actualSize FileSize
ll *list.List
cache map[interface{}]*list.Element
+ mut sync.Mutex
}
// NewObjectLRU creates a new ObjectLRU with the given maximum size. The maximum
@@ -26,6 +28,9 @@ func NewObjectLRU(maxSize FileSize) *ObjectLRU {
// will be marked as used. Otherwise, it will be inserted. A single object might
// be evicted to make room for the new object.
func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
+ c.mut.Lock()
+ defer c.mut.Unlock()
+
if c.cache == nil {
c.actualSize = 0
c.cache = make(map[interface{}]*list.Element, 1000)
@@ -67,6 +72,9 @@ func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
// Get returns an object by its hash. It marks the object as used. If the object
// is not in the cache, (nil, false) will be returned.
func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) {
+ c.mut.Lock()
+ defer c.mut.Unlock()
+
ee, ok := c.cache[k]
if !ok {
return nil, false
@@ -78,6 +86,9 @@ func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) {
// Clear the content of this object cache.
func (c *ObjectLRU) Clear() {
+ c.mut.Lock()
+ defer c.mut.Unlock()
+
c.ll = nil
c.cache = nil
c.actualSize = 0
diff --git a/plumbing/cache/object_test.go b/plumbing/cache/object_test.go
index 9359455..b38272f 100644
--- a/plumbing/cache/object_test.go
+++ b/plumbing/cache/object_test.go
@@ -1,7 +1,9 @@
package cache
import (
+ "fmt"
"io"
+ "sync"
"testing"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -67,6 +69,32 @@ func (s *ObjectSuite) TestClear(c *C) {
c.Assert(obj, IsNil)
}
+func (s *ObjectSuite) TestConcurrentAccess(c *C) {
+ var wg sync.WaitGroup
+
+ for i := 0; i < 1000; i++ {
+ wg.Add(3)
+ go func(i int) {
+ s.c.Put(newObject(fmt.Sprint(i), FileSize(i)))
+ wg.Done()
+ }(i)
+
+ go func(i int) {
+ if i%30 == 0 {
+ s.c.Clear()
+ }
+ wg.Done()
+ }(i)
+
+ go func(i int) {
+ s.c.Get(plumbing.NewHash(fmt.Sprint(i)))
+ wg.Done()
+ }(i)
+ }
+
+ wg.Wait()
+}
+
type dummyObject struct {
hash plumbing.Hash
size FileSize