LINUX.ORG.RU

select для сокетов и синхронизационных примитивов

 ,


0

2

Задача примерно такая: есть multiprocessing.connection.Listener (по сути, просто TCP сервер), который в бесконечном цикле принимает подключения, для простоты будем считать, что всё это в одном потоке.

Как правильно сделать так, чтобы при установке некоторого флага, например экземпляра threading.Event, он выходил из этого цикла, независимо от входящих подключений. Загвоздка в том, аналога select, который бы принимал примитивы я не нашёл и том, что метод accept у Listener'а блокирующий. Есть приближенное решение с подсовыванием в multiprocessing.connection.wait внутреннего сокета Listener'а и Connection'а, который будет играть роль флага, но это слишком костыльно по мне.


But lock-free, race-cond free..

anonymous
()

закрыть какой-нибудь сокет, который был передан в select(), select() вернётся с ошибкой. Сам не проверял

Harald ★★★★★
()

Суть задачи ты так и не описал. Зато описал только своё видение решения это неизвестной задачи.

Не ясно, что тебе мешает просто вызвать метод close в нужный момент.

WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Вот код:

from multiprocessing.connection import Listener

with Listener(addr) as listener:
  while True:
    with listener.accept() as conn:
      ... работаем с сокетом ...

Вопрос в том, как правильно/хорошо/идиоматично выйти из этого цикла по событию, не отправляя ничего листенеру. Вроде у него нет метода вроде shutdown, как у классов из socketserver.

WDWTFWW
() автор топика
Ответ на: комментарий от WDWTFWW

а зачем из него выходить вообще, серверы работают в бесконечном цикле, пока питание не выключат или сигналом не прибьют

Harald ★★★★★
()
Ответ на: комментарий от Harald

сигналом не прибьют

Так в этом и проблема - если я ловлю sigterm, то сам должен всё аккуратно завершить. А делать sigkill и надеяться, что оно само как-нибудь, ну, это такое дело, неблагодарное.

WDWTFWW
() автор топика
Последнее исправление: WDWTFWW (всего исправлений: 1)
Ответ на: комментарий от WDWTFWW

А делать sigkill и надеяться, что оно само как-нибудь, ну, это такое дело, неблагодарное.

ОС всё освободит, что надо, инфа 100%

Harald ★★★★★
()
Ответ на: комментарий от menangen

Так поток будет стоять на блокирующем accept, т.е. если этот флаг установить, то выход всё равно произойдёт только после того, как следующее подключение будет обработано.

WDWTFWW
() автор топика
Ответ на: комментарий от Harald

ОС всё освободит, что надо, инфа 100%

В этом-то я не сомневаюсь, поинт в том, что это завершение надо обработать, а KILL, если что, нельзя словить в питоне.

WDWTFWW
() автор топика
Ответ на: комментарий от WDWTFWW

В упор не вижу проблемы. Регистрируешь коллбек и обрабатываешь сигнал, в чём проблема-то? Если тебе нужно что-то освободить или сделать в контексте, то юзай ООП и воткни объект приложения в коллбек с помощью лямбды или functools.partial.

WitcherGeralt ★★
()

и том, что метод accept у Listener’а блокирующий.

у сокета неблокирующий и поллится тем же селектом как read.

Есть приближенное решение с подсовыванием в multiprocessing.connection.wait внутреннего сокета Listener’а и Connection’а, который будет играть роль флага, но это слишком костыльно по мне.

Не костыльно, разрешаю. Можешь даже пайп открыть и совать его в select стороной на чтение, а лупу сигнал подавать записью.

anonymous
()

На чистом питоне никак, потому что нужен eventfd, а он не обернут, ибо linux-only. В винде уже 25 лет как есть эта фича, а в посикс никак не завезут.

byko3y ★★★★
()
Ответ на: комментарий от anonymous

Можешь даже пайп открыть и совать его в select стороной на чтение, а лупу сигнал подавать записью

Не «даже», а это единственный posix-совместимый способ.

byko3y ★★★★
()
Ответ на: комментарий от WitcherGeralt

Проблема не в том, чтобы словить сигнал. Вот минимальный код, который описывает эту ситуацию.

from multiprocessing.connection import Listener
from threading import Event
from signal import signal, SIGTERM

addr = ('localhost', 11000)
stop_event = Event()
listener = Listener(addr)

def stop_listener(*args):
    stop_event.set()

def main():
    while not stop_event.is_set():
        with listener.accept() as conn:
            data = conn.recv()
            conn.send(data)

signal(SIGTERM, stop_listener)
main()
Теперь, внимание, вопрос - что произойдёт, когда придёт SIGTERM? Ничего, но stop_event будет установлен и на следующем запросе будет выход из цикла. Я ещё просмотрел документацию и увидел Listener.close, который в крайнем случае подойдёт, но он бросает исключение и всё равно немного не то.

А есть такой вариант, который работает, но он мне не нравится тем, что лезет во внутренности Listener'а.

from multiprocessing import Pipe
from multiprocessing.connection import Listener, wait
from threading import Event
from signal import signal, SIGTERM

addr = ('localhost', 11000)
signal_r, signal_w = Pipe(False)
listener = Listener(addr)

def stop_listener(*args):
    signal_w.send(None)

def main():
    while True:
        connections = wait([listener._listener._socket, signal_r])
        if signal_r in connections:
            break
        with listener.accept() as conn:
            data = conn.recv()
            conn.send(data)

signal(SIGTERM, stop_listener)
main()

Поэтому я и спросил о том, есть ли что-то вроде select'а, который может принимать и объекты с дескриптором, и питоновские примитивы, и очереди, или это вообще правильно делать по-другому.

WDWTFWW
() автор топика
Ответ на: комментарий от WDWTFWW

Я ещё просмотрел документацию и увидел Listener.close

Я тебе это в первом же комментарии посоветовал.

но он мне не нравится тем, что лезет во внутренности Listener’а

Ну, а что ты хотел? Твои хотелки только манкипатчинг и удовлетворяет.

WitcherGeralt ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.