Does the Golang MGO module lock or unlock Go objects?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Does the Golang MGO module lock or unlock Go objects?



I am trying to understand whether Mongo locks Go objects.



The first function works fine with the json encoder, however the second function fails fatal error: sync: Unlock of unlocked RWMutex. Is this because mongo.Find is already trying to lock/unlock the state object? Do I need to externally handle race competition for my go objects or does MGO take care of it? I tried reading the source code but I haven't been able to reach a conclusion.


fatal error: sync: Unlock of unlocked RWMutex



Any would be much appreciated!


import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"io"
"sync"
"encoding/json"
)

type ApplicationState struct
FileStates map[string]FileState `json:"fileStates" bson:"fileStates"`
lock sync.RWMutex `json:"-" bson:"-"`


func (state *ApplicationState) ReadState(reader io.Reader) error
state.lock.Lock()
defer state.lock.Unlock()
return json.NewDecoder(reader).Decode(state)

func (state *ApplicationState) ReadStateMGO(c *mgo.Collection) error
state.lock.Lock()
defer state.lock.Unlock()
return c.Find( bson.M ).Select( bson.M"_id": 0 ).One(state)



Note: to test it you can just replace the Filestate field with a string map.




1 Answer
1



First, drop gopkg.in/mgo.v2, it's obsolte, unmaintained. Instead use the community supported fork: github.com/globalsign/mgo.


gopkg.in/mgo.v2


github.com/globalsign/mgo



Next, you should first read the public, package documentation, and only "revert" to reading the source to clear such things up if the documentation does not offer the answers. But drawing conclusions from the source is always dangerous, as the implementation may change at any time, only what is documented is guaranteed. Documentation of mgo.Session states:


mgo.Session



All Session methods are concurrency-safe and may be called from multiple goroutines.



This is all the guarantee you have, and all you should depend on. Using methods of mgo.Collection may or may not be safe for concurrent use, so do not do that. When needed, always obtain a "new" collection from the session, as that is safe to be accessed from multiple goroutines.


mgo.Collection



And now on to your actual problem.



Your ApplicationState struct type contains a lock (sync.RWMutex), and you unmarshal your query result into the same ApplicationState value that holds the lock:


ApplicationState


sync.RWMutex


ApplicationState


func (state *ApplicationState) ReadStateMGO(c *mgo.Collection) error
state.lock.Lock()
defer state.lock.Unlock()
return c.Find( bson.M ).Select( bson.M"_id": 0 ).One(state)



This is a red flag! Don't do this! Unmarshaling into a value may clear any fields, including the lock!



Yes, you may say it's an unexported field, so the mgo package should not / could not change it. This is true, but the mgo package may decide to create a new ApplicationState value to unmarshal into, and the new value may be assigned to the value pointed by the passed pointer (state).


mgo


mgo


ApplicationState


state



If this happens, the newly created ApplicationState will have a lock field being its zero-value, which is an unlocked mutex. And once this happens, the subsequent unlock will obviously fail (panic).


ApplicationState


lock



Solution? Move locks outside of struct values you intend to serialize / deserialize. Or at least don't expect the lock state to transfer along with other fields.





thank you very much for the detailed answer and suggesting best practices! :)
– Semih Sezer
Jun 1 at 16:00






another question, is a lock even needed in this case? Would Select(..) or Upsert() lock the state/result variable until the operation is completed? I am using multi-threading
– Semih Sezer
Jun 1 at 21:34






@SemihSezer No, they don't lock the value they unmarshal into. If you want to read/write the same value from multiple goroutines concurrently, you need to synchronize them yourself.
– icza
Jun 2 at 6:10






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard