I have encountered a SEGV in gcc 4.7.2's unordered_map
in find()
it calls _M_find_node
, which in turn calls _M_find_before_node
, passing in the bucket number , the key we're searching for, and the hash code .
In _M_find_before_node
, it looks up the first node in the bucket in question:
_BaseNode* __prev_p = _M_buckets[__n];
and then gets the node following this:
_Node* __p = static_cast<_Node*>(__prev_p->_M_nxt);
The problem is that __prev_p->_M_nxt
is null; and _M_equals
tries to dereference it and causes the seg fault.
I'm not 100% clued up on the inner workings of unordered_map
- is it a requirement that the first node in a bucket's _M_nxt
be non-null, or is this a bug?
The code in question is here:
// Find the node whose key compares equal to k in the bucket n. Return nullptr
// if no node is found.
template<typename _Key, typename _Value,
typename _Allocator, typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
bool __chc, bool __cit, bool __uk>
typename _Hashtable<_Key, _Value, _Allocator, _ExtractKey,
_Equal, _H1, _H2, _Hash, _RehashPolicy,
__chc, __cit, __uk>::_BaseNode*
_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal,
_H1, _H2, _Hash, _RehashPolicy, __chc, __cit, __uk>::
_M_find_before_node(size_type __n, const key_type& __k,
typename _Hashtable::_Hash_code_type __code) const
{
_BaseNode* __prev_p = _M_buckets[__n];
if (!__prev_p)
return nullptr;
_Node* __p = static_cast<_Node*>(__prev_p->_M_nxt); // __p is null here!!
for (;; __p = __p->_M_next())
{
if (this->_M_equals(__k, __code, __p))
return __prev_p;
if (!(__p->_M_nxt) || _M_bucket_index(__p->_M_next()) != __n)
break;
__prev_p = __p;
}
return nullptr;
}
I'm not 100% clued up on the inner workings of
unordered_map
- is it a requirement that the first node in a bucket's_M_nxt
be non-null, or is this a bug?
The question is obviously specific to GCC's implementation, but I'm pretty sure that if _M_buckets[__n]
is non-null then _M_buckets[__n]->_M_nxt
should be non-null too.
ie if the bucket is empty then _M_buckets[__n]==nullptr
, if the bucket is non-empty then _M_buckets[__n]->_M_nxt
is the first node in the bucket.
Try building with -D_GLIBCXX_DEBUG
and see if it identifies a problem with your code, it's possible there's a bug but it's more likely you've corrupted the container somehow or are using it wrong.
Question is rather old now, but I step on the same issue lately and here is sample code how to reproduce it.
#include <chrono>
#include <iostream>
#include <thread>
#include <unordered_map>
int main()
{
std::unordered_map< std::string, int > m_Map{};
m_Map.insert(std::make_pair("a", 0x61));
auto count{1000u};
auto t_remove = std::thread([&m_Map, &count]() {
while (1)
{
m_Map.erase("a");
std::this_thread::sleep_for(std::chrono::nanoseconds(count));
if(count > 10)
{
count-=10;
}
else
{
count = 1000u;
}
m_Map.insert(std::make_pair("a", 0x61));
}
});
while (1)
{
auto it = m_Map.find("a");
if (it != m_Map.end())
{
std::cerr << "Map has a " << it->first << " = " << it->second << "\n";
}
else
{
std::cerr << "Map does not have a \"a\"\n";
}
}
t_remove.join();
return 0;
}
Which results after couple iterations in (gdb):
Thread 1 "find_stress_tes" received signal SIGSEGV, Segmentation fault.
0x000000000040505b in std::__detail::_Equal_helper<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, unsigned long, true>::_S_equals (__eq=..., __extract=..., __k="a", __c=4993892634952068459, __n=0x0)
at /usr/include/c++/5/bits/hashtable_policy.h:1322
1322 { return __c == __n->_M_hash_code && __eq(__k, __extract(__n->_M_v())); }
(gdb) bt
#0 0x000000000040505b in std::__detail::_Equal_helper<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, unsigned long, true>::_S_equals (__eq=..., __extract=..., __k="a", __c=4993892634952068459, __n=0x0)
at /usr/include/c++/5/bits/hashtable_policy.h:1322
#1 0x0000000000404b2a in std::__detail::_Hashtable_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >::_M_equals (this=0x7fffffffdd40, __k="a",
__c=4993892634952068459, __n=0x0) at /usr/include/c++/5/bits/hashtable_policy.h:1704
#2 0x00000000004044ef in std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node (this=0x7fffffffdd40, __n=1,
__k="a", __code=4993892634952068459) at /usr/include/c++/5/bits/hashtable.h:1433
#3 0x0000000000403e50 in std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_node (this=0x7fffffffdd40, __bkt=1, __key="a",
__c=4993892634952068459) at /usr/include/c++/5/bits/hashtable.h:632
#4 0x000000000040392b in std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::find (this=0x7fffffffdd40, __k="a")
at /usr/include/c++/5/bits/hashtable.h:1307
#5 0x0000000000403675 in std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> > >::find (this=0x7fffffffdd40,
__x="a") at /usr/include/c++/5/bits/unordered_map.h:615
#6 0x000000000040184b in main () at ../find_stress_test/main.cpp:40
Reason for that is simple, concurrent access, solution would be synchronization.
I hope that will help someone ;)
Unless you have detected an error in the gcc std::unorderd_map
implementation, the most likely cause of your error is that you did something like:
std::unorderd_map<MyKey, MyValue> my_map;
auto it = my_map.find(some_key); // if some_key was not found, it == my_map.end()
do something with *it; // kaboom! derefence of past-the-end iterator
If that's the case, replace it with
if (it != my_map.end()) {
do something with *it;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.