Пример типичной системно-прикладной разработки, реализация своей «очереди сообщений» (event loop).
Использованием #epoll, вместо «традиционных» select & poll, для асинхронной работы через polling’а (и мультиплексирования).
Отслеживание файловых дескрипторов через #epoll выглядит более современно, меньше копирования памяти между user space и kernel space. А при появлении ожидаемых данных можно напрямую переходить к объекту или структуре данных, что важно при наблюдении за несколькими файлами и\или соединениями. Устраняется поиск «сработавшего» файлового дескриптора в индексных массивах, полноценное О(1) во всех случаях. Можно сразу же работать с теми экземплярами объектов, которые оборачивают тот файл или udp-поток, tcp-, quic-соединение, где появились новые данные.
Есть несколько готовых к использованию «очередей сообщений» (event loop'ов) — #libev, #libuv, #libevent. Для некоторых агент-серверные решений и брокера #RabbitMQ это подходит. Однако, в некоторых случаях AMQP-библиотеки не скрещиваются с уже готовыми «очередями сообщений». Потому что агентская часть может активно использовать асинхронно-реактивное программирование с хорошей и проверенной «горизонтальной масштабируемостью». Т.е. на агентской части выполняется много работы и реализация сделана через sharing nothing многопоточность. Это такая парадигма, когда не просто достигается не только горизонтальная масштабируемость через lock-free\wait-free, а так же исключается много вредного, как тот же cache ping-pong или false sharing. Внутри агентов идёт своё управление потоками с выделениями памяти. Не только в плане «динамической памяти» (heap, аллокаторы а-ля #jemalloc от #Facebook), но и приколами вокруг pinning страниц, учёта #NUMA и даже huge pages(меньше промахов в #TLB).
Почему бы не использовать epoll?
Библиотека не обязана вычитывает данные целиком из потока (сокета), а может забирает данные лишь до тех пор, пока не насытится автомат состояний (finite-state machine). Например, выполняется парсинг сущностей AMQP-протокола, которые, по мере накопления, передаются в обработчики указанные клиентом библиотеки.
И это плохо соотносится с тем, что используя #epoll надо выбирать какой вариант оповещений использовать:
• «по уровню» (level-triggered),
• «по фронту» (edge-triggered).
Особенности поведения отдельно взятой библиотеки может не позволять использовать работу «по фронту» (edge-triggered), т.к. библиотека не вычитывает полностью все данные из файловых дескрипторов.
Можно быть хоть пять раз technical lead и всё это прекрасно знать, но следует помнить, что как только в коде появляется флаг EPOLLET, то необходимо проводить аудит работы с потоками данных. Это избавляет команду от многих заморочек вокруг тестирования и ковыряния с каким-то совершенно непонятным поведением кода.
Про «Edge Triggered Vs Level Triggered interrupts»
#programming #linux #softdev #трудовыебудни
Decided to not use #libev and use #libevent instead for socket/timer/event loop/callback system. Other than I trust it more, I like the baked in #openssl support (will use for #telnet+tls later).
Additionally, going to try out sqlcipher (#sqlite3 + AES encryption baked in) for data storage. Everything will be stored in a sqlite3 database.
Using cmake and pkg-config, #C, sqlite3 (sqlcipher), libevent, and openssl.
Decided to just focus on developing the software on KDE neon distro (Ubuntu LTS) and worry about other OSes later. I spent too much time worrying about ease of build/install instructions for other operating systems instead of just deciding and moving forward.
К вопросу использования #epoll вместо хорошо знакомых и «традиционных» select & poll. Т.е. асинхронной работы с чем-либо посредством polling’а и мультиплексирования.
Недавно пришлось заниматься реализацией очереди событий для AMQP-CPP. В одном из продуктов решено сделать связь агентских частей с основным «контроллером» через #AMQP, в качестве брокера #RabbitMQ (всё стандартно, обычный кластер и TLS-соединения).
Вот только агенты продукта активно используют асинхронно-реактивное программирование с хорошей «горизонтальной масштабируемостью». Когда достигнуто полноценное sharing nothing, не просто горизонтальная масштабируемость через lock-free или wait-free и закон Амдала. Исключается много всего и сразу, как старый-добрый cache ping-pong, так и печаль с false sharing.
Отсюда внутри агентов и своё управление потоками с выделениями памяти. Не только в плане heap (динамической памяти, со своими аллокаторами а-ля #jemalloc от #Facebook), но и приколы вокруг узлов #NUMA и даже huge pages (снижающих «давление» на #TLB, меньше промахов).
Первая же проблема выплыла почти сразу — не реально использовать библиотеку AMQP-CPP с уже предоставляющейся поддержкой #libev, #libuv, #libevent. Несовместимы эти очереди сообщений с имеющейся моделью управления потоками и организации задач на агентах.
Почему был взят epollПодход используемый в #epoll выглядит более современно, меньше копирований памяти между user space и kernel space. А при появлении данных в отслеживаемом файловом дескрипторе можно напрямую перейти по указателю на объект класса или структуру данных. Тем самым обходиться без поиска дескриптора по индексным массивам/контейнерам. Сразу же работать с экземплярами объектов оборачивающих нужное #tcp -соединение, того самого, в которое и пришли данные.
И тут обозначилась вторая проблема, что используема AMQP-библиотека не вычитывает данные целиком из потока сокета. Например, забирает данные лишь до тех пор, пока не насытится автомат состояний (finite-state machine), выполняющий парсинг сущностей AMQP-протокола.
Используя #epoll приходится выбирать на какой вариант обработки событий ориентироваться:
И беда с библиотекой в очередной раз показала, что нельзя использовать работу «по фронту» (edge-triggered) не изучив досконально работу подсистемы отвечающей за вычитывание данных из файловых дескрипторов. И появление флага EPOLLET в коде является маркером, о том, чтобы проводить аудит использовавшихся решений.
Про Edge Triggered Vs Level Triggered interrupts можно почитать в https://venkateshabbarapu.blogspot.com/2013/03/edge-triggered-vs-level-triggered.html)
#tmux wykrzacza się z #libevent 2.2.1 (alpha). Niestety, komunikat błędu wypisywany jest na nowym terminalu, a tmux od razu go zamyka i czyści ekran.
Próbowałem wydostać tekst poprzez przekierowanie wyjścia, poprzez logi tmuksa, nie udało się nawet przy pomocy strace (!). Dopiero #asciinema dało radę.
Kolejne zastosowanie dla tego świetnego programu. Szczerze polecam!
#tmux crashes with #libevent 2.2.1 (alpha). However, the assertion message only gets printed inside the new terminal, and tmux closes it immediately and clears the screen.
I couldn't manage to get the message via redirecting output, via tmux logs, even via strace (!). Finally, I've figured out to use #asciinema.
Another great use case for this great program. Kudos!
Today's #Gentoo fun:
tmux fails to build with #libevent 2.2.1. It fails because of incompatible declaration of forkpty() in compat.h.
Looking into compat.h. forkpty() is declared there if HAVE_FORKPTY is false.
Looking into config.log. forkpty() is not found because configure tries to link random test programs to -levent_core@CMAKE_DEBUG_POSTFIX@.
At this point, you can guess what the problem is: https://github.com/libevent/libevent/issues/1459
#glibc 2.36 introduces arc4random(). This makes #libevent switch from bundled to system ARC4 when rebuilt. Good so far.
However, glibc doesn't support arc4random_addrandom(), so libevent… removes evutil_secure_rng_add_bytes() and changes its ABI, effectively breaking installed #Tor (#Gentoo is especially affected).
Upstream argues that leaving the function as no-op is bad for #security However, when it's gone, Tor just skips the call.
https://github.com/libevent/libevent/issues/1393
https://gitlab.torproject.org/tpo/core/tor/-/blob/main/src/lib/evloop/compat_libevent.c#L483-485
Oh, groovy!
Also thanks to @eborisch #libevent in #MacPorts now has a commit merged with a patch that is a *little* bit ahead of libevent's versioned release in order to ameliorate some issues with #LibreSSL noticed on versions above 3.5+ (this is related to what I was testing earlier today):
https://github.com/macports/macports-ports/pull/16681
Hopefully that helps folks who were running into issues such as documented in this Trac ticket: https://trac.macports.org/ticket/66096
(Casually wonders why #MacPorts has
#libevent
https://ports.macports.org/port/libevent/ [more or less current with upstream]
and
#libevent1
https://ports.macports.org/port/libevent1/ [what is this doing here? I am so confused. Though obviously has some different things which depend on it such as honeyd, also by Niels Provos. So: maybe old and crufty stuff needs older libevent1?] )
There's still this #libevent diff that I have been meaning to test: https://github.com/macports/macports-ports/pull/16681/files
I have definitely noticed some occasional oddities with #LibreSSL vis-à-vis libevent, so hopefully that rectifies them?
Bug report submitted to #libevent to discuss adding a callback API for accessing the global namespace for better #FreeBSD #Capsicum support: https://github.com/libevent/libevent/issues/601
This will be needed to complete the Capsicum support in #Tor.