Skip to content
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

[BUG] ankerl::unordered_dense::erase is not reentrant #126

Closed
pklima opened this issue Oct 2, 2024 · 1 comment
Closed

[BUG] ankerl::unordered_dense::erase is not reentrant #126

pklima opened this issue Oct 2, 2024 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@pklima
Copy link

pklima commented Oct 2, 2024

Describe the bug
If you have a destructor that calls erase on a ankerl::unordered_dense::map that is currently being erased from, the code will enter an infinite loop.

As a workaround extract can be used instead of erase to delay the destructor from running until the initial erase finishes.

To Reproduce
Run the following code:

struct Foo;
using Map = ankerl::unordered_dense::map<int, std::unique_ptr<Foo>>;

struct Foo
{
  ~Foo()
  {
    if (map)
      map->erase(0);
  }

  Map* map{nullptr};
};

Map map;
map.try_emplace(0, std::make_unique<Foo>());
map.try_emplace(1, std::make_unique<Foo>(&map));

map.erase(1); // <--- freeze

Expected behavior
Same as std::unordered_map - doesn't freeze and erases correctly.

System:

  • OS: Windows
  • Compiler: MSVC
  • Version: 19.39

Additional context
Callstack:

ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::next(unsigned int bucket_idx) Line 852	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::do_erase<`ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase'::`2'::<lambda_1>>(unsigned int bucket_idx, ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase::__l2::<lambda_1> handle_erased_value) Line 1038	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::do_erase_key<int const &,`ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase'::`2'::<lambda_1>>(const int & key, ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase::__l2::<lambda_1> handle_erased_value) Line 1063	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase(const int & key) Line 1683	C++
ConsoleApplication1.exe!`main'::`2'::Foo::~Foo() Line 20	C++
ConsoleApplication1.exe!`main'::`2'::Foo::`scalar deleting destructor'(unsigned int)	C++
ConsoleApplication1.exe!std::default_delete<`main'::`2'::Foo>::operator()(main::__l2::Foo * _Ptr) Line 3139	C++
ConsoleApplication1.exe!std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>::~unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>() Line 3251	C++
ConsoleApplication1.exe!std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>::~pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>()	C++
ConsoleApplication1.exe!std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>::`scalar deleting destructor'(unsigned int)	C++
ConsoleApplication1.exe!std::destroy_at<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>(std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>> * const _Location) Line 326	C++
ConsoleApplication1.exe!std::_Default_allocator_traits<std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>>::destroy<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>(std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>> & __formal, std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>> * const _Ptr) Line 738	C++
ConsoleApplication1.exe!std::vector<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>>::pop_back() Line 1721	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::do_erase<`ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase'::`2'::<lambda_1>>(unsigned int bucket_idx, ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase::__l2::<lambda_1> handle_erased_value) Line 1043	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::do_erase_key<int const &,`ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase'::`2'::<lambda_1>>(const int & key, ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase::__l2::<lambda_1> handle_erased_value) Line 1063	C++
ConsoleApplication1.exe!ankerl::unordered_dense::v4_4_0::detail::table<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>,ankerl::unordered_dense::v4_4_0::hash<int,void>,std::equal_to<int>,std::allocator<std::pair<int,std::unique_ptr<`main'::`2'::Foo,std::default_delete<`main'::`2'::Foo>>>>,ankerl::unordered_dense::v4_4_0::bucket_type::standard,0>::erase(const int & key) Line 1683	C++
ConsoleApplication1.exe!main() Line 30	C++
@pklima pklima added the bug Something isn't working label Oct 2, 2024
@martinus
Copy link
Owner

martinus commented Oct 5, 2024

I had a look, that's something that I can't support in the map. Note that as far as I can say it is not officially supported by std as well, and it might randomly work or not. It certainly doesn't work with boost maps.

@martinus martinus closed this as completed Oct 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants