Исключение Access violation в WSASend и WSARecv (IOCP)
От: Armastab  
Дата: 24.06.09 11:23
Оценка:
Добрый день,
Я столкнулся со следующей проблемой. Есть TCP-сервер построенный на технологии портов завершения ввода-вывода. Есть 20 клиентов, которые в целях тестирования непрерывно выполняют следующие команды: Connect, Send (3байта), Disconnect. Клиенты запущены на той же машине, что и сервер.

Основная функция рабочего потока сервера выглядит стандартно для IOCP, примерно так:

void WorkerThread::Run()
{

...
while(true)
{
...
fOk = m_refIOCP.GetStatus(&CompKey,&dwNumBytes, (OVERLAPPED**)&pIOReq);
...
HandleOperation(CompKey, pIOReq, dwNumBytes);
...
delete pIOReq;
..
}
...
}

Есть класс CIOReq наследованный от OVERLAPPED, который несет дополнительную информацию об операциях.
Функция HandleOperation имеет некоторую логику и делает следующие вызовы:

1.
...
pIOReq = new CIOReq();
...
int nRes = WSASend(socket, pWSABUF , 1, &dwLen, dwFlags, (OVERLAPPED*)pIOReq, NULL);

2.
...
pIOReq = new CIOReq();
...
nRes = WSARecv(socket, pWSABUF, 1, &dwSize, &dwFlags,(OVERLAPPED*)pIOReq , NULL);

Иногда (при указанной нагрузке 20 клиентов, в среднем раз в 30 секунд, иногда чаще, иногда реже), WSASend и WSARecv вываливаются с исключением 0x00000005 Access violation.
При этом поля класса pIOReq оказываются невалидными, как будто объект удален. Данное исключение выскакивает в отладчике и не перехватывается ни try-catch ни __try — __except

Экспериментально выяснилось, если количество рабочих потоков сделать равным 1 (обычно запущено 10), такой ошибки не возникает.
Если сделать защиту (собственный механизм) указателя pIOReq от удаления, до возврата из WSASend, WSARecv, то ошибки тоже не возникает, а отлавливается попытка удалить объект до снятия защиты.

Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.
Он удаляет объект pIOReq и в WSASend/WSARecv происходит нарушение доступа.

Вопросы:
как сигнал о завершении ввода-вывода может поступать до завершения асинхронной функции ввода-вывода,
почему это исключение не отлавливается стандартными средствами обработки исключений
что с этим делать?

Заранее спасибо.
Re: Исключение Access violation в WSASend и WSARecv (IOCP)
От: xentry  
Дата: 24.06.09 12:19
Оценка: 2 (1)
Здравствуйте, Armastab, Вы писали:

A>Иногда (при указанной нагрузке 20 клиентов, в среднем раз в 30 секунд, иногда чаще, иногда реже), WSASend и WSARecv вываливаются с исключением 0x00000005 Access violation.

A>При этом поля класса pIOReq оказываются невалидными, как будто объект удален. Данное исключение выскакивает в отладчике и не перехватывается ни try-catch ни __try — __except

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

A>Экспериментально выяснилось, если количество рабочих потоков сделать равным 1 (обычно запущено 10), такой ошибки не возникает.

A>Если сделать защиту (собственный механизм) указателя pIOReq от удаления, до возврата из WSASend, WSARecv, то ошибки тоже не возникает, а отлавливается попытка удалить объект до снятия защиты.

Это потому что в одном потоке все вызовы будут сериализированы.

A>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.

A>Он удаляет объект pIOReq и в WSASend/WSARecv происходит нарушение доступа.

Тут сложно сказать наверняка, не видя кода, но это предположение скорее всего ошибочное.

A>Вопросы:

A>как сигнал о завершении ввода-вывода может поступать до завершения асинхронной функции ввода-вывода,
A>почему это исключение не отлавливается стандартными средствами обработки исключений
A>что с этим делать?

Отлаживать Стоит подумать о счетчике ссылок в CIOReq (раз уж есть механизм защиты от его удаления)
Re: Исключение Access violation в WSASend и WSARecv (IOCP)
От: Pepel Беларусь  
Дата: 24.06.09 14:46
Оценка:
в качестве передатчика / приемника рутинам WSASend()/WSARecv() соответственно требуется массив WSABUF структур — его надо по любому тащить до завершения асинхронной операции

вот с этим мне сдается у вас нелады, потому как механика простая на самом деле и пашет безотказно у меня к слову уже с месяц круглосуточно

