From e08aa0f83206e88c7751aa4950561f7d8dd36dc5 Mon Sep 17 00:00:00 2001 From: Andrew Hust Date: Fri, 4 Nov 2016 15:01:03 -0600 Subject: [PATCH 1/2] added locking to mutation methods Store, DeleteOne and DeleteAll to InMemory storage --- memory.go | 8 ++++++ memory_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 memory_test.go diff --git a/memory.go b/memory.go index a603a4e..90764d6 100644 --- a/memory.go +++ b/memory.go @@ -2,6 +2,7 @@ package storage import ( "strings" + "sync" "github.com/mailhog/data" ) @@ -10,6 +11,7 @@ import ( type InMemory struct { MessageIDIndex map[string]int Messages []*data.Message + mu sync.Mutex } // CreateInMemory creates a new in memory storage backend @@ -22,8 +24,10 @@ func CreateInMemory() *InMemory { // Store stores a message and returns its storage ID func (memory *InMemory) Store(m *data.Message) (string, error) { + memory.mu.Lock() memory.Messages = append(memory.Messages, m) memory.MessageIDIndex[string(m.ID)] = len(memory.Messages) - 1 + memory.mu.Unlock() return string(m.ID), nil } @@ -156,6 +160,7 @@ func (memory *InMemory) List(start int, limit int) (*data.Messages, error) { // DeleteOne deletes an individual message by storage ID func (memory *InMemory) DeleteOne(id string) error { + memory.mu.Lock() index := memory.MessageIDIndex[id] delete(memory.MessageIDIndex, id) for k, v := range memory.MessageIDIndex { @@ -164,13 +169,16 @@ func (memory *InMemory) DeleteOne(id string) error { } } memory.Messages = append(memory.Messages[:index], memory.Messages[index+1:]...) + memory.mu.Unlock() return nil } // DeleteAll deletes all in memory messages func (memory *InMemory) DeleteAll() error { + memory.mu.Lock() memory.Messages = make([]*data.Message, 0) memory.MessageIDIndex = make(map[string]int) + memory.mu.Unlock() return nil } diff --git a/memory_test.go b/memory_test.go new file mode 100644 index 0000000..fe52913 --- /dev/null +++ b/memory_test.go @@ -0,0 +1,75 @@ +package storage + +import ( + "sync" + "testing" + "time" + + "github.com/mailhog/data" +) + +func TestStore(t *testing.T) { + storage := CreateInMemory() + + if storage.Count() != 0 { + t.Errorf("storage.Count() expected: %d, got: %d", 0, storage.Count()) + } + + var wg sync.WaitGroup + wg.Add(25) + for i := 0; i < 25; i++ { + go func(i int) { + msg := &data.Message{ + ID: data.MessageID(i), + Created: time.Now(), + } + storage.Store(msg) + wg.Done() + }(i) + } + wg.Wait() + + if storage.Count() != 25 { + t.Errorf("storage.Count() expected: %d, got: %d", 25, storage.Count()) + } +} + +func TestDeleteAll(t *testing.T) { + storage := CreateInMemory() + + if storage.Count() != 0 { + t.Errorf("storage.Count() expected: %d, got: %d", 0, storage.Count()) + } + + for i := 0; i < 25; i++ { + storage.Store(&data.Message{ID: data.MessageID(i), Created: time.Now()}) + } + + if storage.Count() != 25 { + t.Errorf("storage.Count() expected: %d, got: %d", 25, storage.Count()) + } + + storage.DeleteAll() + + if storage.Count() != 0 { + t.Errorf("storage.Count() expected: %d, got: %d", 0, storage.Count()) + } +} + +func TestDeleteOne(t *testing.T) { + storage := CreateInMemory() + + if storage.Count() != 0 { + t.Errorf("storage.Count() expected: %d, got: %d", 0, storage.Count()) + } + + for i := 0; i < 25; i++ { + storage.Store(&data.Message{ID: data.MessageID(i), Created: time.Now()}) + } + + storage.DeleteOne("1") + + if storage.Count() != 24 { + t.Errorf("storage.Count() expected: %d, got: %d", 0, storage.Count()) + } +} From a8cd8744e4bae6381e63c175ad3f76e8481d245c Mon Sep 17 00:00:00 2001 From: Andrew Hust Date: Mon, 14 Nov 2016 19:20:26 -0700 Subject: [PATCH 2/2] switched mutex unlock to be deferred --- memory.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/memory.go b/memory.go index 90764d6..9c5be8a 100644 --- a/memory.go +++ b/memory.go @@ -25,9 +25,9 @@ func CreateInMemory() *InMemory { // Store stores a message and returns its storage ID func (memory *InMemory) Store(m *data.Message) (string, error) { memory.mu.Lock() + defer memory.mu.Unlock() memory.Messages = append(memory.Messages, m) memory.MessageIDIndex[string(m.ID)] = len(memory.Messages) - 1 - memory.mu.Unlock() return string(m.ID), nil } @@ -161,6 +161,7 @@ func (memory *InMemory) List(start int, limit int) (*data.Messages, error) { // DeleteOne deletes an individual message by storage ID func (memory *InMemory) DeleteOne(id string) error { memory.mu.Lock() + defer memory.mu.Unlock() index := memory.MessageIDIndex[id] delete(memory.MessageIDIndex, id) for k, v := range memory.MessageIDIndex { @@ -169,16 +170,15 @@ func (memory *InMemory) DeleteOne(id string) error { } } memory.Messages = append(memory.Messages[:index], memory.Messages[index+1:]...) - memory.mu.Unlock() return nil } // DeleteAll deletes all in memory messages func (memory *InMemory) DeleteAll() error { memory.mu.Lock() + defer memory.mu.Unlock() memory.Messages = make([]*data.Message, 0) memory.MessageIDIndex = make(map[string]int) - memory.mu.Unlock() return nil }