std::bad_alloc при вставке в std::unordered_map?

Иногда я получаю std::bad_alloc для следующего кода:

typedef std::unordered_map<chash, block_extended_info> map_type;
map_type m_foo; 

// the transgressor:
auto r = m_foo.insert(map_type::value_type(id, bei));

Иногда это происходит при выполнении тестовых случаев, в которых используется метод, содержащий эту строку, а в других случаях этого не происходит. Я изменил код для использования m_foo[id] = bei, что привело к тому, что это перестало происходить для одной компиляции, но затем, когда я перекомпилировал снова, это не удалось, поэтому я изменил его на вышеприведенный, который продолжал давать сбой. Очевидно, что проблема глубже, чем это.

Я вполне уверен, что это не вопрос нехватки памяти, так как у меня есть top работает во время выполнения тестовых случаев, и он почти не заполняет память.

Что может быть причиной std::bad_alloc? Что в деталях chash а также block_extended_info может быть причиной этого? Объекты этих типов копируются и передаются повсюду в других частях кода, и это не вызывает никаких проблем.

Вот определение chash:

class chash {
  char data[32];
};

И вот все, что требуется для определения block_extended_info:

class csignature {
  chash c, r;
};
enum foob { /* ... */ };
class ct {
  uint64_t a, b;
  foob c;
};
typedef boost::variant< /* structs with ints, maps, vectors of ints */ > txin_v;
typedef boost::variant< /* similar structs */ > txout_target_v;
struct tx_out {
    uint64_t a;
    txout_target_v b;
};
class transaction_prefix
{
public:
  size_t   version;
  uint64_t unlock_time;
  std::vector<uint8_t> extra;

protected:
  std::vector<txin_v> a;
  std::vector<tx_out> b;
  std::vector<ct> c;
  std::vector<ct> d;
};
class transaction: public transaction_prefix
{
public:
  std::vector<std::vector<csignature> > signatures;
};
struct block_header
{
  uint8_t  major_version;
  uint8_t  minor_version;
  uint64_t timestamp;
  chash    prev_id;
  uint32_t nonce;
};
struct block: public block_header
{
  transaction miner_tx;
  std::vector<chash> tx_hashes;
  uint16_t food_id;
  csignature food_sig;
};
struct block_extended_info
{
  block    bl;
  uint64_t height;
  size_t   block_cumulative_size;
  uint64_t cumulative_difficulty;
  uint64_t already_generated_coins;
};

Обходя соответствующую область в GDB, вот несколько последних строк, которые ведут к bad_alloc:

cryptonote::transaction::transaction(cryptonote::transaction const&) () at /usr/include/c++/4.8/bits/stl_vector.h:313
313       { this->_M_impl._M_finish =
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
91      : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
168       { return __n != 0 ? _M_impl.allocate(__n) : 0; }
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
181     this->_M_impl._M_start = this->_M_allocate(__n);
182     this->_M_impl._M_finish = this->_M_impl._M_start;
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
310       vector(const vector& __x)
117     __uninit_copy(__first, __last, __result);
cryptonote::tx_out* std::__uninitialized_copy<false>::__uninit_copy<__gnu_cxx::__normal_iterator<cryptonote::tx_out const*, std::vector<cryptonote::tx_out, std::allocator<cryptonote::tx_out> > >, cryptonote::tx_out*>(__gnu_cxx::__normal_iterator<cryptonote::tx_out const*, std::vector<cryptonote::tx_out, std::allocator<cryptonote::tx_out> > >, __gnu_cxx::__normal_iterator<cryptonote::tx_out const*, std::vector<cryptonote::tx_out, std::allocator<cryptonote::tx_out> > >, cryptonote::tx_out*) ()
    at /usr/include/c++/4.8/bits/stl_uninitialized.h:68
68          __uninit_copy(_InputIterator __first, _InputIterator __last,
74            for (; __first != __last; ++__first, ++__cur)
83      }
cryptonote::transaction::transaction(cryptonote::transaction const&) () at /usr/include/c++/4.8/bits/stl_vector.h:313
313       { this->_M_impl._M_finish =
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
91      : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
168       { return __n != 0 ? _M_impl.allocate(__n) : 0; }
101     if (__n > this->max_size())
104     return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
181     this->_M_impl._M_start = this->_M_allocate(__n);
182     this->_M_impl._M_finish = this->_M_impl._M_start;
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
310       vector(const vector& __x)
74            for (; __first != __last; ++__first, ++__cur)
71        _ForwardIterator __cur = __result;
75      { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
748     ++_M_current;
74            for (; __first != __last; ++__first, ++__cur)
313       { this->_M_impl._M_finish =
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
91      : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
168       { return __n != 0 ? _M_impl.allocate(__n) : 0; }
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
181     this->_M_impl._M_start = this->_M_allocate(__n);
182     this->_M_impl._M_finish = this->_M_impl._M_start;
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
310       vector(const vector& __x)
74            for (; __first != __last; ++__first, ++__cur)
570   class transaction: public transaction_prefix
313       { this->_M_impl._M_finish =
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
91      : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
168       { return __n != 0 ? _M_impl.allocate(__n) : 0; }
101     if (__n > this->max_size())
104     return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
181     this->_M_impl._M_start = this->_M_allocate(__n);
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
181     this->_M_impl._M_start = this->_M_allocate(__n);
182     this->_M_impl._M_finish = this->_M_impl._M_start;
183     this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
310       vector(const vector& __x)
74            for (; __first != __last; ++__first, ++__cur)
101     if (__n > this->max_size())
75      { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
646       { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
91      : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
168       { return __n != 0 ? _M_impl.allocate(__n) : 0; }
101     if (__n > this->max_size())
102       std::__throw_bad_alloc();

1 ответ

Решение

Как и предполагал π wasντα ῥεῖ, проблема была в другом месте.

Код, который я перечислил, был в функции под названием add_block_as_invalid(...), Сайт вызова выглядел примерно так:

// parameter, iterators into m_alternative_chains
std::list<blocks_ext_by_hash::iterator>& alt_chain; 

for(auto alt_ch_iter = alt_chain.begin(); 
    alt_ch_iter != alt_chain.end(); 
    alt_ch_iter++) 
{
    // ....
    auto ch_ent = *alt_ch_iter;
    // ....
    m_alternative_chains.erase(ch_ent);

    for(auto alt_ch_to_orph_iter = ++alt_ch_iter; 
        alt_ch_to_orph_iter != alt_chain.end(); 
        alt_ch_to_orph_iter++) 
    {
        add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
        m_alternative_chains.erase(*alt_ch_to_orph_iter);
    }
}

Призыв к add_block_as_invalid продолжал разыменование alt_ch_iter, который уже был стерт.

Другие вопросы по тегам