попробуйте именно динамически брать память под WSABUF на каждом вызове WSASend()/WSARecv() — освобождая ее только на следующем таком же вызове — переразмещение

ну и конечно же overlapped надо тож тащить на завершение вызова целочкой
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 24.06.09 18:29
Оценка:
Здравствуйте, Pepel, Вы писали:

P>в качестве передатчика / приемника рутинам WSASend()/WSARecv() соответственно требуется массив WSABUF структур — его надо по любому тащить до завершения асинхронной операции

P>вот с этим мне сдается у вас нелады, потому как механика простая на самом деле и пашет безотказно у меня к слову уже с месяц круглосуточно
P>попробуйте именно динамически брать память под WSABUF на каждом вызове WSASend()/WSARecv() — освобождая ее только на следующем таком же вызове — переразмещение
P>ну и конечно же overlapped надо тож тащить на завершение вызова целочкой

Вроде так и делаю... WSABUF у меня живет в классе CIOReq, наследнованном от OVERLAPPED, поэтому должен жить столько же, сколько и сама структура. Я динамически выделяю память под CIOReq и соответственно освобождаю ее в единственном месте — обработчике завершения ввода-вывода.
В любом случае, спасибо за идеи, буду продолжать копать...
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 24.06.09 18:45
Оценка:
Здравствуйте, xentry, Вы писали:

X>Здравствуйте, Armastab, Вы писали:


A>>Иногда (при указанной нагрузке 20 клиентов, в среднем раз в 30 секунд, иногда чаще, иногда реже), WSASend и WSARecv вываливаются с исключением 0x00000005 Access violation.

A>>При этом поля класса pIOReq оказываются невалидными, как будто объект удален. Данное исключение выскакивает в отладчике и не перехватывается ни try-catch ни __try — __except
X>Вероятно в этот момент удалена структура CIOReq или сами буфера чтения/записи. Поскольку сама асинхронная операция в этот момент выполняется не в рабочем потоке процесса система выплевывает исключение которое не будет поймано. Или будет поймано "без стека", или вы увидите на экране "Приложение выполнило недопустимую операцию и будет закрыто", если отладки не было.

О, спасибо. Мне в голову как-то даже не пришло, что операция не в рабочем потоке выполняется.

A>>Экспериментально выяснилось, если количество рабочих потоков сделать равным 1 (обычно запущено 10), такой ошибки не возникает.

A>>Если сделать защиту (собственный механизм) указателя pIOReq от удаления, до возврата из WSASend, WSARecv, то ошибки тоже не возникает, а отлавливается попытка удалить объект до снятия защиты.
X>Это потому что в одном потоке все вызовы будут сериализированы.
Ага, до это я догадался.

A>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.

A>>Он удаляет объект pIOReq и в WSASend/WSARecv происходит нарушение доступа.
X>Тут сложно сказать наверняка, не видя кода, но это предположение скорее всего ошибочное.

В данный момент удаление структуры (по крайней мере оператор delete) CIOReq происходит в единственном месте — рабочем потоке, после того как он получает сигнал о завершении операции ввода/вывода, именно в этом месте наблюдается переодическая попытка удаления структуры, защищенной от удаления, хотя, конечно, это теоретически может происходить и в промежуток между командой WSARecv и следующим за ней снятием защиты.

X>Отлаживать Стоит подумать о счетчике ссылок в CIOReq (раз уж есть механизм защиты от его удаления)

Ну, собственно, механизм защиты и реализован через примитивный счетчик ссылок, правда не в самом CIOReq а во внешнем объекте. Это, конечно, избавляет от исключений, но что-то мне подсказывает, что такой ошибки быть не должно. Спасибо за информацию, буду отлаживать.
Re: Исключение Access violation в WSASend и WSARecv (IOCP)
От: Michael Chelnokov Украина  
Дата: 25.06.09 14:50
Оценка: 2 (1) +1
Здравствуйте, Armastab, Вы писали:

A>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.


Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Pepel Беларусь  
Дата: 26.06.09 05:48
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

MC>Здравствуйте, Armastab, Вы писали:


A>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.


MC>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).



и какое количество переданных/полученных байт возвращается на таком 'преждевременном' завершении отсылки/получения ?
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 26.06.09 05:55
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

MC>Здравствуйте, Armastab, Вы писали:


A>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.

MC>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).

Спасибо большое! А то я уже устал отлаживать... Есть вопрос про Ваш механизм счетчика ссылок.

