Repository navigation
Conversation
- Misc fixes to hash_map.
|
New Existing |
I feel that this one is going to hide bugs and generate surprise behavior that is hard to reproduce:
It may be memory safe, but not application safe? Instead what if we keep a count of iterators active. If an iterator is active then no new inserts are allowed. Removals are ok, but rehashes only occur when there are no iterators active, or if the iterator count goes to zero. |
|
I think that's a reasonable approach. But as far as I can tell, every option has its poison:
(FWIW, C++ Those are the options that I can see. Are there any others? |
|
Additional orthogonal idea: introduce a The naming is a little funny, since you want the user to pick the right one. We could call the two iteration types:
|
|
Checking unordered_map insert, it returns an iterator to the inserted element, so the responsible thing to do would be to handle the rehash in the app, which sounds leaky. Currently we have hash_table_keys_array that allows the more stable iterate. My proposal would be:
I think this eliminates all surprising behaviors? |
|
In general I would say to only allow removal of the current element, and do not allow inserts during iteration because otherwise keys would be treated differently and that sound ugly. Some keys would be considered in the same iteration that they got inserted, or some keys may be deleted after they were already considered by the iteration. |
Agreed. Will fatal on insert, and then we have to see if this breaks any existing code...
Agreed.
Agreed.
I think delete of any item is easy to make safe under open addressing. |
|
Contrived but this is the idea: If limited to the current position of the cursor an error in counting at least it would be my fault. |
|
That's a good example. I think our challenges may lie more in our patterns of usage than the data structure itself. Or rather, we need to set up good constraints to flush out the bad habits that we have now. (Or may have in the future.) |
|
Ok, that is good example. I think part of our challenge here is distinguishing between problems with the data structure vs problems in our usage patterns. |
Proposed Changes
Our current hash_table has evolved over a number of years and revealed some weaknesses in semantics around the (single) iterator and the closed-addressing linked list implementation. While it can be used correctly, it is tricky to avoid the disallowed patterns (nested iteration, deletion during iteration, etc) that may cause further trouble down the road.
This is a first pass at improving the data structure, with some slight API changes. For the sake of this WIP, I am calling the new one
hash_mapbut it could be renamedhash_tablein the future. Key differences:HASH_MAP_ITERATE(likelist.h) operate using a simple integer position, allowing for nested iteration without confusion. The obvious thing works correctly.hash_mapkeeps an internal version number which is updated on each rehash. If a cursor notices a version change, it immediately goes to the invalid position, resulting in a truncated but still safe iteration over values.hash_map_insertandhash_map_removecan be safely performed while cursors are active. However, if either triggers a rehash, cursors jump to invalid, so the iteration ends safely but prematurely. This may be surprising behavior.hash_map_remove_stableremoves an item but suppresses the rehash, guaranteeing cursor stability. This one should be used inside an iteration.I'm sure that I haven't sorted out all the implementation bugs yet. And there are also some needed features to bring over from
hash_table, like random iteration initialization. Also some documentation to be added. Would like feedback on the usability of this approach.Merge Checklist
The following items must be completed before PRs can be merged.
Check these off to verify you have completed all steps.
make testRun local tests prior to pushing.make formatFormat source code to comply with lint policies. Note that some lint errors can only be resolved manually (e.g., Python)make lintRun lint on source code prior to pushing.