I have a map (entities) where the key is a string and the value is a struct. Building that struct is an expensive operation because it has to go to the database to load the data.
type entityStateManagers struct {
entities map[string]*entityStateManager
mainLock *sync.Mutex
locks map[string]*sync.Mutex
}
The function below returns that struct for the provided key. If the struct is not in memory it will load it from the database.
I didn't want to have only one lock for everything because that would completely block access to the map while expensiveLoad(entityId) is running.
I tried having a separate struct (locks) with one lock per map key. With that, only access to entity_123 will have to wait for expensiveLoad(entity_123), for example.
I still have to do more testing, but it seems to be working.
func (handler entityStateManagers) getStateManager(entityId string) (*entityStateManager, error) {
handler.mainLock.Lock()
if handler.locks[entityId] == nil {
handler.locks[entityId] = &sync.Mutex{}
}
handler.mainLock.Unlock()
handler.locks[entityId].Lock()
defer handler.locks[entityId].Unlock()
if handler.entities[entityId] == nil {
handler.entities[entityId] = expensiveLoad(entityId)
}
return handler.entities[entityId], nil
}
Is this a reasonable approach? Is there something I am missing? Is there a better way for doing that?
i would say like this but i can be wrong, keeping two maps with same keys is useles, also pointers to mutexes are ehh.
type Wrapper struct {
*entityStateManager
sync.Mutex
}
type entityStateManagers struct {
entities map[string]*Wrapper
mainLock sync.Mutex
}
func (handler entityStateManagers) getStateManager(entityId string) (*entityStateManager, error) {
handler.mainLock.Lock()
val, ok := handler.entities[entityId]
if !ok {
val = &Wrapper{}
handler.entities[entityId] = val
}
val.Lock()
handler.mainLock.Unlock()
val.entityStateManager = expensiveLoad(entityId)
defer val.Unlock()
return val.entityStateManager, nil
}
Related
Some starter code goes here,
func (chm *ConcurrentHashMap) NFetchWorker() {
for {
key := <-NFetchWorkerPipe
chm.mu.RLock()
data := chm.data[string(key)]
chm.mu.RUnlock()
if data.IsUsingNFetch {
chm.mu.Lock()
*(chm.data[string(key)].NFetch)--
chm.mu.Unlock()
}
}
}
go NFetchWorker()
Struct ConcurrentHashMap looks like this,
type ConcurrentHashMap struct {
data map[string]DataBlock
mu sync.RWMutex
}
Struct DataBlock looks like this,
type DataBlock struct {
...
NFetch *int32
IsUsingNFetch bool
...
}
Now when I try to run tests with race flag enabled. I get,
Write at 0x00c00012e310 by goroutine 8:
(*ConcurrentHashMap).NFetchWorker()
The line numbers point to this line.
*(chm.data[string(key)].NFetch)--
However I don't face this issue when I do not use pointer in DataBlock.NFetch.
But if I do that I lose the ability to make changes to NFetch directly from map without reassigning a whole new struct object to that hash in map, which would be relatively computationally expensive. I want to change the value of NFetch without reassigning the whole struct again for one small change while being free from DATA RACE. Any solutions??
Fairly New to Golang, I could be doing something really stupid here, feel free to point it out.
UPDATE: Adding Read and Write Op function
func (chm *ConcurrentHashMap) Get(key Key) Value {
chm.mu.RLock()
fetch, ok := chm.data[string(key)]
chm.mu.RUnlock()
if ok {
if CheckValueValidity(&fetch) {
NFetchWorkerPipe <- key
return fetch.Value
} else {
chm.mu.Lock()
delete(chm.data, string(key))
chm.mu.Unlock()
}
}
return constants.NIL
}
Write Op
func (chm *ConcurrentHashMap) Insert(key Key, value Value, options Options) {
...
chm.mu.Lock()
chm.data[string(key)] = Block{
Value: value,
NFetch: nFetchOld,
Expiry: time.Now().Add(delay),
IsUsingNFetch: foundNFetch,
IsUsingExpiry: foundTTL,
mu: mutex,
}
chm.mu.Unlock()
}
The problem is in the CheckValueValidity where you are accessing NFetch without locking it. Pointers should never be accessed without locking them.
func CheckValueValidity(value *Block) bool {
if value.IsUsingExpiry && !value.Expiry.After(time.Now()) {
return false
}
if value.IsUsingNFetch && *(value.NFetch) <= 0 {
return false
}
return true
}
This code should work
func (chm *ConcurrentHashMap) Get(key Key) Value {
chm.mu.RLock()
fetch, ok := chm.data[string(key)]
isValid := CheckValueValidity(&fetch)
chm.mu.RUnlock()
if ok {
if isValid {
NFetchWorkerPipe <- key
return fetch.Value
} else {
chm.mu.Lock()
delete(chm.data, string(key))
chm.mu.Unlock()
}
}
return constants.NIL
}
I am currently learning Go.
Following this link. On initCache function it accepts a evictionAlgo type parameter without * prefix so meaning it's not the pointer. See below the code I am referring.
type cache struct {
storage map[string]string
evictionAlgo evictionAlgo
capacity int
maxCapacity int
}
func initCache(e evictionAlgo) *cache {
storage := make(map[string]string)
return &cache{
storage: storage,
evictionAlgo: e,
capacity: 0,
maxCapacity: 2,
}
}
I got really confused. So my question is when or what scenario to consider when using a dereference pointer? And when should I not use dereference.
From my experience, you only have two situations where you would use a pointer as input or output. For output, I don't think it's ever really needed. My use case for it, it just as a convenience. Normally if you are returning an empty struct, you might do it like this:
package user
import "os"
type cache struct { dir string }
func newCache() (cache, error) {
c, e := os.UserCacheDir()
if e != nil {
return cache{}, e
}
return cache{c}, nil
}
but if you instead change to pointer, then you can use nil for the zero value:
func newCache() (*cache, error) {
c, e := os.UserCacheDir()
if e != nil { return nil, e }
return &cache{c}, nil
}
Regarding input, you should only need a pointer, if you need to change a field:
package user
type cache struct { dir string }
func (c *cache) set() {
c.dir = "north"
}
I thought you could change a slice or map field without a pointer receiver, but upon trying it only seems to work with pointer receiver as well.
I have a struct that is shared across many goroutines, and I would like to reduce the number of times that one field on the struct is computed, if possible. Probably easier shown:
type Container struct {
Items []Item
mu sync.RWMutex
}
(container *Container) func loadContainer() {
if container.Items != nil { // This is detected as a race condition by go
return
}
container.mu.Lock()
container.Items = loadItems() // Does some logic that I would like to avoid repeating
container.mu.Unlock()
}
Is there a safe way to accomplish this? It's almost like I want a race condition, where I don't mind if it's written to multiple times, but the first thread to do so should prevent subsequent reads from doing so. Pretty new to go and concurrency in general.
In your approach you would have to lock first before you can check if Items has been set. E.g.:
func (container *Container) loadContainer() {
container.mu.Lock()
defer container.mu.Unlock()
if container.Items != nil {
return
}
container.Items = loadItems()
}
But sync.Once provides a better and faster alternative. Also it would be wise to hide the Items field so it cannot accidentally be referred to. An exported function should take care of initialization, and return the value, initialized once only:
type Container struct {
once sync.Once
items []Item
}
func (container *Container) GetItems() []Item {
container.once.Do(func() {
container.Items = loadItems()
})
return container.items
}
I'm writing a net/http server. My request handlers need access to some shared data that is updated by another go-routine. This go-routine keeps a single struct updated, and all of the net/http handlers need access to the same struct.
type MyData struct {
Expiration time.Time
A string
B string
C string
}
func update(data MyData) {
go func() {
for {
if data.Expiration >= time.Now() {
// set A, B, C
}
// sleep
}
}()
}
What's the best way to make a single, common MyData available to other go-routines? I was thinking a channel, but every reader of the channel should get the same MyData. The problem is that reading from a channel pops the item off the channel. I guess I'm looking for how to Peek() on the reader side, and Drain() on the writer side when it's time to update.
Mutex based solution
type MyData struct {
Expiration time.Time
A string
B string
C string
}
type MutexedMyData struct {
sync.RWMutex
MyData MyData
}
var m = &MutexedMyData{}
func (m *MutexedMyData) Update(my_data MyData) {
m.Lock()
m.MyData = my_data
m.Unlock()
}
func (m *MutexedMyData) Read() MyData {
u.RLock()
value := m.MyData
u.RUnlock()
return value
}
Below I have an example of one structure which embeds another. I'm trying to figure out how to pass the more specific structure pointer to be stored in a less specific one. You can think of it as a collection. Wrapping in an interface doesn't seem to work, as doing so would make a copy, which isn't valid for structs with locks. Ideas?
package stackoverflow
import "sync"
type CoolerThingWithLock struct {
fancyStuff string
ThingWithLock
}
func NewCoolerThingWithLock() *CoolerThingWithLock {
coolerThingWithLock := &CoolerThingWithLock{}
coolerThingWithLock.InitThingWithLock()
return coolerThingWithLock
}
type ThingWithLock struct {
value int
lock sync.Mutex
children []*ThingWithLock
}
func (thingWithLock *ThingWithLock) InitThingWithLock() {
thingWithLock.children = make([]*ThingWithLock, 0)
}
func NewThingWithLock() *ThingWithLock {
newThingWithLock := &ThingWithLock{}
newThingWithLock.InitThingWithLock()
return newThingWithLock
}
func (thingWithLock *ThingWithLock) AddChild(newChild *ThingWithLock) {
thingWithLock.children = append(thingWithLock.children, newChild)
}
func (thingWithLock *ThingWithLock) SetValue(newValue int) {
thingWithLock.lock.Lock()
defer thingWithLock.lock.Unlock()
thingWithLock.value = newValue
for _, child := range thingWithLock.children {
child.SetValue(newValue)
}
}
func main() {
thingOne := NewThingWithLock()
thingTwo := NewCoolerThingWithLock()
thingOne.AddChild(thingTwo)
thingOne.SetValue(42)
}
Error: cannot use thingTwo (type *CoolerThingWithLock) as type
*ThingWithLock in argument to thingOne.AddChild
It's impossible to store the wrapping type in []*ThignWithLock since go has no notion of structural subtyping.
Your assertion that an interface will result in copying is incorrect, and you can get the desired effect by doing:
type InterfaceOfThingThatParticipatesInAHierarchy interface {
AddChild(InterfaceOfThingThatParticipatesInAHierarchy)
SetValue(int)
}
type ThingWithLock struct {
...
children []InterfaceOfThingThatParticipatesInAHierarchy
}
func (thingWithLock *ThingWithLock) AddChild(newChild InterfaceOfThingThatParticipatesInAHierarchy) { ... }
As long as the interface is implemented on a *ThingWithLock and not ThingWithLock, there will be no copying of the receiver struct itself, only the pointer to the struct will be copied on the stack.