Я делаю следующим образом:
1) перед вызовом WSASend/WSARecv увеличиваю счетчик ссылок на струтуру OVERLAPPED
2) после вызова WSASend/WSARecv уменьшаю счетчик ссылок на струтуру OVERLAPPED
3) В обработчике перед удалением структуры OVERLAPPED проверяю, если счетчик ссылок не 0, то жду пока он станет 0, т.о. я удостовериваюсь, что произошел возврат из WSASend/WSARecv и удаление объекта не приведет к ошибке.

Я не совсем понял Ваш алгоритм работы, если счетчик инкрементировать перед асинхронным вызовом, а декрементировать только в обработчике, каким образом это может защитить от разрушения структуры OVERLAPPED до завершения WSASend\WSARecv ? Или идея в чем-то другом?
Re[3]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 26.06.09 05:58
Оценка:
Здравствуйте, Pepel, Вы писали:

P>Здравствуйте, Michael Chelnokov, Вы писали:


MC>>Здравствуйте, Armastab, Вы писали:


A>>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.


MC>>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).


P>

P>и какое количество переданных/полученных байт возвращается на таком 'преждевременном' завершении отсылки/получения ?

У меня возвращается количество байт, которое было получено. Тестирующий клиент посылает 3 байта. В момент таких "странных" срабатываний обработчика вызова WSARecv возвращается 3 байта.
Re[4]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Pepel Беларусь  
Дата: 26.06.09 06:17
Оценка:
Здравствуйте, Armastab, Вы писали:

A>Здравствуйте, Pepel, Вы писали:


P>>Здравствуйте, Michael Chelnokov, Вы писали:


MC>>>Здравствуйте, Armastab, Вы писали:


A>>>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.


MC>>>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).


P>>

P>>и какое количество переданных/полученных байт возвращается на таком 'преждевременном' завершении отсылки/получения ?

A>У меня возвращается количество байт, которое было получено. Тестирующий клиент посылает 3 байта. В момент таких "странных" срабатываний обработчика вызова WSARecv возвращается 3 байта.


я так понимаю странность в том, что за ним следует точно такое же срабатывание — которое, поскольку оно последнее, является вроде как настоящим ?
Re[5]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Pepel Беларусь  
Дата: 26.06.09 06:33
Оценка:
и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа

тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно
Re[3]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Gomes Россия http://irazin.ru
Дата: 26.06.09 06:36
Оценка:
Здравствуйте, Armastab, Вы писали:

A>Я делаю следующим образом:

Не надо так делать. Делай как сказано: инкремент до, декремент в обработчике (или при ошибке). Это наглядно и логично.

A>Я не совсем понял Ваш алгоритм работы, если счетчик инкрементировать перед асинхронным вызовом, а декрементировать только в обработчике, каким образом это может защитить от разрушения структуры OVERLAPPED до завершения WSASend\WSARecv ? Или идея в чем-то другом?

Не понятно что не понятно. Структуру ты удаляешь сам, в обработчике, по счетчику == 0.
Re[5]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 26.06.09 06:40
Оценка:
Здравствуйте, Pepel, Вы писали:

A>>>>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.

MC>>>>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).
P>>>и какое количество переданных/полученных байт возвращается на таком 'преждевременном' завершении отсылки/получения ?
A>>У меня возвращается количество байт, которое было получено. Тестирующий клиент посылает 3 байта. В момент таких "странных" срабатываний обработчика вызова WSARecv возвращается 3 байта.
P>я так понимаю странность в том, что за ним следует точно такое же срабатывание — которое, поскольку оно последнее, является вроде как настоящим ?

Нет, странность не в этом. Точно такого же срабатывания не следует. На один динамически выделяемый объект OVERLAPPED, передаваемый в асинхронный вызов WSARecv/WSASend, следует одно срабатываение обработчика вызовов. В обработчике я разрушаю объект OVERLAPPED. Странность в том, что срабатывание обработчика, по моему мнению, происходит до того, как WSARecv/WSASend закончили свою работу и вернули управление в вызывающий их поток. Т.к. WSARecv/Send еще не отработал до конца, а OVERLAPPED уже рарзушено в другом процессе внезапно сработавшим обработчиком, возникает исключение.

