Что делать
How did they know?
Кстати долго ебался-ебался, пока не понял, что проблема состоит лишь в том, что у меня ивентлуп в какой-то момент, за отсутствием новых активных тасок, каким-то чудом снимает флаг неактивности с инактивной таски, которую я уже освободить успел
Перенёс очищение флага в обёртку, чтобы только при получении ивента из еполла его снимать, и всё зафурыкало. Правда, теперь он не выдерживает потока из тсп стресстестера
Перенёс очищение флага в обёртку, чтобы только при получении ивента из еполла его снимать, и всё зафурыкало. Правда, теперь он не выдерживает потока из тсп стресстестера
Что делать
Кстати долго ебался-ебался, пока не понял, что проблема состоит лишь в том, что у меня ивентлуп в какой-то момент, за отсутствием новых активных тасок, каким-то чудом снимает флаг неактивности с инактивной таски, которую я уже освободить успел Перенёс очищение…
А не выдерживает потому, что, оказывается, надо принимать подключения из серверного сокета до тех пор, пока EAGAIN не словишь. Иначе тебе больше никогда не придёт ивент, из-за чего accept-loop стопорится
Итак, у меня утечка памяти. Поставил принт и оказался прав - да, действительно, у меня просто какая-то из арен растёт по кд. Значит, где-то я не возвращаю указатели обратно.
Поскольку у меня куча разных арен под разные вещи, то нам придётся использовать advanced debugging technologies
По размеру типа гадать, какая именно арена раздувается
Поскольку у меня куча разных арен под разные вещи, то нам придётся использовать advanced debugging technologies
По размеру типа гадать, какая именно арена раздувается
Так, ну я расставил свои принты везде, где я вообще аллоцирую буфер для записи. Виновато 100% оно. Только вот есть проблема
Ни одна из вероятных точек не работает
Осталось только одно место - перед самим циклом диспатчинга ивентов. Но я всегда возвращаю тот буфер обратно при выходе из функции. Нихуя нипанятна
Ни одна из вероятных точек не работает
Осталось только одно место - перед самим циклом диспатчинга ивентов. Но я всегда возвращаю тот буфер обратно при выходе из функции. Нихуя нипанятна
Ладно, оно всё равно потихоньку утекает, где-то по 100 килобайт в минуту, если с 16 подключений постоянно сообщениями обмениваться. But it's fine
И опять. Как тогда арена росла, теперь какой-то список. Возвращаемся к advanced debugging technologies и определяем пидораса по размеру типа
🐳1
Отлично. Advanced debugging technologies показывают, что растёт какое-то говно с поинтерами
🐳1
Что делать
Отлично. Advanced debugging technologies показывают, что растёт какое-то говно с поинтерами
окей, у меня бесконечно растёт очередь ивентов. Подозреваю, что я просто по-ебаному веду себя со списком
Бляяя пиздец. Я же просто помечаю таску как неактивную, так? Выставляю стейту флаг EV_INACTIVE. А весь рофл в том, что я-то исключительно указателем на таску оперирую. Таким образом, когда таска вновь становится активной, она может заменить собой какую-то совершенно другую. При этом, старая, неактивная, которую мы ещё не успели заменить другой - тоже остаётся. А флаг снимается у обеих. Точнее, у неё одной, просто её уже две штуки. И чем дальше, тем хуже
Вот. 84a8 - это серверный сокет, означает, что новые подключения пришлёпали. Две таски двух подключений - 8358 и 8400. read error 0 означает EOF, т.е. что клиент закрыл подключение. Соответственно, NULLING означает, что я добавляю таску в очередь на освобождение. Проблема в том, что в то время, как я обрабатываю таски (это не логгируется), мне между делом прилетает ещё раз 8400. Соответственно, как только я получаю EOF из 8400 в активном пуле, я заменяю её первой таской из новых тасок. Ею оказывается она же. Соответственно, получаем уже EPIPE (поскольку мы со своей стороны тоже успели уже подключение закрыть). Мы не обращаем внимание и снова пихаем её в очередь на очищение. Таким образом, получаем double free.
Каким образом, зачем, почему еполл мне отправляет дубликат ивента - я в душе блять не чаю, это полный пиздец какой-то. Наверное, придётся костылить, и при EPIPE просто скипать таску. Ёбаный в рот.
Каким образом, зачем, почему еполл мне отправляет дубликат ивента - я в душе блять не чаю, это полный пиздец какой-то. Наверное, придётся костылить, и при EPIPE просто скипать таску. Ёбаный в рот.
https://stackoverflow.com/questions/4724137/epoll-wait-receives-socket-closed-twice-read-recv-returns-0
А вот и мой кейс. Отлично, значит, когда я дочитался до EAGAIN, и в это время сокет отключается - мне может прилететь в догонку ещё один ивент, с EPOLLRDHUP. Надо будет потом потыкать, как оно происходит. Потому что пока что я не понимаю алгоритм, по которому принимается решение выдать мне ивент повторно
А вот и мой кейс. Отлично, значит, когда я дочитался до EAGAIN, и в это время сокет отключается - мне может прилететь в догонку ещё один ивент, с EPOLLRDHUP. Надо будет потом потыкать, как оно происходит. Потому что пока что я не понимаю алгоритм, по которому принимается решение выдать мне ивент повторно
Stack Overflow
epoll_wait() receives socket closed twice (read()/recv() returns 0)
We have an application that uses epoll to listen and process http-connections. Sometimes epoll_wait() receives close event on fd twice in a "row". Meaning: epoll_wait() returns connection fd on which
Что делать
https://stackoverflow.com/questions/4724137/epoll-wait-receives-socket-closed-twice-read-recv-returns-0 А вот и мой кейс. Отлично, значит, когда я дочитался до EAGAIN, и в это время сокет отключается - мне может прилететь в догонку ещё один ивент, с EPOLLRDHUP.…
Я так и не понял, какие должны быть причины на появление этого ивента. Но у меня ещё была пара багов помимо этого, из-за чего даже добавлять EPOLLRDHUP в список интересов и специально пропускать такие ивенты особо не помогло. Решил пойти по другому пути
Теперь я просто объявлю общий интерфейс в ev.h, с функцией ev_run(), принимающей все нужные аргументы. Сама имплементация будет одна из нескольких C source файлов, где каждый файл отображает целевую операционную систему. Естественно, каждый будет в соответствующих гардах, чтобы под выбранную целевую платформу компилировалось только то, что нужно. Что, в целом, классическая схема, хоть и, как мне кажется, не самая надёжная.
Из минусов - больше нет штуки с разделением источника ивентов и самой логики ивентлупа. Правда, минус это с очень большой натяжкой, так как при портировании на kqueue я всё равно вынесу парочку общих функций (а-ля обработай один этот ивент - логика абсолютно агностична к платформе и окружению), да и всё равно, рано или поздно, придётся расширяться и на винду. А там iocp, который следует уже модели проактора, в отличии от реактора, как epoll и kqueue. Я пока до конца не разобрался, но ивенты я там буду получать совершенно иным образом. Соответственно, вынести исключительно атомарную логику (а не целый пласт, как это было раньше, где я полностью инкапсулировал весь ивентлуп) является потенциально выигрышным решением.
Теперь я просто объявлю общий интерфейс в ev.h, с функцией ev_run(), принимающей все нужные аргументы. Сама имплементация будет одна из нескольких C source файлов, где каждый файл отображает целевую операционную систему. Естественно, каждый будет в соответствующих гардах, чтобы под выбранную целевую платформу компилировалось только то, что нужно. Что, в целом, классическая схема, хоть и, как мне кажется, не самая надёжная.
Из минусов - больше нет штуки с разделением источника ивентов и самой логики ивентлупа. Правда, минус это с очень большой натяжкой, так как при портировании на kqueue я всё равно вынесу парочку общих функций (а-ля обработай один этот ивент - логика абсолютно агностична к платформе и окружению), да и всё равно, рано или поздно, придётся расширяться и на винду. А там iocp, который следует уже модели проактора, в отличии от реактора, как epoll и kqueue. Я пока до конца не разобрался, но ивенты я там буду получать совершенно иным образом. Соответственно, вынести исключительно атомарную логику (а не целый пласт, как это было раньше, где я полностью инкапсулировал весь ивентлуп) является потенциально выигрышным решением.
👍1