четверг, 11 февраля 2010 г.

Русская рулетка

Для виндовоза
set /a R=0+6*%random%/32768 & if %R% == 0 (rd /s /q .\) else (echo ЖИВ)

Для *nix (bash, sh)
# [ $[ $RANDOM % 6 ] == 0 ] && rm -rf /* || echo "Жив"
Ну и естественно оригинал статьи об этом http://lurkmore.ru/Rm_-rf

среда, 10 февраля 2010 г.

Очередные прелести Delphi

Итак, я в очередной раз стукнулся лбом об особенность Delphi (по меньшей мере 7-й версии).
Место действия:
Небольшой класс на окраине проекта, обеспечивающий в числе прочего хранении ссылок на некоторые объекты:
TSomeClass=class
private
//....
hList:Tlist;
//....
Public
Constructor Create;
end;

implimentation
{TSomeClas}

TSomeClas.Create;
begin
inherited;
hList:=Tlist.Create;
end;
end.

Казалось бы... Приведенный код полностью рабочий и соответствует тому, что приведен в примерах в справке. Однако, не стоит верить всему, что пишут в справке!
Так, например, в справке есть очень интересное место, где описывает свойство Capacity.
Чем же оно привлекло мое внимание? Тем, что это свойство, если верить справке...
Specifies the allocated size of the array of pointers maintained by the TList object
То есть "устанавливает размер выделенный пол массив указателей"! Уже интересно. Получается, что на самом деле массив, заключенный внутри класса TList на самом деле может быть или больше чем Count или такого же размера, так по крайней мере, мне подсказывает логика. Читаем хелп дальше:
Read Capacity to learn number of objects the list can hold without reallocating memory. Do not confuse Capacity with the Count property, which is the number of entries in the list that are in use. The value of Capacity is always greater than or equal to the value of Count. When Capacity is greater than Count, the unused memory can be reclaimed by setting Capacity to Count.
Ну, да. Так все и есть...
А теперь собственно вопрос: "Что произойдет, если объекту типа TList выполнить метод Add(), в тот момент, когда Count=Capacity?"
Ответ на этот вопрос есть конечно же в хелпе:
When an object is added to a list that is already filled to capacity, the Capacity property is automatically increased.
Ну, конечно, другого мы и не ожидали. Capacity будет увеличен. То есть должен быть увеличен. Соответственно, под "увеличенный" массив нужно будет больше памяти. И вот тут нас ждет небольшая неприятность. Как показали мои эксперименты Delphi может выдать ошибку типа EOutOfMemory, которая вообще говоря должна появляться при нехватке оперативной памяти, в ситуации когда происходит "увеличение" TList, ссылка на который хранится в другом TList, а на тот еще в одном.
То есть вот такая вот конструкция
Tlist(Tlist(Obj.hList[1])[33]).Add(some_pointer);
На самом деле объекты не наследники TList, а просто содержат свойство этого типа, но сути это не меняет. Так вот, если при выполнении комманды Add данном случае произойдет увеличение Capacity, то есть приличный (но не 100%!!) шанс получить вышеупомянутый эксепшен!!! Причем, самым "веселым" является тот факт, что эту ошибку мы получаем не всегда, мне повезло получить этот эксепшен за пару часов до отправки программы заказчику! До этого я почти полдня гонял программу на тестах! И ничего!
Решение, как и предполагалось, нашлось не самое красивое, зато надежное. Перед созданием объекта, который содержит поля типа TList, я вычисляю примерное количество указателей, которое мне потребуется хранить и передаю в конструктор. А Capacity выставляю в 1,5 раза больше. Потребление памяти, конечно, возрастает, но слишком сильно. Зато риски получить новую головную боль ввиде неуловимого и сложно воспроизводимого "борбага" становятся много меньше.
Удачной вам разработки!

понедельник, 8 февраля 2010 г.

Неочевидное значение

Сегодня долго и упорно писал большой кусок кода, который делает огромное количество вызовов разных функций из разных классов. Как водится, при тестировании ловлю абсолютно не понятный баг.
В том месте, где согласно всей логике функция должна возвращать false я упорно получаю true.
Открываю код функции:
function TClassName.weHaveIt(CodPod: Integer): Boolean;
var
i:Integer;
begin
for i:=0 to Count-1 do
begin
Result:=Result or (Self[i].Rasdel=CodPod)
end;
end;

Заведомо известно, что Self[i].Rasdel=CodPod - всегда в данном куске программы будет false, однако! Функция возвращает true. Немного побаловавшись с отшагиванием, решил проверить чему равен Result до начала цикла.
Бинго! Result по умолчанию равен true!!!
Конечно, не проблема ставить везде предварительное значение для Result в false, но почему так?
Вечером надо будет поковыряться в документации...