Does the Golang MGO module lock or unlock Go objects?
Clash 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.
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.
thank you very much for the detailed answer and suggesting best practices! :)
– Semih Sezer
Jun 1 at 16:00