Добрый день,
Я столкнулся со следующей проблемой. Есть TCP-сервер построенный на технологии портов завершения ввода-вывода. Есть 20 клиентов, которые в целях тестирования непрерывно выполняют следующие команды: Connect, Send (3байта), Disconnect. Клиенты запущены на той же машине, что и сервер.
Основная функция рабочего потока сервера выглядит стандартно для IOCP, примерно так:
Есть класс CIOReq наследованный от OVERLAPPED, который несет дополнительную информацию об операциях.
Функция HandleOperation имеет некоторую логику и делает следующие вызовы:
1.
...
pIOReq = new CIOReq();
...
int nRes = WSASend(socket, pWSABUF , 1, &dwLen, 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)
Здравствуйте, 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)
в качестве передатчика / приемника рутинам WSASend()/WSARecv() соответственно требуется массив WSABUF структур — его надо по любому тащить до завершения асинхронной операции
вот с этим мне сдается у вас нелады, потому как механика простая на самом деле и пашет безотказно у меня к слову уже с месяц круглосуточно
попробуйте именно динамически брать память под WSABUF на каждом вызове WSASend()/WSARecv() — освобождая ее только на следующем таком же вызове — переразмещение
ну и конечно же overlapped надо тож тащить на завершение вызова целочкой
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Pepel, Вы писали:
P>в качестве передатчика / приемника рутинам WSASend()/WSARecv() соответственно требуется массив WSABUF структур — его надо по любому тащить до завершения асинхронной операции P>вот с этим мне сдается у вас нелады, потому как механика простая на самом деле и пашет безотказно у меня к слову уже с месяц круглосуточно P>попробуйте именно динамически брать память под WSABUF на каждом вызове WSASend()/WSARecv() — освобождая ее только на следующем таком же вызове — переразмещение P>ну и конечно же overlapped надо тож тащить на завершение вызова целочкой
Вроде так и делаю... WSABUF у меня живет в классе CIOReq, наследнованном от OVERLAPPED, поэтому должен жить столько же, сколько и сама структура. Я динамически выделяю память под CIOReq и соответственно освобождаю ее в единственном месте — обработчике завершения ввода-вывода.
В любом случае, спасибо за идеи, буду продолжать копать...
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, 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)
Здравствуйте, Armastab, Вы писали:
A>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.
Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Michael Chelnokov, Вы писали:
MC>Здравствуйте, Armastab, Вы писали:
A>>Из всего вышеописанного у меня сложилось ощущение, что один из ожидающих рабочих потоков получает управление и сигнал о завершении ввода-вывода, еще до того как произошел возврат из WSASend WSARecv.
MC>Такая ситуация вполне реальна и происходит достаточно часто, особенно на многопроцессорных системах. Я обычно делаю счетчик ссылок, инкрементирую его перед вызовом WSASend/WSARecv, а декрементирую в обработчике IOCP или в случае ошибки WSASend/WSARecv (не-IO_PENDING, т.е. когда обработчик вызываться не будет).
и какое количество переданных/полученных байт возвращается на таком 'преждевременном' завершении отсылки/получения ?
Re[2]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, 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
Здравствуйте, 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
Здравствуйте, 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
и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа
тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно
Re[3]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Armastab, Вы писали:
A>Я делаю следующим образом:
Не надо так делать. Делай как сказано: инкремент до, декремент в обработчике (или при ошибке). Это наглядно и логично.
A>Я не совсем понял Ваш алгоритм работы, если счетчик инкрементировать перед асинхронным вызовом, а декрементировать только в обработчике, каким образом это может защитить от разрушения структуры OVERLAPPED до завершения WSASend\WSARecv ? Или идея в чем-то другом?
Не понятно что не понятно. Структуру ты удаляешь сам, в обработчике, по счетчику == 0.
Re[5]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, 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
Здравствуйте, Pepel, Вы писали:
P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа
Это что за оккультизм?
P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно
Ужос-ужос.
Re[6]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Pepel, Вы писали:
P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа
Счетчики ссылок помогают защитить объект от преждевременного удаления (см. соседние комментарии)
P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно
Я пробовал — try-catch на WSARecv/WSASrnd не ловит это исключение. Xentry подсказал, что это может быть из-за того, что выполнение асинхронных функций происходит не в вызывающем потоке.
В обработчике ловить это исключений не додумался — попробую. Спасибо.
Re: Исключение Access violation в WSASend и WSARecv (IOCP)
Здравствуйте, Armastab, Вы писали:
A>Странность в том, что срабатывание обработчика, по моему мнению, происходит до того, как WSARecv/WSASend закончили свою работу и вернули управление в вызывающий их поток. Т.к. WSARecv/Send еще не отработал до конца, а OVERLAPPED уже рарзушено в другом процессе внезапно сработавшим обработчиком, возникает исключение.
Ерунда. Обработчик вызывается только после завершения WSARecv/WSASend, иначе быть не может. Может тебе так кажется на двух ядрах?
В обработчике можешь смело удалять структуру. Если вылазит ошибка — ищи баг.
Re[4]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Gomes, Вы писали:
G>Здравствуйте, Armastab, Вы писали:
A>>Я делаю следующим образом: G>Не надо так делать. Делай как сказано: инкремент до, декремент в обработчике (или при ошибке). Это наглядно и логично.
A>>Я не совсем понял Ваш алгоритм работы, если счетчик инкрементировать перед асинхронным вызовом, а декрементировать только в обработчике, каким образом это может защитить от разрушения структуры OVERLAPPED до завершения WSASend\WSARecv ? Или идея в чем-то другом? G>Не понятно что не понятно. Структуру ты удаляешь сам, в обработчике, по счетчику == 0.
Если я делаю инкремент до вызова WSARecv- счетчик становиттся равным 1. Происходит "преждевременное" срабатывание обработчика — вызов WSARecv еще не завершен. В обработчике я делаю декремент — счетчик равен 0. Я удалю структуру — возникает ошибка т.к. WSARecv еще не завершился и использует структуру. Где профит?
Я, видимо, что-то не понимаю в алгоритме работы со счетчиками, поясните пожалуйста.
Re[7]: Исключение Access violation в WSASend и WSARecv (IOCP
Здравствуйте, Pepel, Вы писали:
P>и как в такой ситуации помогают некие счетчики ссылок — я понимаю есть псевдосрабатывание завершения некой асинхронной операции, которое тем и отличается от настоящего, что находится на самом деле еще в работе и посему одновременное обращение к конструкциям данного асинхронного вызова вызывает нарушение доступа
Скорее всего там нет никакого псевдосрабатывания — я думаю что топикстартер не разобрался доконца в механизме работы. Счетчик ссылок может помочь например при закрытии сокета — т.к. это может вызвать завершение асинхронных операций (если они конечно были).
P>тогда только в __try — catch() брать обработчик завершения отсылки/получения и если ловим ACCESS_VIOLATION — игнорируем его, полагая , что код вменяемый и нарушение доступа по иной причине маловероятно
В обрабочике ловить ACCESS_VIOLATION будет поздно — приложение к этому моменту уже вылетит в отладчик.