-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
py/map: Make map rehashing an atomic operation. #11604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Code size report:
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #11604 +/- ##
=======================================
Coverage 98.36% 98.36%
=======================================
Files 161 161
Lines 21089 21089
=======================================
Hits 20744 20744
Misses 345 345 ☔ View full report in Codecov by Sentry. |
Great idea! Worth noting that this isn't just important for hard IRQs but also GIL-free multi-core (on e.g. rp2) where the problem is much more likely to occur. |
Unfortunately from further discussion with @dpgeorge about this, I'm not sure this solves the problem. Here are two scenarios:
Also worth pointing out -- if two threads (or thread + hard IRQ) do simultaneous modifications to a map, it is fine only if they are both updating existing keys. In other words, any operation that mutates the set of keys (insert, remove) is not concurrent safe. For the insert at least, you probably shouldn't be allowed to do this in a hard IRQ because there's a chance it might trigger a re-hash anyway (which would fail to allocate). So for the hard IRQ case at least, we could consider preventing any mutation that isn't an update to an existing key. For the non-GIL threading case, it seems reasonable that two threads shouldn't be allowed to concurrently mutate a dictionary's keys. Enforcing that without a mutex seems hard though. I feel that conceptually the globals dict and, say, the locals dict of a class or an actual dict object in users code should have different rules. Maybe in non-GIL threading mode, we need to do special protection of just globals dicts? CC @peterhinch |
The test I was able to fix that unix case by adding a mutex that is obtained during all map operations. That's obviously not efficient but at least shows the problem and how it could be fixed.
Yes, I think that would be a reasonable way to solve this problem, to somehow make globals dicts (the dict of the top level of a module) special and support concurrent access. Maybe the map can internally have both a read-only and write-only table and then the rehash scheme is:
|
e25fe94
to
3102154
Compare
I added a test which does concurrent globals access and fails easily on the unix port with non-GIL threading. |
Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
3102154
to
d5a023b
Compare
Alternative idea for a fix:
|
This simple change to map rehashing makes it so the old map is kept intact during the rehash, and only after the rehash is complete is the old map updated to the new one. And this update is a very quick memory copy, which is done inside an atomic section.
This means that hard IRQs handlers can access dict members (eg globals) even during the update (add/del) of that dict.
See related #10620.