проблема с std :: map std :: find

У меня работает с ++ дэймон. Проблема в том, что демон останавливается один или два раза в месяц. Как вы видите из вывода GDB ниже, это происходит, когда демон ищет unsigned int sessionID в std::map <unsigned int const, SessionID*> контейнер. Я не могу воспроизвести проблему и думаю, что это может быть что-то не так с данными от пользователей (может быть, std::sting cookie_ssid есть некоторые неожиданные данные и после преобразования с strtoulчто-то идет не так. (знаю, что это не правильный способ получить unsigned int )

у меня есть только .core файл после сбоя демона. и увидеть эту проблему в if (!_M_impl._M_key_compare(_S_key(__x), __k)), Есть идеи, как решить эту проблему? Большое спасибо.

Вывод GDB:

#0  std::_Rb_tree<unsigned int, std::pair<unsigned int const, SessionID*>, std::_Select1st<std::pair<unsigned int const, SessionID*> >, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, SessionID*> > >::find (this=0x529df15c, __k=@0x7f5fab7c)
at stl_tree.h:1376
##########
1376            if (!_M_impl._M_key_compare(_S_key(__x), __k))
##########
#0  std::_Rb_tree<unsigned int, std::pair<unsigned int const, SessionID*>, std::_Select1st<std::pair<unsigned int const, SessionID*> >, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, SessionID*> > >::find (
this=0x529df15c, __k=@0x7f5fab7c) at stl_tree.h:1376
#1  0x0805e6be in TR::find_session (this=0x529df110,
cookie_ssid=@0x47ef3614, ptr_to_ptr_session=0x7f5fac7c)
at stl_map.h:542

функция TR::find_session выпущен так:

bool TR::find_session ( const std::string &cookie_ssid, SessionID **ptr_to_ptr_session )
{
unsigned int uint_sessionid = std::strtoul ( cookie_ssid.c_str(),NULL,0);

MUTEX_map_sessionids.lock_reading();
std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.find( uint_sessionid );

if ( it_sessionids != map_sessionids.end() )
{ // exists
*ptr_to_ptr_session = it_sessionids->second;
MUTEX_map_sessionids.unlock();
return true;
}

MUTEX_map_sessionids.unlock();
return false;
}

РЕДАКТИРОВАТЬ
Моя функция очистки, которая работает в отдельном потоке (один раз в минуту или 5 минут). По запросу в комментариях. Я не уверен с этой функцией. Может быть, это глючит ….

void TR::cleanup_sessions () // not protected from multithread using! used only at one thread
{
std::list<SessionID*> list_to_clean; // tmplary store sessions to delete

MUTEX_map_sessionids.lock_reading();
std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.begin();
MUTEX_map_sessionids.unlock();

while ( true )
{
MUTEX_map_sessionids.lock_writing();
if (it_sessionids == map_sessionids.end() )
{
MUTEX_map_sessionids.unlock();
break;
}

SessionID *ptr_sessionid = it_sessionids->second;

time_t secondsnow = time (NULL);

ptr_sessionid->MUTEX_all_session.lock_reading();
time_t lastaccesstime = ptr_sessionid->last_access_time;
size_t total_showed = ptr_sessionid->map_showed.size();
ptr_sessionid->MUTEX_all_session.unlock();if ( lastaccesstime and secondsnow - lastaccesstime > LOCALSESSION_LIFETIME_SEC ) // lifetime end!
{
// delete session from map
map_sessionids.erase( it_sessionids++ ); // Increments the iterator but returns the original value for use by erase
MUTEX_map_sessionids.unlock();list_to_clean.push_back ( ptr_sessionid ); // at the end
}
else if ( total_showed == 0 and secondsnow - lastaccesstime > 36000 ) // not active for N secontes
{
map_sessionids.erase( it_sessionids++ ); // Increments the iterator but returns the original value for use by erase
MUTEX_map_sessionids.unlock();

// add pointer to list to delete it latter
list_to_clean.push_back ( ptr_sessionid ); // at the end
}
else
{
++it_sessionids; // next
MUTEX_map_sessionids.unlock();
}

}

// used? pause
if ( !list_to_clean.empty() )
{
//sleep(1);
}

// cleanup session deleted from working map
while ( !list_to_clean.empty() )
{
SessionID *ptr_sessionid_to_delete = list_to_clean.front();
list_to_clean.pop_front();

ptr_sessionid_to_delete->MUTEX_all_session.lock_writing(); // protected lock session mutex. can not delete session if its already locked. (additational protection)
ptr_sessionid_to_delete->cleanup();
delete ptr_sessionid_to_delete;
}

}

НОТА как вы можете видеть при каждом изменении, я блокирую / разблокирую map_sessions, потому что другие потоки в это время находят / вставляют новые сеансы и это критично, потому что пользователи не могут ждать.

0

Решение

Обратите внимание, что любая модификация карты может сделать недействительным любой итератор в этой карте. У тебя есть:

MUTEX_map_sessionids.lock_reading();
std::map<unsigned int, SessionID*>::iterator it_sessionids = map_sessionids.begin();
MUTEX_map_sessionids.unlock();

Теперь сразу после этой разблокировки другой поток может получить блокировку и выполнить некоторую операцию, которая делает недействительной it_sessionids, который оставит его в состоянии, когда последующий код повредит карту, что приведет к более позднему сбою.

Вам необходимо получить и удерживать блокировку в течение всего времени жизни итератора. Похоже, у вас есть блокировки чтения / записи, поэтому вам нужно только удерживать блокировку чтения на все время, обновив ее до блокировки записи, когда вы хотите изменить карту, а затем сразу же понизив ее обратно до блокировки чтения после модификации. Удерживание блокировки чтения в течение длительного времени блокирует только другие потоки, которые хотят получить блокировку записи, а не другие потоки, которым нужна только блокировка чтения.

Отвечая на ваши вопросы в комментарии:

  1. Если вы не можете удерживать блокировку в течение длительного периода времени, то у вас не может быть итераторов, которые остаются в силе в течение длительного периода времени. Одна вещь, которую вы можете сделать, это иногда вспомнить приблизительно, где вы находитесь на карте, снять блокировку (предоставив другим потокам шанс и лишить законной силы итератор), затем повторно получить блокировку и создать новый итератор примерно в той же точке. Вы можете добавить что-то вроде этого в середине вашего цикла:

    if (++count > limit) { // only do this every Nth iteration
    unsigned int now_at = it_sessionids->first;
    MUTEX_map_sessionids.unlock();
    // give others a chance
    MUTEX_map_sessionids.lock_reading();
    it_sessionids = map_sessionids.lower_bound(now_at);
    count = 0; }
    
  2. Обновление блокировки только для чтения до чтения / записи — это примитивная операция с блокировками чтения / записи, которую ваша реализация может не поддерживать. Если нет, то вам не повезло, и вам нужно держать замок писателя все время.

2

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector