TCP или не TCP в целом не ясно - это может быть и самодельный UDP шайтан-протокол. Connection
- абстракция. В целом ясно, что стурктура данных heap
AKA priority queue
. Хочется простого: 10 сек не было данных от клиента - close()
ему на сокет. Архитектура традиционная - однопоточный евент луп - сидит в epoll_wait()
с пробуждениями, скажем, 10 раз в сек, чтобы поделать служебное (позакрывать по таймауту кого-то, другие таймеры попроцессить). Входящих коннектов могут быть тыщи. Обычно для такого юзают heap (min-heap например): пихаешь в эту структуру коннекшены, впереди лежат те, у которых значение time_close_nanosec
меньше. time_close_nanosec
- это такое uint64_t значение у объекта-connection, которое апдейтится каждый раз, когда из коннекшена выпали байтики и означает «monitonic время, при наступлении которого коннект надо закрыть». Соответственно апдейтится оно на now_nanosec + 10 * 1000 * 1000 * 1000
всякий раз, когда от клиента что-то прилетело.
Проблема в том, что time_close_nanosec
таки апдейтится. Прилетели из сокет байты - время close()
этого сокета надо отложить до now + 10 * 1000 * 1000 * 1000
как выше сказано. А это значит, в heap
его надо перевставить, ведь старая позиция в priority queue
уже не валидна. А значит в объекте Connection
будет лежить индекс этого Connection
в нашем heap
(чтобы сходить по этому индексу в этот heap
для удаления этого Connection
). А ещё, heap
должен быть так реализован, что при каждом перетасовывании объектов в нём, heap
ходит по указателям в тасуемых и апдейтит им индексы-в-себе на новые (ведь могут вставить кого-то на позицию 0 и я стану допустим 1). То есть, если из клиентов просто регулярно спокойно летят байтики, то эта heap
постоянно шевелится и идёт возня с памятью. Это не православно.
Вот пришла в голову оптимизация: time_close_nanosec
в объекте Connection
апдейтить, а в heap
этот Connection
не перевставлять. Тогда heap
будет постоянно «ложно» срабатывать. И вот в этом ложном срабатывании мы и будем делать всю работу в heap
- если срабатывание реально ложное (time_close_nanosec
ещё не достигло текущего момента), то передобавлять его в heap
, иначе это не ложное и делать close()
. Ясно, что в такой heap
надо будет хранить не просто указатели на Connection *
(по которым будет лазить компаратор, чтобы посмотреть на time_close_nanosec
(это вредно, они постоянно меняются и структура развалится)), а ещё и копию time_close_nanosec_COPY
которая верна на момент вставки и по которой компаратор работает.
В результате этой оптимизации число операций с heap
наверное минимально возможное и постоянно нормально льющиеся байтики от клиентов никак её не трогают, но всё работает как задумано. «Срабатывание heap
» - это не что-то плохое, это один if
в евент лупе: текущее время с корнем кучи сравнить.
(UPDATE: сделал эту идейку, работает на первый взгляд норм)
Штош, может кто что посоветует по этой теме? Как то же самое сделано в ядре например или где-то ещё в похожем месте. Додумался ли я до чего-то крутого или есть ещё круче оптимизации?