You can compile/run with -race : https://golang.org/doc/articles/race_detector.html
On Saturday, January 7, 2017 at 11:59:40 PM UTC-8, tsuna wrote: > > On Sat, Jan 7, 2017 at 7:00 PM, 'Keith Randall' via golang-nuts < > golan...@googlegroups.com <javascript:>> wrote: > >> >> >> On Friday, January 6, 2017 at 5:12:54 PM UTC-8, tsuna wrote: >>> >>> Hi there, >>> I have a struct that contains an unexported map protected by a mutex, >>> let’s say something like: >>> >>> type Map struct { >>> mtx sync.Mutex >>> col map[string]interface{} >>> } >>> >>> I want to implement a ForEach method that works in O(1) space and >>> doesn’t hold the mutex while working on each entry. This is what I have: >>> >>> func (m *Map) ForEach(fn func(k, v interface{})) { >>> m.mtx.Lock() >>> for k, v := range m.col { >>> m.mtx.Unlock() >>> fn(k, v) >>> m.mtx.Lock() >>> } >>> m.mtx.Unlock() >>> } >>> >>> (runnable example: https://play.golang.org/p/tX2lCCYWxq) >>> >>> The language spec <https://golang.org/ref/spec#For_statements> says: >>> >>>> 3. The iteration order over maps is not specified and is not guaranteed >>>> to be the same from one iteration to the next. If map entries that have >>>> not >>>> yet been reached are removed during iteration, the corresponding iteration >>>> values will not be produced. If map entries are created during iteration, >>>> that entry may be produced during the iteration or may be skipped. The >>>> choice may vary for each entry created and from one iteration to the next. >>>> If the map is nil, the number of iterations is 0. >>> >>> >>> With the caveats mentioned above regarding concurrent >>> deletions/insertions in mind, is this implementation of ForEach correct? >>> Is there a better way? >>> >>> >> I'm not sure I understand what you're trying to guarantee. >> > > That the code is race-free. The guarantees on data access are pretty > loose – fn() could be called on a key-value pair while it’s being > removed/changed by another goroutine. That’s okay. > > >> The code you've shown looks fine, but the devil is in the details of what >> fn does. >> >> The fundamental constraint is that you need to make sure that no two >> concurrent operations, at least one of which is a write, happen to the >> map. Treat iteration as a read. And it isn't one read extending for the >> whole loop. It is one "instantaneous" read each time a new k,v is assigned. >> So fn can read all it wants. >> If fn writes, those writes must be synchronized so that they do not >> happen concurrently with the ForEach goroutine iterating. >> So if fn writes in the same goroutine that ForEach is running in, that is >> fine. >> If fn spawns other goroutines to write to the map, asks other goroutines >> to write to the map via channels, etc. then those accesses must be properly >> synchronized. >> One way to do that is to use the same lock as you used in ForEach around >> those accesses. >> > > Yes, that’s exactly what’s happening. The contrived example I showed only > included ForEach(), but I also have Get(k) and Set(k, v) methods on this > struct, which can be called concurrently from other goroutines. Of course > they also acquire the same mutex. > > Alright, so thanks for checking this wasn’t insane. The code looks weird, > but it is what it is. > > -- > Benoit "tsuna" Sigoure > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.