Борьба с Bad Pointer
LogonCommServerSocket * s;
for(itr = m_serverSockets.begin(); itr != m_serverSockets.end();)
{
s = *itr;
it2 = itr;
++itr;
if(s->last_ping < t && ((t - s->last_ping) > 240))
{
В m_serverSockets есть один указатель видимо уже на удаленный сокет, но оставленный в наборе...
Краш происходит здесь: s->last_ping
Соответсвенно вопрос, можно ли как-то узнать что s "плохой указатель" и как узнать (с примером пожалуйста)?
я смотрел функции IsBadCodePtr и IsBadReadPtr, но что-то не понял как ими пользоваться.
в случае если пробовать IsBadCodePtr(s), то пишет что неможет сконвертировать s к нужному типу, а в случае с readprt, мне не известен размер блока памяти в байтах, ну и наверное тоже будут проблемы с конвертацией.
Соответсвенно вопрос, можно ли как-то узнать что s "плохой указатель" и как узнать (с примером пожалуйста)?
Нет, узнать в общем случае нельзя.
я смотрел функции IsBadCodePtr и IsBadReadPtr, но что-то не понял как ими пользоваться.
Это не поможет.
Объект у тебя же не сам исчезает, он же где-то удаляется в коде. Вот и исключай там из контейнера указатель на уничтоженный объект или обнуляй его.
Вообще-то, это здесь как пятая нога...
А на счет того что обект сам не исчезает, это еще не факт :lol:
Я просмотрел весь код и везде все нормально. Ну и еще, ведь этот краш не постоянный, он может быть в любое время, через 2 минуты или 6 часов работы приложения. Думаю что баг связан с реальным временем. типа где-то обьект уже уничтожается а указатель удаляется после этого (Собственно проверял, везде сперва указатель удаляется %) но какое-то обяснение должно быть), в другом потоке пробегаются по m_serverSockets и о чудо, указатель есть, а объекта нет :)
трай кетч кстати тоже не помогает, все равно вылетает ошибка с предложением отправить отчет мс.
В общем я так понял, что если ничего не поможет, то надо сушить ласты
А на счет того что обект сам не исчезает, это еще не факт :lol:
Т.е. ты склонен к мысли о мистическом происхождении этого явления... :)
Я просмотрел весь код и везде все нормально. Ну и еще, ведь этот краш не постоянный, он может быть в любое время, через 2 минуты или 6 часов работы приложения. Думаю что баг связан с реальным временем. типа где-то обьект уже уничтожается а указатель удаляется после этого (Собственно проверял, везде сперва указатель удаляется %) но какое-то обяснение должно быть), в другом потоке пробегаются по m_serverSockets и о чудо, указатель есть, а объекта нет :)
Объяснение:
1) указатель не удаляется из контейнера;
2) если приложение многопоточное, то не осуществляется синхронизация доступа к контейнеру;
3) ошибка может быть и наведенной.
трай кетч кстати тоже не помогает, все равно вылетает ошибка с предложением отправить отчет мс.
Разберись, как правильно работать с try/cath, тогда и диалог появляться не будет.
Но в данной ситуации try/cath тебе не нужен и не поможет.
В общем я так понял, что если ничего не поможет, то надо сушить ласты
По твоему изложению понятно, что ты совсем не владеешь кодом. Ты не сам его писал? У тебя нет доступа к исходникам?
Не ласты надо сушить, а разобраться в коде и действовать продуманно, а не научными тыками.
К сожалению это такая вещь, где часто случаются мистические вещи... Конечно источник этой мистики - баги, из которых, пожалуй, состоит большая часть этого приложения
1) указатель не удаляется из контейнера;
2) если приложение многопоточное, то не осуществляется синхронизация доступа к контейнеру;
3) ошибка может быть и наведенной.
Склоняюсь ко второму варианту, но разобраться во всем этом ужасе не так уж и просто, но судя по попыткам разобраться все нормально
Но в данной ситуации try/cath тебе не нужен и не поможет.
Я немного переписал функцию в которой краш. Трай кетч вроде нормально расположен и отлавливает все ошибки. Может конечно с ним надо как-то по другому работать, но как научили, так и научили
{
if(!usepings) return;
serverSocketLock.Acquire();
uint32 t = uint32(time(NULL));
for(set<LogonCommServerSocket*>::iterator itr = m_serverSockets.begin(); itr != m_serverSockets.end(); ++itr)
{
try
{
if((*itr)->last_ping < t && ((t - (*itr)->last_ping) > 240))
{
sLog.outString("Closing socket due to ping timeout...");
(*itr)->removed = true;
for(set<uint32>::iterator i = (*itr)->server_ids.begin(); i != (*itr)->server_ids.end(); ++i)
UpdateRealmStatus((*i), 2);
m_serverSockets.erase(itr);
(*itr)->Disconnect();
}
} catch(...) {m_serverSockets.erase(itr); break;}
}
serverSocketLock.Release();
}
Не ласты надо сушить, а разобраться в коде и действовать продуманно, а не научными тыками.
Код писал не я, его писали много разных людей начиная с 2006 года. Доступ к исходникам есть. В общем что досталось, с тем и разбираюсь потихоньку, как могу. Но это не тектовый редакор чтобы взять и разобраться в нем сразу, тут много страшных и ужасных вещей, назначение которых известно только написавшему их
Не знаю, во что перестает эта беседа, во флуд или оффтоп, но дабы не получить тут предупреждений, я лучше пойду дальше разбираться вов сем этом ужасе, а то даже за благодарности грозились дать предупреждение
(*itr)->Disconnect();
// в начало цикла и ++itr
В изначальном же варианте у тебя вообще объявлено два итератора одного типа, которые, по-видимому, изменяются в циклах разной степени вложенности...
{
if(!usepings)
return;
/* burlex: this is vulnerable to race conditions, adding a mutex to it. */
serverSocketLock.Acquire();
uint32 t = uint32(time(NULL));
// check the ping time
set<LogonCommServerSocket*>::iterator itr, it2;
LogonCommServerSocket * s;
for(itr = m_serverSockets.begin(); itr != m_serverSockets.end();)
{
s = *itr;
it2 = itr;
++itr;
if(s->last_ping < t && ((t - s->last_ping) > 240))
{
// ping timeout
printf("Closing socket due to ping timeout.\n");
s->removed = true;
set<uint32>::iterator itr = s->server_ids.begin();
for(; itr != s->server_ids.end(); ++itr)
UpdateRealmStatus((*itr), 2);
m_serverSockets.erase(it2);
s->Disconnect();
}
}
serverSocketLock.Release();
}
Но факт есть фактом, в m_serverSockets каким-то чудом остается указатель на удаленный сокет что в старой версии, что в новой. Причем как он там появляется не ясно. Во всех местах где дисконнектятся сокеты все нормально. И приложение ведь работает нормально некоторое время
"А итератор там во время/после удаления переприсваивать не надо?"
Нет, достаточно просто извлеч его из набора
"Неужели прокатывает?!"
Хм... что-то мне сомнительным это показалось, но работает все точно так же без новых проблем и крашится что с кетчем, что без него, с аптаймом от 2 минут до 8 часов
---- Updated after some time
Что-то совсем меня засмущало это, попробую лучше так:
m_serverSockets.erase(itr);
s->Disconnect();
Но врятли это что-то исправит, если старый вариант функции подвержен крашу
По крайней мере, у меня твой вариант точно не работает и даже вид не делает. Всегда думал, что надо так:
// ...
set<type>::iterator i = ++s.begin();
i = s.erase(i);
Я бы на вашем месте вообще не стал использовать try/catch. Да, в большинстве случаев обращение по указателю к уже удаленному объекту вызовет access violation. Но не всегда, а если по этому адресу уже успел разместиться новый объект того же класса, тогда исключение выкидываться не будет (по крайней мере в этом месте), но логика программы уже скорее всего будет нарушена. А локализавать такую ошибку будет намного сложнее.
Сейчас перенес удаление из контейнера в саму функцию Disconnect(), может так хоть проблема решится с исчезновением объекта.
Der Meister, Хм... Во всем коде это делается так:
for(; itr != m_players.end(); itr++)
{
Player * plr = (Player *)(*itr);
...
for(set<GameObject*>::iterator itr = m_gates.begin(); itr != m_gates.end(); ++itr)
(*itr)->PushToWorld(m_mapMgr);
{
if((*itr) == grp->GetID())
itr = m_queuedGroups.erase(itr);
else
++itr;
}
И как-то работает, так что лучше я не буду менять все это на то, чего не понимаю
itr = m_queuedGroups.erase(itr);
Это похоже н ато что в твоем примере, но ++ перед begin() нигде нет.
Я конечно не исключаю что я вообще ничего не понял что ты имел ввиду :)
По-крайней мере, если ошибка действительно в плохом указателе, почему бы не переписать управление подключениями на один объект-фабрику? А если нет, то дебаг поможет + если совсем плохо - логи и профилирование кода.
Дело действительно в плохом указателе. Я узнал это запустив приложение в вижуал студии и дождавшись этой ошибки. Те же калл стек, локалс, аутос и указание на строчку кода присутсвуют.
С учетом того, что не я писал все это, а код достаточно большой и ужасный, то переписать здесь что-то сложно мне. Пока я ищу легкие пути решения проблемы. Мне бы сделать что угодно, только чтоб приложение так и не оставалось в таком состоянии. Либо чтоб завершало работу, либо чтоб продолжало работать удалив плохой казатель, либо еще что-то. Лучше конечно исправить эту проблему :)
Сейчас добавил извлечение из контейрера прямо в Disconnect() (не стандартный конечно), который занимается многими вещами и уничтожением обекта в том числе. Впринципи проблема должна решиться, но чудеса всегда бывают
{
if((*itr) == grp->GetID())
itr = m_queuedGroups.erase(itr);
else
++itr;
}
То есть, ты пытаешься сделать единый механизм уничтожения подключений. Только не сам объект должен себя "увольнять", а его "начальник", и не абы как (уйди, противный), а одним регламентированным и универсальным способом. Тогда и трудовые никто забывать не будет :) С "приёмом на работу" всё должно быть аналогично. Пока этого нет, результат так и будет оставаться в стиле стройки молдован.