Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MutexMap MutexSection/RMutexSection #47

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 119 additions & 41 deletions mutex_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,43 @@ package nex

import "sync"

// RawMap implements a map type with helper functions to operate it.
type RawMap[K comparable, V any] struct {
real map[K]V
}
Comment on lines +5 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be made into a type alias rather than creating a whole struct

Suggested change
// RawMap implements a map type with helper functions to operate it.
type RawMap[K comparable, V any] struct {
real map[K]V
}
// RawMap implements a map type with helper functions to operate it.
type RawMap[K comparable, V any] map[K]V

Then it can just be used like a regular map, how real is being used internally. Though I'm not sure what the benefit of this type is right now, since all it seems to do is wrap a map and then give it helper functions for things Go already supports?

Like why use RawMap.Delete() rather than just using a normal map and the built in delete()?


// MutexMap implements a map type with go routine safe accessors through mutex locks. Embeds sync.RWMutex
type MutexMap[K comparable, V any] struct {
*sync.RWMutex
real map[K]V
mutex *sync.RWMutex
rawMap RawMap[K, V]
}
Comment on lines 10 to 14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer if structs are defined directly before their methods, so this should be moved further down


// Set sets a key to a given value
func (m *MutexMap[K, V]) Set(key K, value V) {
m.Lock()
defer m.Unlock()

func (m *RawMap[K, V]) Set(key K, value V) {
m.real[key] = value
}

// Get returns the given key value and a bool if found
func (m *MutexMap[K, V]) Get(key K) (V, bool) {
m.RLock()
defer m.RUnlock()

func (m *RawMap[K, V]) Get(key K) (V, bool) {
value, ok := m.real[key]

return value, ok
}

// Has checks if a key exists in the map
func (m *MutexMap[K, V]) Has(key K) bool {
m.RLock()
defer m.RUnlock()

func (m *RawMap[K, V]) Has(key K) bool {
_, ok := m.real[key]
return ok
}

// Delete removes a key from the internal map
func (m *MutexMap[K, V]) Delete(key K) {
m.Lock()
defer m.Unlock()

func (m *RawMap[K, V]) Delete(key K) {
delete(m.real, key)
}

// DeleteIf deletes every element if the predicate returns true.
// Returns the amount of elements deleted.
func (m *MutexMap[K, V]) DeleteIf(predicate func(key K, value V) bool) int {
m.Lock()
defer m.Unlock()

func (m *RawMap[K, V]) DeleteIf(predicate func(key K, value V) bool) int {
amount := 0
for key, value := range m.real {
if predicate(key, value) {
Expand All @@ -61,31 +51,22 @@ func (m *MutexMap[K, V]) DeleteIf(predicate func(key K, value V) bool) int {
}

// RunAndDelete runs a callback and removes the key afterwards
func (m *MutexMap[K, V]) RunAndDelete(key K, callback func(key K, value V)) {
m.Lock()
defer m.Unlock()

func (m *RawMap[K, V]) RunAndDelete(key K, callback func(key K, value V)) {
if value, ok := m.real[key]; ok {
callback(key, value)
delete(m.real, key)
}
}

// Size returns the length of the internal map
func (m *MutexMap[K, V]) Size() int {
m.RLock()
defer m.RUnlock()

func (m *RawMap[K, V]) Size() int {
return len(m.real)
}

// Each runs a callback function for every item in the map
// The map should not be modified inside the callback function
// Returns true if the loop was terminated early
func (m *MutexMap[K, V]) Each(callback func(key K, value V) bool) bool {
m.RLock()
defer m.RUnlock()

func (m *RawMap[K, V]) Each(callback func(key K, value V) bool) bool {
for key, value := range m.real {
if callback(key, value) {
return true
Expand All @@ -95,12 +76,9 @@ func (m *MutexMap[K, V]) Each(callback func(key K, value V) bool) bool {
return false
}

// Clear removes all items from the `real` map
// Clear removes all items from the map
// Accepts an optional callback function ran for every item before it is deleted
func (m *MutexMap[K, V]) Clear(callback func(key K, value V)) {
m.Lock()
defer m.Unlock()

func (m *RawMap[K, V]) Clear(callback func(key K, value V)) {
for key, value := range m.real {
if callback != nil {
callback(key, value)
Expand All @@ -109,10 +87,110 @@ func (m *MutexMap[K, V]) Clear(callback func(key K, value V)) {
}
}

// Set sets a key to a given value
func (m *MutexMap[K, V]) Set(key K, value V) {
m.mutex.Lock()
defer m.mutex.Unlock()

m.rawMap.Set(key, value)
}

// Get returns the given key value and a bool if found
func (m *MutexMap[K, V]) Get(key K) (V, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()

value, ok := m.rawMap.Get(key)

return value, ok
}

// Has checks if a key exists in the map
func (m *MutexMap[K, V]) Has(key K) bool {
m.mutex.RLock()
defer m.mutex.RUnlock()

return m.rawMap.Has(key)
}

// Delete removes a key from the internal map
func (m *MutexMap[K, V]) Delete(key K) {
m.mutex.Lock()
defer m.mutex.Unlock()

m.rawMap.Delete(key)
}

// DeleteIf deletes every element if the predicate returns true.
// Returns the amount of elements deleted.
func (m *MutexMap[K, V]) DeleteIf(predicate func(key K, value V) bool) int {
m.mutex.Lock()
defer m.mutex.Unlock()

return m.rawMap.DeleteIf(predicate)
}

// RunAndDelete runs a callback and removes the key afterwards
func (m *MutexMap[K, V]) RunAndDelete(key K, callback func(key K, value V)) {
m.mutex.Lock()
defer m.mutex.Unlock()

m.rawMap.RunAndDelete(key, callback)
}

// Size returns the length of the internal map
func (m *MutexMap[K, V]) Size() int {
m.mutex.RLock()
defer m.mutex.RUnlock()

return m.rawMap.Size()
}

// Each runs a callback function for every item in the map
// The map should not be modified inside the callback function
// Returns true if the loop was terminated early
func (m *MutexMap[K, V]) Each(callback func(key K, value V) bool) bool {
m.mutex.RLock()
defer m.mutex.RUnlock()

return m.rawMap.Each(callback)
}

// Clear removes all items from the map
// Accepts an optional callback function ran for every item before it is deleted
func (m *MutexMap[K, V]) Clear(callback func(key K, value V)) {
m.mutex.Lock()
defer m.mutex.Unlock()

m.rawMap.Clear(callback)
}

// RMutexSection read-locks the map and runs the provided callback. The map will
// be unlocked after the callback returns. Useful for critical sections where
// multiple map operations are needed. Do not perform write operations to the map.
func (m *MutexMap[K, V]) RMutexSection(callback func(rawMap *RawMap[K, V])) {
m.mutex.RLock()
defer m.mutex.RUnlock()

callback(&m.rawMap)
}

// MutexSection write-locks the map and runs the provided callback. The map will
// be unlocked after the callback returns. Useful for critical sections where
// multiple map operations are needed.
func (m *MutexMap[K, V]) MutexSection(callback func(rawMap *RawMap[K, V])) {
m.mutex.Lock()
defer m.mutex.Unlock()

callback(&m.rawMap)
}

// NewMutexMap returns a new instance of MutexMap with the provided key/value types
func NewMutexMap[K comparable, V any]() *MutexMap[K, V] {
return &MutexMap[K, V]{
RWMutex: &sync.RWMutex{},
real: make(map[K]V),
mutex: &sync.RWMutex{},
rawMap: RawMap[K, V]{
real: make(map[K]V),
},
}
}