diff --git a/v3/syncorderedmap.go b/v3/syncorderedmap.go new file mode 100644 index 0000000..e03df51 --- /dev/null +++ b/v3/syncorderedmap.go @@ -0,0 +1,79 @@ +package orderedmap + +import "sync" + +type SyncOrderedMap[K comparable, V any] struct { + OrderedMap[K, V] + sync.RWMutex +} + +func NewSyncOrderedMap[K comparable, V any]() *SyncOrderedMap[K, V] { + return &SyncOrderedMap[K, V]{*NewOrderedMap[K, V](), sync.RWMutex{}} +} + +func NewSyncOrderedMapWithCapacity[K comparable, V any](capacity int) *SyncOrderedMap[K, V] { + return &SyncOrderedMap[K, V]{*NewOrderedMapWithCapacity[K, V](capacity), sync.RWMutex{}} +} + +func (m *SyncOrderedMap[K, V]) Get(key K) (value V, ok bool) { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.Get(key) +} + +func (m *SyncOrderedMap[K, V]) Set(key K, value V) bool { + m.Lock() + defer m.Unlock() + + return m.OrderedMap.Set(key, value) +} + +func (m *SyncOrderedMap[K, V]) ReplaceKey(originalKey, newKey K) bool { + m.Lock() + defer m.Unlock() + + return m.OrderedMap.ReplaceKey(originalKey, newKey) +} + +func (m *SyncOrderedMap[K, V]) GetOrDefault(key K, defaultValue V) V { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.GetOrDefault(key, defaultValue) +} + +func (m *SyncOrderedMap[K, V]) GetElement(key K) *Element[K, V] { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.GetElement(key) +} + +func (m *SyncOrderedMap[K, V]) Len() int { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.Len() +} + +func (m *SyncOrderedMap[K, V]) Delete(key K) (didDelete bool) { + m.Lock() + defer m.Unlock() + + return m.OrderedMap.Delete(key) +} + +func (m *SyncOrderedMap[K, V]) Copy() *OrderedMap[K, V] { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.Copy() +} + +func (m *SyncOrderedMap[K, V]) Has(key K) bool { + m.RLock() + defer m.RUnlock() + + return m.OrderedMap.Has(key) +} diff --git a/v3/syncorderedmap_test.go b/v3/syncorderedmap_test.go new file mode 100644 index 0000000..bed8577 --- /dev/null +++ b/v3/syncorderedmap_test.go @@ -0,0 +1,149 @@ +package orderedmap_test + +import ( + "fmt" + "math/rand" + "sync" + "testing" + + "github.com/elliotchance/orderedmap/v3" +) + +func TestRaceCondition(t *testing.T) { + m := orderedmap.NewSyncOrderedMap[int, int]() + wg := &sync.WaitGroup{} + + var asyncGet = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + m.Get(key) + wg.Done() + }() + } + + var asyncSet = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + value := rand.Intn(100) + m.Set(key, value) + wg.Done() + }() + } + + var asyncDelete = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + m.Delete(key) + wg.Done() + }() + } + + var asyncHas = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + m.Has(key) + wg.Done() + }() + } + + var asyncReplaceKEy = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + newKey := rand.Intn(100) + m.ReplaceKey(key, newKey) + wg.Done() + }() + } + + var asyncGetOrDefault = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + def := rand.Intn(100) + m.GetOrDefault(key, def) + wg.Done() + }() + } + + var asyncLen = func() { + wg.Add(1) + go func() { + m.Len() + wg.Done() + }() + } + + var asyncCopy = func() { + wg.Add(1) + go func() { + m.Copy() + wg.Done() + }() + } + + var asyncGetElement = func() { + wg.Add(1) + go func() { + key := rand.Intn(100) + e := m.GetElement(key) + if e != nil { + m.RLock() + _ = e.Value + m.RUnlock() + } + wg.Done() + }() + } + + for i := 0; i < 10000; i++ { + asyncSet() + asyncGet() + asyncDelete() + asyncHas() + asyncLen() + asyncReplaceKEy() + asyncGetOrDefault() + asyncCopy() + asyncGetElement() + } + + wg.Wait() + fmt.Println("TestRaceCondition completed") + fmt.Printf("SyncOrderedMap eventually has %v elements\n", m.Len()) +} + +func TestSyncOrderedMapMutex(t *testing.T) { + m := orderedmap.NewSyncOrderedMap[int, string]() + + for i := 0; i < 1000; i++ { + m.Set(i, fmt.Sprintf("value-%v", i)) + } + + stop := make(chan struct{}) + go func() { + i := 0 + for { + select { + case <-stop: + return + + default: + m.Set(i, fmt.Sprintf("new-value-%v", i)) + i++ + } + } + }() + + m.Lock() + for key := range m.Keys() { + m.OrderedMap.Get(key) + } + m.Unlock() + + stop <- struct{}{} +}