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.

Reply via email to