Выводы о "преждевременном" срабатывании обработчика я сделал на основании экспериментов с уменьшением количества рабочих потоков до 1 (ошибки не возникает, т.к. нет работы нескольких потоков, вызовы сериализуются), и организацией счетчика ссылок на структуру OVERLAPPED ( увеличение счетчика перед асинхронным вызовом, уменьшение его после, ожидание обнуление счетчика перед удалением структуры — наблюдаются попытки удаления при ненулевом счетчике, но оно блокируются и ошибки не возникает). Возможно, я где-то ошибаюсь и такое "странное" поведение программы обусловлено не найдеными мною багами.
Re[6]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Gomes Россия http://irazin.ru
Дата: 26.06.09 06:41
Оценка:
Здравствуйте, Pepel, Вы писали:

P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа

Это что за оккультизм?

P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно

Ужос-ужос.
Re[6]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 26.06.09 06:45
Оценка:
Здравствуйте, Pepel, Вы писали:

P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа

Счетчики ссылок помогают защитить объект от преждевременного удаления (см. соседние комментарии)

P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно

Я пробовал — try-catch на WSARecv/WSASrnd не ловит это исключение. Xentry подсказал, что это может быть из-за того, что выполнение асинхронных функций происходит не в вызывающем потоке.
В обработчике ловить это исключений не додумался — попробую. Спасибо.
Re: Исключение Access violation в WSASend и WSARecv (IOCP)
От: Pepel Беларусь  
Дата: 26.06.09 06:47
Оценка:
я к слову вот overlapped не разрушаю, а обнуляю на обработчике завершения (memeset(..))

а вообщем чтот здесь странно , я так понял у вас это не редкость — такая ситуевина, тогда это черти че , мож патчи накатайте на операционку
Re[6]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Gomes Россия http://irazin.ru
Дата: 26.06.09 06:50
Оценка: 2 (1)
Здравствуйте, Armastab, Вы писали:

A>Странность в том, что срабатывание обработчика, по моему мнению, происходит до того, как WSARecv/WSASend закончили свою работу и вернули управление в вызывающий их поток. Т.к. WSARecv/Send еще не отработал до конца, а OVERLAPPED уже рарзушено в другом процессе внезапно сработавшим обработчиком, возникает исключение.

Ерунда. Обработчик вызывается только после завершения WSARecv/WSASend, иначе быть не может. Может тебе так кажется на двух ядрах?
В обработчике можешь смело удалять структуру. Если вылазит ошибка — ищи баг.
Re[4]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Armastab  
Дата: 26.06.09 06:50
Оценка:
Здравствуйте, Gomes, Вы писали:

G>Здравствуйте, Armastab, Вы писали:


A>>Я делаю следующим образом:

G>Не надо так делать. Делай как сказано: инкремент до, декремент в обработчике (или при ошибке). Это наглядно и логично.

A>>Я не совсем понял Ваш алгоритм работы, если счетчик инкрементировать перед асинхронным вызовом, а декрементировать только в обработчике, каким образом это может защитить от разрушения структуры OVERLAPPED до завершения WSASend\WSARecv ? Или идея в чем-то другом?

G>Не понятно что не понятно. Структуру ты удаляешь сам, в обработчике, по счетчику == 0.

Если я делаю инкремент до вызова WSARecv- счетчик становиттся равным 1. Происходит "преждевременное" срабатывание обработчика — вызов WSARecv еще не завершен. В обработчике я делаю декремент — счетчик равен 0. Я удалю структуру — возникает ошибка т.к. WSARecv еще не завершился и использует структуру. Где профит?
Я, видимо, что-то не понимаю в алгоритме работы со счетчиками, поясните пожалуйста.
Re[7]: Исключение Access violation в WSASend и WSARecv (IOCP
От: Gomes Россия http://irazin.ru
Дата: 26.06.09 06:52
Оценка:
Здравствуйте, Armastab, Вы писали:

A>В обработчике ловить это исключений не додумался — попробую. Спасибо.

Не занимайся ерундой.
Re[6]: Исключение Access violation в WSASend и WSARecv (IOCP
От: xentry  
Дата: 26.06.09 06:53
Оценка:
Здравствуйте, Pepel, Вы писали:

P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа


Скорее всего там нет никакого псевдосрабатывания — я думаю что топикстартер не разобрался доконца в механизме работы. Счетчик ссылок может помочь например при закрытии сокета — т.к. это может вызвать завершение асинхронных операций (если они конечно были).

P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно


В обрабочике ловить ACCESS_VIOLATION будет поздно — приложение к этому моменту уже вылетит в отладчик.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.