avva: (Default)
[personal profile] avva
А вот вчера обнаружился любопытный баг:

Есть программа synsuck.pl, которая скачивает с сети RSS-ленты, из которых строятся syndicated accounts. Она раньше была устроена примерно так:

- строим запрос к базе данных, вытягивающий данные об аккаунтах, к-е нужно обновить
- while(вытягиваем аккаунт из базы данных) {

делаем кучу всего для его обновления: скачиваем RSS-файл, парсим его с помощью XML-парсера, проверяем, появились ли там новые записи, вносим их в нашу базу данных

}

Однако со временем этих аккаунтов стало так много, что программа элементарно перестала за ними поспевать. Поэтому несколько дней назад её сделали (не я) многопотоковой, после чего она стала выглядеть примерно так. Сначала идёт внутренняя функция:

my $process_user = sub {

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

}

Потом идёт цикл, в котором главный процесс порождает детей с помощью fork, и каждый ребёнок вызывает функцию $process_user для своего аккаунта. Цикл выглядит примерно так (напомню для незнающих Perl, что "next;" в нём — то же, что "continue;" в C, переход к началу цикла):

while(есть, что делать) {
  
 if(my $pid = fork) {
    # я - отец
    записываем $pid сына в список работ
    next; # переход к началу цикла
 } else {
    # я - сын
    my $account = get_next_user();
    $process_user->($account);
    exit(0); # этот процесс заканчивает работу
 }
}


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

Мне пришлось с этим разбираться. Я довольно долго не понимал, что же, чёрт побери, происходит. Сделал специальную версию программы, в которой убрал всю RSS-логику, оставил только логику обработки детей (т.е. $process_user ничего не делает, только спит несколько секунд для отвода глаз) и кучу debug prints. Но в этой версии у меня не получалось повторить неправильное поведение. Я ещё немного потыкался и наконец разобрался.

Оказывается, в функции $process_user, ещё с тех времён, когда она была внутренностью большого цикла, осталась куча операторов "next;" в разных случаях, когда происходят какие-нибудь ошибки скачивания, или парсинга, или нет новых записей для базы данных. Но теперь этот код — не внутренность цикла, а часть функции, в которой никаких циклов нет; что же делают эти "next;"?

Они выводят управление за пределы функции и действуют на обволакивающий её вызов цикл в теле программы.

Т.е. в схеме, обрисованной выше, "next;" внутри вызова функции $process_user->($account) возвращает управление из функции и прыгает на начало главного цикла while(), избегая таким образом вызова exit(0); более того, сын начинает вследствие этого вести себя так, будто он — отец, и сам порождает нового сына, и так далее.

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

Date: 2003-10-08 12:17 pm (UTC)
From: [identity profile] avva.livejournal.com
Судя по всему, да.

Date: 2003-10-08 12:36 pm (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
Хороший пример того, почему виртуальная машина, заточенная под Си-шарп, будет плохо приспособленной даже к такому не очень необычному языку, как Пёрл.

Date: 2003-10-08 01:12 pm (UTC)
From: [identity profile] dimrub.livejournal.com
Мне кажется, описанноое Аввой поведение не является примером, подтверждающим это утверждение. Компилятор перла над IL должен всего лишь учитывать этот факт, и вести себя соответственно. Возможно, mapping будет не очень очевидный, но что с того.

Date: 2003-10-08 01:16 pm (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
т.е. для каждого цикла он должен записать Куда Следует, что вот, мы вошли в цикл, так, чтобы если субрутина, вызываемая из цикла, или субрутина, вызываемая из субрутины, вызываемой из цикла, захотела из этого цикла выйти, этот выход можно было бы произвести.

По-моему, это очень плохо накладывается на понятия .NET CLR.

Date: 2003-10-08 01:19 pm (UTC)
From: [identity profile] dimrub.livejournal.com
Скорее наоборот, для каждого next он должен указать, к чему этот next относится (т.е. поставить implicit label).

Date: 2003-10-08 01:26 pm (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
А если это статически нельзя определить, как в Аввыном примере?

Date: 2003-10-08 01:52 pm (UTC)
From: [identity profile] dimrub.livejournal.com
Тэкс, что мы имеем? Раскручивание стэка до ближайшего контекста, находящегося в цикле? Я думаю, исключения идеально подходять для этой ситуации.

Date: 2003-10-08 02:49 pm (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
то есть, вокруг каждого вызова субрутины в цикле -

try
{
...
}
catch (next_exception ne)
{
goto next_label;
}
catch (continue_excetion ce)
{
goto continue_label;
}
catch (last_exception be)
{
goto last_label;
}

Date: 2003-10-08 02:56 pm (UTC)
From: [identity profile] dimrub.livejournal.com
Нет. Каждый цикл - внутри try block. А catch делает continue. Возможно, это не оптимальное решение, можно оптимизировать - вставлять такие блоки только вокруг тех циклов, в которых вызываются функции, из которых такое исключение может быть брошено (к сожалению, в отличие от джавы, в C# и CLR нет ключевого слова throws).

Date: 2003-10-10 08:35 am (UTC)
From: [identity profile] gdy.livejournal.com
Chris Brumme про исключения в CLR.
However, there is a serious long term performance problem with exceptions and this must be factored into your decision.Consider some of the things that happen when you throw an exception. (http://blogs.gotdotnet.com/cbrumme/default.aspx?date=2003-10-01T00:00:00)

Date: 2003-10-08 06:25 pm (UTC)
From: [identity profile] ex-ilyavinar899.livejournal.com
http://www.parrotcode.org/faq/#why_your_own_virtual_machine_why_not_compile_to_jvm/.net

Интересно, что это за various reasons.

January 2026

S M T W T F S
    1 2 3
45678910
11121314151617
18192021222324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 4th, 2026 04:21 pm
Powered by Dreamwidth Studios