All streams
Search
Write a publication
Pull to refresh
16
0
Send message
Хорошо, допустим вы правы. Как вы могли бы тогда объяснить тот факт, что замена CreateThread() позволила если не исправить, то хотя бы замаскировать ошибку? _beginthreadex() не требуется 1 МБ непрерывной памяти?
Небольшого примера я привести не могу. Единственным примером мог бы быть исходный код нашего проекта. Но к нему доступ закрыт по понятным причинам. Я постараюсь написать небольшой тест и попробовать воспроизвести ошибку на нем.

Вы правы, что ошибка вызвана совокупностью всех выполняющихся задач в программе. Но искать причину, почему наш фикс сработал — это как искать иголку в стоге сена. Хотя мне самому интересно.

Единственным указанием на то, что проблема действительно была решена при помощи _beginthreadex, это то, что на целевом ноутбуке были запущены тяжеловесные программы, типа Photoshop'а. Следовательно, в какой-то момент могла просто не успеть выделиться память под tiddata или выделение памяти закончилось ошибкой.
Еще одной уликой в пользу этой теории — под другим пользователем на этой же машине (никаких тяжеловесных программ не запущено) проблема не воспроизводится.
> Замена CreateThread на _beginthreadex не могла решить описанную в статье проблему.
Очень смелое утверждение, учитывая, что у меня на руках история изменения кода, а у вас только предположения. Вы, конечно, можете больше доверять собственному опыту, чем мне, но мне врать тоже нет резона.
Я, возможно, ввел вас в заблуждение. Серверное ПО в нашем случае — это не совсем серверное ПО в общем понимании.
Архитектура ПО подразумевает, что внутри самой программы есть серверная часть, которая обрабатывает и подготавливает данные для различного железа, с которым наше ПО взаимодействует. Так вот когда появляется новое железо в ходе работы, то к серверной части идет запрос на выдачу большого количества данных. К сожалению, код написан так, что в первое время этот запрос можно удовлетворить только большим количеством потоков.
К сожалению, или к счастью, ваши предположения не верны: изменили только вызов CreateThread на _beginthreadex.

Да, старая версия тестировалась, проблема была. Нет, тестовое окружение не менялось. И остальной код тоже не менялся. Только один вызов заменили.

Количество одновременно работающих потоков не изменилось. В работающей версии их больше, чем в неработающей.

И еще могу добавить, что эта правка исправила другие подземные стуки (непонятно почему возникающие ошибки) в другой функциональности продукта, которые проявлялись уже у тестировщика. И эти ошибки имели ту же самую природу: невозможность в нужный момент создать поток.
> Их приложение «Windows Password Recovery» насколько мне известно — уникальное
Вроде у Passware есть аналогичный продукт (забавно, название фирм почти совпадает), причем он умеет восстанавливать пароли и с образа системного диска (т.е. продукт запускается не локально на системе).
Думаю, на данной стадии формальная верификация подходит только для «Hello, world». Потому что для больших проектов написание правил верификации — работа такая же, если не больше, по трудоемкости, как и написание самого проекта.
1) Да, уверен. Нет, новые потоки создаются, пока старые еще работают. Отсюда и рост количества потоков. Да, растет при нагрузке, но растет весьма ограниченно: при стабильной работе максимум количества потоков около 130. Потом убывает.

2) Нет, размер стека стоит по-умолчанию.

3) К сожалению, потоки независимы. И все ждут разных событий. Да, хотелось бы переработать архитектуру, но руки до этого дойдут еще не скоро.

4) Как я уже писал, ноутбук не рабочий, если туда поставить студию, то эффект может пропасть. А когда проблема перестала проявляться, то ноутбук уже точно не отдадут на тестирование.

5) Пока все эксперименты с Application Verifier заканчиваются крахом программы без сообщения об ошибке, еще до отрисовки главного окна.

Спасибо за советы. Возьму на карандаш. )
> Можно Вас попросить линк на описание проблемы с CreateThread?
Описание нашей проблемы приведено в тексте: CreateThread() завершается с ошибкой «Failed to create thread. Not enough storage is available to process this command.» Или вам нужна какая-то другая информация?

> То есть программа должна была падать, а не фейлится вызов CreateThread.
Возможно, имелось в виду, что будет завершен процесс создания потока, а не пользовательский процесс.

> Тут больше похоже на утечку хендлов или что-то в этом роде…
Утечек не было, проверяли.

> тестировалась ли программа с использованием Application Verifier
Нет, но я возьму на заметку. Самому интересно, справится ли эта программа с нашим проектом. :)
Да, когда ошибка трудно воспроизводится, приходится очень вдумчиво смотреть в код и думать, что же там могло сломаться.
Да, вы были правы. Без антивируса проблема не повторяется. Однако разграничение доступа к файлам — задача ОС, а не приложений.
Да, так и есть. Обновил пост.
Однако вы прекрасно понимаете, что просить пользователя отключить антивирус не есть хорошо. )
Спасибо за наводку на эти книги! Действительно, первой ситуации можно было бы избежать, прочитав Рихтера.
А как, не смешивая разные API и фреймворки, написать программу, работающую с сетью и имеющую GUI, к примеру? Если не использовать сторонние библиотеки, то такую программу можно писать ооочень долго.
Я в конце статьи выложил свой полный тест. У меня ошибка воспроизводится стабильно в пределах 100000 итераций.
Я использовал CRT, а не WinAPI.
Полностью с вами согласен. И я не оправдывался, сам знаю, что костыль на костыле в программе. :)

Другое дело, что когда рефакторинг стоит на последнем месте в приоритете разработки проекта (а на первом — новые фичи), то вот такие ошибки отнимают очень много времени на их исправление.
Удалить и переименовать файлы — быстрее, чем перезаписывать их содержимое. Хотя бы потому, что скорость такого алгоритма не зависит от размера самих файлов.

> ЗЫ не совсем понимаю логику, запускаем стороннюю прогу, у нее все получается, у нашей — фейл, в результате виновата ось…
Читайте внимательней. В первой ситуации я и написал, что косяк у нас. А на ось я грешил во второй ситуации.
Почитайте статью внимательней. На функцию удаления я не жаловался. Мне не понятна была ситуация, когда нельзя создать файл с тем же именем после того, как предыдущий был удален. При этом переименовать файл в это имя можно.
Вы не верите в ситуацию, описанную во втором случае? Соберите тестовую программу и запустите ее. Код теста я привел. Он тоже будет глючить. Единственное, что я не написал — это вывод номера итерации, на которой произошла ошибка.
Мы стараемся это исправить. Вот, один конфликт API исправили. :)

Information

Rating
Does not participate
Registered
Activity