LINUX.ORG.RU

timertt-1.2.0

 ,


0

4

Мы обновили свою легковесную библиотеку для работы с отложенными и периодическими таймерами (wallclock-таймеры не поддерживаются в принципе). В этой версии добавлены две важные фичи:

1. Раньше действие для таймера всегда имело тип std::function<void()>, что было гибко и удобно, но имело скрытые накладные расходы, связанные с std::function (по сути, std::function тут выступал как умный указатель для лямбд и функторов). Если от этих скрытых расходов хочется избавиться, то можно задать свой собственный тип. Например:

class operation_canceler {
    operation_manager & manager_;
    operation_id id_;
public:
    operation_canceler(operation_manager & manager, operation_id id)
        : manager_{manager}, id_{id}
    {}
    void operator()() const noexcept
    {
        manager_.cancel(id_);
    }
};
...
// Define type of timer thread which was use operation_canceler as
// a timer action type.
using my_timer_wheel_thread = timertt::timer_wheel_thread_template<
    operation_canceler,
    timertt::default_error_logger,
    timertt::default_actor_exception_handler >;
...
// Create and use this timer thread.
my_timer_wheel_thread tt;
tt.start();
...
tt.activate( std::chrono::milliseconds(750), operation_canceler{manager, current_id});
Тип должен быть Moveable и MoveConstructible. Соответственно, теперь когда создается объект-таймер, там резервируется место для пользовательского объекта-функтора. А при активации таймера пользовательский функтор мувится в этот зарезервированный кусок памяти. Тем самым не происходит дополнительных аллокаций памяти (как это временами может происходить с std::function).

2. Теперь пользователь сам может создавать объекты-таймеры на стеке или внутри других своих объектов. Ранее объекты-таймеры создавались исключительно динамически. Теперь можно избежать дополнительных аллокаций, но зато пользователь сейчас сам должен следить за тем, чтобы таймер не закончил свою жизнь раньше времени. Например:

void do_something_complex() {
    timertt::default_timer_wheel_thread tt;
    tt.start();
    ...
    timertt::default_timer_wheel_thread::scoped_timer_object timer;
    // Activate
    tt.activate(timer, std::chrono::milliseconds(250), ...);
    ...
    // Timer can be deactivated in usual way.
    tt.deactivate(timer);
    ...
    tt.shutdown_and_join();
}

Правда, в этой версии мы несколько сломали совместимость на уровне исходного кода. Поэтому номер версии 1.2.0, а не 1.1.4.

Пару слов о происхождении и назначении библиотеки. Когда-то мы долго и с удовольствием использовали большую библиотеку ACE. В том числе и тамошние таймеры (реализация которых была добротной и продвинутой). Но по мере перехода на C++11 мы постепенно отказывались от ACE и в один прекрасный момент оказалось, что из ACE нам нужны только таймеры. Чтобы не таскать дистрибутив ACE только ради таймеров, мы сделали свою легковесную header-only либу, которая базируется только на штатных возможностях C++11.

У нас timertt в работе уже года три. Проблем не замечено. Работает стабильно, может поддерживать изрядное количество таймеров (десятки и сотни миллионов). Реализует три разных таймерных механизма: wheel, heap и list, каждый из которых хорош в своей ситуации.

Предыдущие версии timertt могли работать и с компиляторами, которые не очень хорошо поддерживали C++11 (в частности, VS2013). Начиная с 1.2.0 мы на такие компиляторы уже не оглядываемся. Нужно что-то более-менее нормальное (gcc 4.8-7.2, clang 3.5-5.0, vs2015/2017). Однако, основная часть кода пока еще под все возможности C++ (вроде noexcept и constexpr там, где это разумно) еще не адаптирована. Сделаем это со временем.

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

Архивы с исходниками библиотеки можно найти здесь. Сами исходники живут здесь. Документация здесь.

Лицензия: BSD-3-CLAUSE.

Disclaimer: если вы уже используете таймеры, которые предоставляют такие инструменты, как ACE, asio, libuv, Qt и пр., то вряд ли вам нужна timertt. А вот если вам потребовалось в программе работать с отложенными или периодическими действиями, а тащить в проект ACE/asio/libuv, которых там не было... Или если у вас миллионы таких действий, то вот тогда timertt вам может помочь. В общем, для большинства — это образцовое ненужно. Но вот нам потребовалось, может кому еще пригодится.

