[Moscow.pm] Ограничить время выполнения произвольной функции

Dmitry E. Oboukhov unera на debian.org
Пт Янв 29 02:06:25 PST 2010


стоит сабжевая задача.

Для ее решения я пошел по пути описанному в perldoc -f alarm (на CPAN
есть штук пяток модулей на эту тему например Sys::AlarmCall (он же
рекомендован к употреблению в perlfaq), я про них знаю, но описанная
здесь проблема в них во всех проявляется, потому стоит задача
написания нового модуля, либо прийти к нереализуемости задачи), итак,
в perldoc -f alarm пример:

   eval {
       local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
       alarm $timeout;
       $nread = sysread SOCKET, $buffer, $size;
       alarm 0;
   };
   if ($@) {
       die unless $@ eq "alarm\n";   # propagate unexpected errors
       # timed out
   }
   else {
       # didn't
   }


пишем вместо sysread ту функцию, которую хотим ограничить по времени
работы и вуаля - работает. Если посмотреть CPAN'овские модули, то все
они работают на этой технологии, только дополнительно делают следующие
вещи:
    - определяют контекст вызова wantarray
    - запоминают/восстанавливают предыдущее значение alarm 
    - ловят исключения в самой функции
    - прочий сервисный шняг
но это несущественно, собственно вернемся к проблеме.

допустим мы заменили sysread на foo()

и как оказывается работа сей конструкции зависит от того чтоже там
внутри foo()

допустим foo у нас содержит такой код:

sub foo
{
    for ($i = 0; $i < 100; $i++) {
    	sleep 1;
    }
}

с такой функцией будет все ок.

но вот если например внутри функции хотя бы в одном месте будет eval
sub foo
{
    for ($i = 0; $i < 100; $i++) {
    	eval "sleep 1";
    }
}

то именно этот eval и получит die "alarm\n".
Теперь все зависит от функции, распространит она die выше или не
распространит. можно было бы сказать что функция которая не
распространяет die - "неправильная", да только таких функций в
cpan-модулях чуть меньше чем дохрена, да и "правильные есть"

sub divide($$)
{
    my ($a, $b) = @_;
    die "на ноль делить нельзя" unless $b;
    return $a / $b;
}

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

Получается хоть функция приведенная в perldoc -f alarm SIGALRM и
словила, но тормознуть выполнение "подопытной" функции не может, если
тот содержит eval.

Искал искал решение, но пока ничего приемлемого не нашел. 
Может кто боролся с подобной проблемой? есть какие-либо предложения?

в обработчике SIGALRM мы в принципе видим стек вызовов, можно ли
как-то из него изъять несколько уровней? perl'овыми средствами

-- 
... mpd is off

. ''`.                               Dmitry E. Oboukhov
: :’  :   email: unera на debian.org jabber://UNera@uvw.ru
`. `~’              GPGKey: 1024D / F8E26537 2006-11-21
  `- 1B23 D4F8 8EC0 D902 0555  E438 AB8C 00CF F8E2 6537
----------- следущая часть -----------
A non-text attachment was scrubbed...
Name: отсутствует
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://mail.pm.org/pipermail/moscow-pm/attachments/20100129/a0cd1937/attachment.bin>


Подробная информация о списке рассылки Moscow-pm