Вопрос: где здесь клиент запрашивает ответ, тем самым заставляя socket выкинуть POLLOUT в poll'е?
Это работает не так, с чего бы клиент
должен запрашивать ответ? Есть возможность запись в сокет без блокировки, поэтому POLLOUT. Если использовать твое представление о сокетах, в котором обмен данными очень похоже на обмен по HTTP - то нет нужды регистрировать POLLOUT пока не сформируешь ответ. Его если и надо регистрировать, то только если хочешь что-то отправить. А сама "работа" с сокетами зависит от используемых протоколов.
Можно вообще забить на отслеживание события POLLOUT и при отправке данных используя try/except блок которым обёрнут `socket.send` отслеживать EAGAIN/EWOULDBLOCK код ошибки в эксепшене, если возник такой - пытаться отправить снова на следующем лупе - меньше возни и ни на что не особо не влияет (при условии, что клиент данные из сокета читает)
А можно скомбинировать подход. Изначально POLLOUT не отслеживается, если есть данные на отправку - попытаться отправить, если не все данные были отправлены - регистрировать POLLOUT, когда событие придет - отправить оставшееся, если снова не все данные были отправлены - снова ждать POLLOUT
чтобы классифицировать каждый дескриптор, нужно делать что-то типа: result_event & POLLOUT == POLLOUT?
Да
result_event & POLLOUT
или например
result_event & (POLLERR|POLLHUP|POLLNVAL)
selectors здесь нет.
модуль selectors это абстракция над поллингом, я, конечно, не уверен и что внутри либы уже не особо помню, но возможно, если просто скопировать код либы себе - всё заработает без особых проблем.
Неблокирующий сервис можно написать на тредах - хорошо если запросы быстрые и клиентов не очень много