Есть объект Connection, представляющий соединение. Соединение менеджится epoll-ом. На всякие события, происходит вызов Connection::eventRead() или там Connection::eventWrite().
Connection персистентный, т.е. существует вечно. Что будет если его дропнут - рассмотрено и обработано.
И вот из другого потока я хочу через этот Connection() отправить данные. Для этого в другом потоке надо запихнуть пачку данных в Connection() (в его буфер), перевести состояние Connection в «посылай запрос» и пнуть Connection::eventWrite() - тогда оно эту пачку упихает в дескриптор в один или несколько приёмов (уже по дальнейшим событиям от epoll). Ну там внутри Connection пачка данных ещё и оформляется по нужному протоколу, но это уже не суть, это происходит в момент запихивания.
Казалось бы, всё ништяк. Но я не хочу трогать Connection() из разных потоков. Можно добавить межпоточную синхронизацию в Connection(), но можно ли обойтись?
Хочется epoll-потоку сказать «проснись из epoll_wait() и сделай такую-то хрень». Он должен проснуться из epoll_wait() и найти специальный флаг «сделать хрень» во взведённом состоянии и хрень осуществить. В хрени будет подготовка Connection() к отправке данных и поток снова уйдёт в epoll_wait(). Короче, работа с Connection будет вестись всегда в одном потоке: epoll-потоке.
Как будить epoll_wait()? Ну например можно завести pipe, добавить его в этот же epoll и пихать туда байт 1, а реакцей на побудку epoll_wait() от дескриптора этого pipe и будет моя хрень. А какие ещё существуют способы? Какие есть механизмы IPC, выразимые в (int) дескрипторах и отслеживаемые epoll? И какой из всех 100 вариантов самый быстрый?
P.S.
Вариант с eventfd:
Постинг задачи
// можно и не атомик, а сырой поинтер
std::atomic<T_TASK*> task_;
// Этикетка метода: !!! WARNING !!!
// Запрещено постить 2 раза подряд, очереди нет!
// Запостил - жди исполнения, иначе жопа.
bool exec_task(T_TASK *_task) {
task_ = _task;
LOG_PURE("... ... post task");
uint64_t add = 1;
::write(fd_event_, &add, sizeof(uint64_t));
return true;
}
Исполнение задачи:
// цикл разгребания пачки событий от epoll
...
if ( events[i].data.fd == fd_event_ ) {
if (events[i].events & EPOLLIN) {
uint64_t val;
// TODO: check read() return value.
// TODO: check val's value.
read(fd_event_, &val, sizeof(uint64_t));
(*task_)();
}
continue;
}
...