spec: clarify range over maps will produce every non-deleted entry avaiable when it starts #35239
Labels
Documentation
NeedsDecision
Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone
What version of Go are you using (
go version
)?N/A, but 1.13
Does this issue reproduce with the latest release?
Sure.
What operating system and processor architecture are you using (
go env
)?N/A
What did you do?
https://play.golang.org/p/jX-Bp63JDHX
What did you expect to see?
I have no idea. This is a spec question, really. I'm writing it because I brought this up in a couple of places and ended up with a significant range of intuitions and answers from experienced and competent developers, ranging from "that will work" to "that won't work" to "that might work". I think it should perhaps be clearer whether or not this is "safe" -- could this code produce a race condition or concurrency failure?
If you have a range loop on a map, and unsynchronized writes to the map, that's a clear violation -- you can't read the map while it's being written to.
But let's say, hypothetically, that you want to range over a map slowly -- say, for a low priority background cleanup task. You don't want to run the entire thing with a lock held preventing the map from being updated. You could, of course, lock the map, grab a slice of keys, unlock it, and iterate the keys.
But why would you do something sane like that when you can do something insane?
Enter the example code: Grab a lock, start a range loop, and then inside the body of the loop, release the lock, operate on the k/v pair, and grab the lock again right before resuming at the range statement. Each evaluation of the range statement happens with the lock held. If the same lock controls modifications to the map, that's... maybe... safe?
I can't tell whether it's safe. I think it probably is, not because the spec says so, but because the spec says something that implies it, I think:
This suggests that map entries may be removed or created during iteration, which implies that the range statement is expected to function at least somewhat if there's creation or deletion while it's running. It's less obvious whether the creation or deletion will be reliable. Imagine for instance that we have a map with a hundred items, we iterate through 90 of them, and then we add a thousand more items, causing the entire map to get grown and rehashed at least once. Do I reliably expect to see the other 10 items that were in it before I started? I have no idea. It looks to me like the current implementation is at least trying to handle that case carefully, and indeed, I can't make it fail; I always hit every item that was present when the range statement started and hasn't been deleted yet, but don't necessarily hit any new items.
I can't tell whether I'm safe or (un?)lucky.
The text was updated successfully, but these errors were encountered: