6 декабря 2007 в 23:33

Правильный способ реализации таймеров и таймаутов.

Perl*
А вы знаете, что в 99% приложений и абсолютно всех CPAN-модулях таймеры и таймауты реализованы неправильно? Не верите? Правильно не верите! Есть, есть один CPAN-модуль, с корректной реализацией таймеров: EV. :) (Кстати, это, похоже, единственный CPAN-модуль из всех, реализующих event loop, который знает о проблеме использования fork вместе с event loop, описывает её в документации и даже пытается как-то решать!)

Проблема в том, что для реализации таймеров и таймаутов используются функции, возвращающие текущее время. А текущее время может быть изменено в любой момент в любую сторону на любую величину — админом или NTP-демоном. Поэтому получается так, что, например, таймаут установленный программой на 30 секунд, может сработать либо через 2 секунды, либо через пару суток — смотря как изменится текущее время после установки таймаута (а в особо запущенных случаях может не сработать никогда).

Единственный надёжный способ работать с таймерами и таймаутами — использовать монотонное время:
use Time::HiRes qw( clock_gettime CLOCK_MONOTONIC );
$now = clock_gettime(CLOCK_MONOTONIC);

Поддержка CLOCK_MONOTONIC была добавлена в Time::HiRes (по моей инициативе :)) два года назад. И вот сегодня я снова, как и два года назад, поискал на CPAN модули с правильной реализацией таймеров и таймаутов… и нашёл только один. :(
Alex Efros @powerman
карма
303,5
рейтинг 0,0
Systems Architect, Senior Go/Perl Linux Developer
Похожие публикации
Самое читаемое Разработка

Комментарии (22)

  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Кто сказал что не заметил? Просто проблемы такого рода возникают редко, и их списывают на пролетавшую мимо моль, в смысле bug. :) Программку перезапускают, она работает, никто не знает что это было и все на это забивают.

      А если Вы интересуетесь, почему о таких вещах заранее почти никто не задумывается... скажем так, корпоративная культура это не поощряет, есть даже специальное ругательство для описания таких вещей: overquality.
      • 0
        Здесь дело не только в корпоративной структуре. Решив одну проблему вы тут же создаёте себе другую. Например если ваш скрипт будет мигрировать с одного компьютера на другой, то что случится с CLOCK_MONOTONIC - одному богу ведомо (скорее всего ничего хорошего), а синхронизированные через NTP часы будут продолжать идти как ни в чём ни бывало...
        • 0
          Глупости, ничего с ним не случится. Что за дурная привычка использовать вещи не по назначению?! CLOCK_MONOTONIC предназначен для надёжного отслеживания интервалов времени на одной машине. А для синхронизации операций или логов между машинами нужно использовать обычное текущее время и настроенный NTP (если, конечно, нет необходимости в абсолютно надёжной синхронизации - иначе придётся вводить свой внутренний протокол для этой цели).

          Всё должно быть на своём месте. Использовать текущее время для отслеживания интервалов так же неправильно, как использовать монотонное время для синхронизации разных машин.
          • 0
            А нужно ли умножать сущности без необходимости ? Сначала мы решаем проблему синхронизации времени через NTP, потом вводим CLOCK_MONOTONIC для решения "другой задачи", после чего вводим миграцию процессов и нам снова нужно решать исходную задачу.

            Ну нет никакого "надёжного времени" в случае если у вас под задачу не выделена железка! Всё чего можно добиться - того, чтобы время не ходило "взад". Ну так это почти никогда не проблема: нужно очень криво написать код, чтобы он при времени, пошедшем взад не просто неправильно отсчитал интервал, но вообще с ума сошёл (хотя и возможно, конечно - скажем uint использовать). А задержки в не realtime-системе могут случиться какие угодно всё равно по 1000 причин!
            • 0
              И снова в Вас говорит какой-то юношеский максимализм. Да, безусловно, бывают ситуации, когда сущности умножать с помощью CLOCK_MONOTONIC не стоит.

              Но, по моему опыту, ситуации когда использование CLOCK_MONOTONIC позволяет увеличить надёжность кода даром возникают значительно чаще. Просто нужно отдавать себе отчёт, когда нужно вызывать time(), а когда clock_gettime(CLOCK_MONOTONIC).
            • 0
              Что касается задержек на не-realtime, то для организации таймаутов это не так важно. В большинстве случаев срабатывание таймаута позже ничего не портит, в отличие от срабатывания раньше.
            • 0
              Конкретно, указанную автором задачу, оптимальней всего делать как он и предложил. Я сталкиваюсь с кучей серверов, где ntp не натраивается админом и время гуляет плюс/минус страшно сказать. Однажды один из админов поправит вручную число и заснут мои скрипты. . Так что powerman выручил меня, я реально переживал поводу несанкционированной смены даты. Риспект. Больше всего я обрадовался, что этот модуль есть в "стандартной поставке" perl.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          А Вы пошлите их подальше, и идите во фриланс. Клиентов много, хороших программистов мало. Всегда можно найти такого, которому качество будет важнее скорости или цены. Например, посмотрите, какой фильтр "на клиентов" стоит у меня на корневой странице сайта.

          Подумайте вот ещё о чём. Чем больше вокруг программистов пишущих дёшево и сердито быстро, тем больше ценятся люди, которые умеют писать качественно. А программисты, усмирявшие свой оверквалити годами, зачастую уже не могут начать думать иначе и писать качественно. Да и зачем - ведь и так работает... большую часть времени.

          Ну и самое главное. Если работаешь "чисто за бабки", а душой отдыхаешь в воскресенье на рыбалке - овеквалити нафиг не нужен. Но если программирование - это для души, то усмиряя душевные порывы, будь то стремление к оверквалити или чему-то другому, Вы убиваете в себе программиста, теряете возможность работать для души, и, постепенно, начинаете работать "чисто за бабки". В результате - жизнь просто проходит мимо... в 60 лет Вы это поймёте, но будет уже поздно.

          Так что программируйте так, как Вам кажется правильным, и заставляйте остальных подстраиваться под Вас, а не наоборот!
          "Не стоит прогибаться под изменчивый мир, пусть лучше он прогнётся под нас!"
          (с) А. Макаревич.
          • НЛО прилетело и опубликовало эту надпись здесь
          • –1
            Ну использование PERL'а - это всегда субоптимальное решение, так что неясно о чём говорить. Снявши голову по волосам не плачут...

            PERL - это такой "COBOL сегодня", то есть неплохой способ обеспечить себя на старости куском хлеба с маслом (программисты на COBOL сейчас 40 после его расцвета получают больше, чем люди знающие про epoll(4) и clock_gettime(3) - и с программистами на PERL будет та же история). Но писать на PERL и рассуждать об overquality - это по меньшей мере дикость...
            • 0
              Нда. Вы готовы отвечать за свои слова, или это было из серии - гав, и в будку?

              Я не могу сказать ничего хорошего про Perl, равно как и плохого про другие языки. Язык, как язык. У разные языков разные сильные и слабые стороны, разные ограничения по области применимости. Но при чём здесь качество?

              В общем, очень хочется услышать здравые аргументы, подкрепляющие Ваше высказывание.
              • –1
                PERL в приципе не предназначен для написания качественного кода. Ибо качественный код - это в первую очередь такой код, который легко читается и понимается.

                Есть языки, которые ставили себе задачу упростить написание качественного кода (Ada, Java, отчасти даже C++, хотя тут всё весьма непросто) и, с переменным успехом, этого достигли. PERL же действует строго в обратном направлении. Конечно можно воспринять это как вызов и пытаться писать качественные программы на языке, который этому не просто не способствует, а провоцирует писать некачественно - но зачем ?
                • 0
                  А по-моему просто некоторые "программисты" не предназначены для написания качественного кода. На любом языке.
                • +1
                  Теперь я Вас понял.

                  Perl предоставляет большее кол-во степеней свободы, чем многие другие языки, это правда. И ещё правда, что многим людям нельзя давать свободу, они просто не умеют ей пользоваться. В случае Perl это означает, что многие пользуются предоставленной свободой неправильно и пишут write-only код, и это тоже правда.

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

                  Если Вас язык провоцирует писать некачественно - значит Вам на этом языке работать пока рано. Ибо не язык должен вами управлять, а Вы языком. Хороший программист на любом языке будет писать хорошо.
    • +1
      Сколько угодно. И если админы всё правиьно настроили то никаких проблем нет и не будет. Почему ? Потому что несмотря на уверения в том, что все пытаются ездить на квадратных колёсах на самом-то деле они овальные, почти круглые!

      Если людм нужно чтобы часы были синхронизированы (а на бирже без этого нельзя), то это делается через NTP. А что это значит ? А это значит что the system time value is "drifted" (rather than changed), to avoid the obvious problems that would arise with "negative time changes". То есть демон увидев что ему нужно сдвинуть часы на пять минут вперёд будет сдвигать часы, скажем, на секунду за полчаса (на самом деле на секунду за 2000 секунд) - и в конце-концов всё синхронизируется. А если часы нужно сдвинуть назад - то он будет часы притормаживать и, опять-таки, время никуда не сбежит. В общем man ntpd вам в помощь.

      При загрузке системы - там да, делается резкий перевод стрелок. Но в это время ещё нет никаких скриптов, которые могли бы пострадать. Опять-таки если часы у вас в компьютере идут СИЛЬНО неправильно (скажем уходят вперёд или назад секунд на 10 за час), то начнутся проблемы - но в этих случаях правильнее будет разобраться с часами, чем пытаться исправить проблемы в десятках и сотнях программ. Kerberos, скажем, CLOCK_MONOTONIC не спасёт - ему по настоящему синхронизированные часы нужны...
      • 0
        Всё правильно. Только у этого подхода есть и тёмная сторона. Из-за таких вот бесконечных workaround-ов в одном месте, вместо того чтобы решить проблему раз и навсегда в другом месте, мы и имеем то, что имеем. Типичный пример - quirks mode в браузерах при разборе html-страничек.

        А вот если бы в NTP такие workaround-ы не встраивали, и биржа пару раз глюкнула бы как следует, глядишь, пофиксили бы софт, чтобы он не сбоил из-за коррекции времени! Эх, мечты, мечты...
        • 0
          А вот если бы в NTP такие workaround-ы не встраивали, и биржа пару раз глюкнула бы как следует, глядишь, пофиксили бы софт, чтобы он не сбоил из-за коррекции времени!

          Ну да - при таком раскладе drift бы встроили в NTP после того как кто-то потерял бы кучу денег. Разницы-то ?

          Надо понимаеть что если есть хоть какая-то возможность заставить работать кучу уже существующего софта никто в здравом уме и твёрдой памяти не будет ничего переписывать. Просто нерационально.

          А CLOCK_MONOTONIC... Теперь вместо одного костыля придётся поддерживать два. Хотя и первый прекрасно работал...
          • 0
            Ну да - при таком раскладе drift бы встроили в NTP после того как кто-то потерял бы кучу денег. Разницы-то ?
            Во-во, именно поэтому я и не выдвигаю никаких конструктивных предложений, а ограничиваюсь "Эх, мечты, мечты...". Есть есть возможность сделать неправильно - ей обязательно воспользуются. Ибо лень. Ибо дешевле. Ибо быстрее. Ибо позволяет не напрягаться в поисках правильного решения.

            LOOP:
            Но неправильные решения всегда аукаются чуть позже, в каком-нить другом месте. Но и эту проблему снова решают неправильно, т.к. решить её правильно - это-ж надо начинать с переделывания ещё того, первого неправильного решения! А кто за это башлять будет?

            И так всё и работает, на соплях и подвязочках, пока не рассыпется окончательно. И только тогда выделяют деньги на переделывание всего с нуля. Казалось бы - ура, наконец-то можно будет сделать всё правильно!!! Ага. Щаззз. Фигвам (народная индейская изба). Ибо лень. Ибо надо думать, напрягаться. Ибо "и так будет работать"... большую часть времени. И снова начинают плодить кривые workaround-ы.
            goto LOOP;
            • 0
              man 3 adjtime

              NOTES
              The adjustment that adjtime() makes to the clock is carried out in such a manner that the clock is always monotonically increasing. Using adjtime() to adjust the time prevents the problems that can be caused for certain applications (e.g., make(1)) by abrupt positive or negative jumps in the system time.

              И ещё: man adjtimex

              Один я не вижу проблемы?
  • 0
    Ну что вы все набросились?

    Человеку нравится программировать, он нашел ошибку, с удовольствием ее исправил, всем про это раструбил, теперь ходит гордый.

    А вы начинаете - в NTP и так все сделано, the clock is always monotonically increasing, проблемы нет, все давным-давно реализовано..

    Не обламывайте кайф! Это ведь так приятно - сознавать, что ты оказался полезен людям. :)

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