Почему `$ @` ненадежен?

Мне кажется, я помню, что доверять значению $@ после eval небезопасно. Что-то о том, что у обработчика сигнала есть шанс установить $@ до того, как вы его увидите, или что-то в этом роде. Я также слишком устал и ленив прямо сейчас, чтобы найти настоящую причину. Итак, почему доверять $@ небезопасно?


person Chas. Owens    schedule 10.09.2010    source источник
comment
comment
Обратите внимание, что в Perl 5.14 это было исправлено.   -  person Robert P    schedule 15.11.2011


Ответы (3)


В Try::Tiny perldoc есть исчерпывающее обсуждение проблемы с $@:

Есть ряд проблем с eval.

Убийство $ @

Когда вы запускаете блок eval и он успешен, $ @ будет очищен, что потенциально может привести к обнаружению ошибки, которая в настоящее время обнаруживается.

Это вызывает действие на расстоянии, устраняя предыдущие ошибки, которые ваш вызывающий абонент, возможно, еще не обработал.

Чтобы избежать этой проблемы, перед вызовом eval необходимо правильно локализовать $ @.

В частности, $ @ затирается в начале eval, что также делает невозможным захват предыдущей ошибки до того, как вы умрете (например, при создании объектов исключений со стеками ошибок).

По этой причине try фактически установит $ @ в его предыдущее значение (до локализации) в начале блока eval.

Локализация $ @ автоматически маскирует ошибки

Внутри блока eval кубик ведет себя примерно так:

sub die {
        $@ = $_[0];
        return_undef_from_eval();
}

Это означает, что если вы были вежливы и локализовали $ @, вы не можете умереть в этой области, или ваша ошибка будет отброшена (вместо этого вы напечатаете «Что-то не так»).

Обходной путь очень уродливый:

my $error = do {
        local $@;
        eval { ... };
        $@;
};

...
die $error;

$ @ может быть неверным значением

Этот код неверен:

if ( $@ ) {
        ...
}

потому что из-за предыдущих предупреждений он мог быть отключен.

$ @ также может быть перегруженным объектом ошибки, который оценивается как false, но в любом случае это вызывает проблемы.

Классический режим отказа:

sub Object::DESTROY {
        eval { ... }
}

eval {
        my $obj = Object->new;

        die "foo";
};

if ( $@ ) {

}

В этом случае, поскольку Object :: DESTROY не локализует $ @, но по-прежнему использует eval, он установит для $ @ значение "".

Деструктор вызывается, когда стек разматывается, после того, как die устанавливает $ @ в "foo at Foo.pm, строка 42 \ n", поэтому к моменту вычисления if ($ @) он был очищен eval в деструкторе.

Обходной путь для этого еще хуже, чем предыдущие. Несмотря на то, что мы не можем сохранить значение $ @ из кода, который не локализован, мы, по крайней мере, можем быть уверены, что eval был прерван из-за ошибки:

my $failed = not eval {
        ...

        return 1;
};

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

person mob    schedule 10.09.2010
comment
Да, думаю, это то, что я вспомнил. - person Chas. Owens; 10.09.2010
comment
Не нужно больше играть в эти безумные игры. - person tchrist; 30.09.2011

В Try :: Tiny довольно хороший список _1 _ / _ 2_ недостатки. Я думаю, вы, возможно, имеете в виду Локализация $ @ незаметно маскирует ошибки там.

person rafl    schedule 10.09.2010

$@ имеет те же проблемы, что и каждая глобальная переменная: когда что-то еще устанавливает ее, она сбрасывается во всей программе. Любой eval может установить $@. Даже если вы не видите рядом eval, вы не знаете, кто еще может его вызвать (подпрограммы, связанные переменные и т. Д.).

person brian d foy    schedule 10.09.2010