ЗЫ. Если кому-то интересно, то самый первый анонс timertt вызвал забавный срач.

★★★★★

если вы уже используете таймеры, которые предоставляют такие инструменты, как ACE, asio, libuv, Qt и пр., то вряд ли вам нужна timertt. А вот если вам потребовалось в программе работать с отложенными или периодическими действиями, а тащить в проект ACE/asio/libuv, которых там не было... Или если у вас миллионы таких действий, то вот тогда timertt вам может помочь.

Расскажи, для тех кто в танке, что вообще такое эти твои таймеры? Зачем их может потребоваться создавать «десятки и сотни миллионов»?

Crocodoom ★★ ()
Ответ на: комментарий от DELIRIUM

Понятно, а я подумал, что это вы кресты буквами t обозначили)

Автор давно не школьник, чтобы такой ерундой маяться (заменять + на t, i на 1, о на 0).

anonymous ()
Ответ на: комментарий от Crocodoom

Расскажи, для тех кто в танке, что вообще такое эти твои таймеры? Зачем их может потребоваться создавать «десятки и сотни миллионов»?

Допустим, у вас распределенное приложение и для общения между частями вы используете MQ-шный брокер и механизм publish/subscribe. У вас есть некая сущность «менеджер» и этот менеджер хочет спросить у некой сущности «исполнитель №117» насколько этот исполнитель загружен. Менеджер публикует сообщение «tell_load» в топик «/workers/117/cmds». После чего начинает ждать ответа. Но ответ, в принципе, может не прийти. Если ответ не пришел за 5 минут, менеджер может повторить запрос. Вот эти 5 минут нужно как-то отсчитать. Для этого менеджер после публикации сообщения «tell_load» взводит для себя отложенный таймер. Когда таймер срабатывает, менеджер понимает, что исполнитель ему ничего не ответил.

С другой стороны, у исполнителя может быть обязанность раз в 15 минут сообщать всем, что он жив-здоров и занимается какой-то полезной работой (или же стоит и ждет этой самой работы). Для этого исполнитель взводит периодический таймер с темпом 15 минут. Когда таймер срабатывает, исполнитель публикует очередное сообщение о своем состоянии.

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

eao197 ★★★★★ ()
Ответ на: комментарий от DELIRIUM

Думаю, это он из вежливости.

Кстати, в названии последняя t лишняя, потому что шаблонов их как бы три, но два из них для очень экзотических случаев, а потому можно обойтись без шаблонов. Предпоследняя t с предыдущего выпуска уже тоже как бы лишняя, ибо отдельный поток стал не обязателен (и это правильно, потому что поток таймеров полезно совместить с потоком, где делается epoll). Боюсь, что такими темпами в следующем выпуске, и первая t потеряет смысл. Development он такой, да...

anonymous ()
Ответ на: комментарий от eao197

да если честно без разницы. Просто нужен пруф покрытия тестами нужных возможностей.

Я пока потерялся в коде. Зачем, например, там собственная реализация хипа?

anonymous ()
Ответ на: комментарий от anonymous

Зачем, например, там собственная реализация хипа?

Этот вопрос нужно было бы задать года три назад, когда руки помнили то, что было сделано. Видимо, по каким-то причинам средства из STL-я не подошли.

eao197 ★★★★★ ()
Ответ на: комментарий от anonymous

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

Почему это не описано в комментариях в коде? Вероятно потому, что это проектное решение, документировать которое в коде не имело смысла.

Как бы то ни было, у меня с недавних пор на LOR-е правило: я не отвечаю на вопросы анонимов. Здесь сделал исключение, но если у вас есть какие-то вопросы, то зарегистрируйтесь на LOR-е, пожалуйста.

Регистрация на LOR-е позволяет мне тратить меньше времени на идентификацию собеседника и на понимания того, какую пользу общение может принести проекту. Если же вы не хотите тратить свое время на создание аккаунта на LOR-е, то я не хочу тратить свое время на общение с анонимами, поскольку в большинстве случаев это общение бесплодно. Прошу отнестись с пониманием.

eao197 ★★★★★ ()