Несерьёзный Выдумщик<p>К вопросу использования <a class="hashtag" href="https://idealists.su/tag/epoll" rel="nofollow noopener noreferrer" target="_blank">#epoll</a> вместо хорошо знакомых и «традиционных» select & poll. Т.е. асинхронной работы с чем-либо посредством polling’а и мультиплексирования.</p><p>Недавно пришлось заниматься реализацией очереди событий для AMQP-CPP. В одном из продуктов решено сделать связь агентских частей с основным «контроллером» через <a class="hashtag" href="https://idealists.su/tag/amqp" rel="nofollow noopener noreferrer" target="_blank">#AMQP</a>, в качестве брокера <a class="hashtag" href="https://idealists.su/tag/rabbitmq" rel="nofollow noopener noreferrer" target="_blank">#RabbitMQ</a> (всё стандартно, обычный кластер и TLS-соединения).</p><p>Вот только агенты продукта активно используют асинхронно-реактивное программирование с хорошей «горизонтальной масштабируемостью». Когда достигнуто полноценное sharing nothing, не просто горизонтальная масштабируемость через lock-free или wait-free и закон Амдала. Исключается много всего и сразу, как старый-добрый cache ping-pong, так и печаль с false sharing.</p><p>Отсюда внутри агентов и своё управление потоками с выделениями памяти. Не только в плане heap (динамической памяти, со своими аллокаторами а-ля <a class="hashtag" href="https://idealists.su/tag/jemalloc" rel="nofollow noopener noreferrer" target="_blank">#jemalloc</a> от <a class="hashtag" href="https://idealists.su/tag/facebook" rel="nofollow noopener noreferrer" target="_blank">#Facebook</a>), но и приколы вокруг узлов <a class="hashtag" href="https://idealists.su/tag/numa" rel="nofollow noopener noreferrer" target="_blank">#NUMA</a> и даже huge pages (снижающих «давление» на <a class="hashtag" href="https://idealists.su/tag/tlb" rel="nofollow noopener noreferrer" target="_blank">#TLB</a>, меньше промахов).</p><p>Первая же проблема выплыла почти сразу — не реально использовать библиотеку AMQP-CPP с уже предоставляющейся поддержкой <a class="hashtag" href="https://idealists.su/tag/libev" rel="nofollow noopener noreferrer" target="_blank">#libev</a>, <a class="hashtag" href="https://idealists.su/tag/libuv" rel="nofollow noopener noreferrer" target="_blank">#libuv</a>, <a class="hashtag" href="https://idealists.su/tag/libevent" rel="nofollow noopener noreferrer" target="_blank">#libevent</a>. Несовместимы эти очереди сообщений с имеющейся моделью управления потоками и организации задач на агентах.</p>Почему был взят epoll<p>Подход используемый в <a class="hashtag" href="https://idealists.su/tag/epoll" rel="nofollow noopener noreferrer" target="_blank">#epoll</a> выглядит более современно, меньше копирований памяти между user space и kernel space. А при появлении данных в отслеживаемом файловом дескрипторе можно напрямую перейти по указателю на объект класса или структуру данных. Тем самым обходиться без поиска дескриптора по индексным массивам/контейнерам. Сразу же работать с экземплярами объектов оборачивающих нужное <a class="hashtag" href="https://idealists.su/tag/tcp" rel="nofollow noopener noreferrer" target="_blank">#tcp</a> -соединение, того самого, в которое и пришли данные.</p><p>И тут обозначилась вторая проблема, что используема AMQP-библиотека не вычитывает данные целиком из потока сокета. Например, забирает данные лишь до тех пор, пока не насытится автомат состояний (finite-state machine), выполняющий парсинг сущностей AMQP-протокола.</p><p>Используя <a class="hashtag" href="https://idealists.su/tag/epoll" rel="nofollow noopener noreferrer" target="_blank">#epoll</a> приходится выбирать на какой вариант обработки событий ориентироваться:</p><ul><li>срабатывание оповещений «по уровню» (level-triggered),</li><li>выбрасывания событий «по фронту» (edge-triggered).</li></ul><p>И беда с библиотекой в очередной раз показала, что нельзя использовать работу «по фронту» (edge-triggered) не изучив досконально работу подсистемы отвечающей за вычитывание данных из файловых дескрипторов. И появление флага EPOLLET в коде является маркером, о том, чтобы проводить аудит использовавшихся решений.</p><p>Про Edge Triggered Vs Level Triggered interrupts можно почитать в <a href="https://venkateshabbarapu.blogspot.com/2013/03/edge-triggered-vs-level-triggered.html" rel="nofollow noopener noreferrer" target="_blank">https://venkateshabbarapu.blogspot.com/2013/03/edge-triggered-vs-level-triggered.html</a>)</p><p><a class="hashtag" href="https://idealists.su/tag/programming" rel="nofollow noopener noreferrer" target="_blank">#programming</a> <a class="hashtag" href="https://idealists.su/tag/linux" rel="nofollow noopener noreferrer" target="_blank">#linux</a> <a class="hashtag" href="https://idealists.su/tag/трудовыебудни" rel="nofollow noopener noreferrer" target="_blank">#трудовыебудни</a></p>