# Descritor de arquivo de temporizador

Versão:

  1.13

Este documento discute o suporte do Python para o descritor de arquivo de temporizador do Linux.

## Exemplos

O exemplo a seguir mostra como usar um descritor de arquivo de temporizador para executar uma função duas vezes por segundo:

    # Scripts práticos devem realmente usar um timer sem bloqueio.
    # Usamos um timer com bloqueio aqui para simplificar.
    import os, time

    # Cria um descritor de arquivo de temporizador
    fd = os.timerfd_create(time.CLOCK_REALTIME)

    # Inicia o temporizador em 1 segundo, com um intervalo de meio segundo
    os.timerfd_settime(fd, initial=1, interval=0.5)

    try:
        # Processa eventos do temporizador quatro vezes.
        for _ in range(4):
            # read() ser bloqueado até o temporizador expirar
            _ = os.read(fd, 8)
            print("Timer expired")
    finally:
        # Lembre-se de fechar o descritor de arquivo de temporizador!
        os.close(fd)

Para evitar a perda de precisão causada pelo tipo `float`, os descritores de arquivos de temporizador permitem especificar a expiração inicial e o intervalo em nanossegundos inteiros com variantes `_ns` das funções.

Este exemplo mostra como `epoll()` pode ser usado com descritores de arquivo de temporizador para esperar até que o descritor de arquivo esteja pronto para leitura:

    import os, time, select, socket, sys

    # Cria um objeto epoll
    ep = select.epoll()

    # Neste exemplo, usa o endereço de loopback para enviar o comando "stop" ao servidor.
    #
    # $ telnet 127.0.0.1 1234
    # Trying 127.0.0.1...
    # Connected to 127.0.0.1.
    # Escape character is '^]'.
    # stop
    # Connection closed by foreign host.
    #
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("127.0.0.1", 1234))
    sock.setblocking(False)
    sock.listen(1)
    ep.register(sock, select.EPOLLIN)

    # Cria descritores de arquivo de temporizador no modo não bloqueante.
    num = 3
    fds = []
    for _ in range(num):
        fd = os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
        fds.append(fd)
        # Registra o descritor de arquivo do temporizador para eventos de leitura
        ep.register(fd, select.EPOLLIN)

    # Inicia o temporizador com os.timerfd_settime_ns() em nanossegundos.
    # O temporizador 1 dispara a cada 0,25 segundos; o timer 2 a cada 0,5 segundos; etc.
    for i, fd in enumerate(fds, start=1):
        one_sec_in_nsec = 10**9
        i = i * one_sec_in_nsec
        os.timerfd_settime_ns(fd, initial=i//4, interval=i//4)

    timeout = 3
    try:
        conn = None
        is_active = True
        while is_active:
            # Aguarda o temporizador expirar por 3 segundos.
            # epoll.poll() retorna uma lista de pares (fd, event).
            # fd é um descritor de arquivo.
            # sock e conn[=valor retornado de socket.accept()] são objetos de socket, não descritores de arquivo.
            # Então use sock.fileno() e conn.fileno() para obter os descritores de arquivo.
            events = ep.poll(timeout)

            # Se mais de um descritor de arquivo de timer estiver pronto para leitura ao mesmo tempo,
            # epoll.poll() retorna uma lista de pares (fd, event).
            #
            # Neste exemplo de configurações,
            #    O 1º temporizador dispara a cada 0,25 segundos em 0,25 segundos. (0,25, 0,5, 0,75, 1,0, ...)
            #    O 2º temporizador dispara a cada 0,5 segundos em 0,5 segundos. (0,5, 1,0, 1,5, 2,0, ...)
            #    O 3º temporizador dispara a cada 0,75 segundos em 0,75 segundos. (0,75, 1,5, 2,25, 3,0, ...)
            #
            #    Em 0,25 segundos, apenas o 1º temporizador dispara.
            #    Em 0,5 segundos, o 1º temporizador e o 2º temporizador disparam ao mesmo tempo.
            #    Em 0,75 segundos, o 1º temporizador e o 3º temporizador disparam ao mesmo tempo.
            #    Em 1,5 segundos, o 1º temporizador, o 2º temporizador e o 3º temporizador disparam ao mesmo tempo.
            #
            # Se um descritor de arquivo de temporizador for sinalizado mais de uma vez desde
            # a última chamada de os.read(), os.read() retornará o número de sinalizados
            # como ordem de host de bytes de classe.
            print(f"Signaled events={events}")
            for fd, event in events:
                if event & select.EPOLLIN:
                    if fd == sock.fileno():
                        # Verifica se há uma requisição de conexão.
                        print(f"Accepting connection {fd}")
                        conn, addr = sock.accept()
                        conn.setblocking(False)
                        print(f"Accepted connection {conn} from {addr}")
                        ep.register(conn, select.EPOLLIN)
                    elif conn and fd == conn.fileno():
                        # Verifica se há dados para ler.
                        print(f"Reading data {fd}")
                        data = conn.recv(1024)
                        if data:
                            # Você deveria capturar exceção UnicodeDecodeError por segurança.
                            cmd = data.decode()
                            if cmd.startswith("stop"):
                                print(f"Stopping server")
                                is_active = False
                            else:
                                print(f"Unknown command: {cmd}")
                        else:
                            # Acabaram os dados, fecha a conexão
                            print(f"Closing connection {fd}")
                            ep.unregister(conn)
                            conn.close()
                            conn = None
                    elif fd in fds:
                        print(f"Reading timer {fd}")
                        count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)
                        print(f"Timer {fds.index(fd) + 1} expired {count} times")
                    else:
                        print(f"Unknown file descriptor {fd}")
    finally:
        for fd in fds:
            ep.unregister(fd)
            os.close(fd)
        ep.close()

Este exemplo mostra como `select()` pode ser usado com descritores de arquivo de temporizador para esperar até que o descritor de arquivo esteja pronto para leitura:

    import os, time, select, socket, sys

    # Neste exemplo, usa o endereço de loopback para enviar o comando "stop" ao servidor.
    #
    # $ telnet 127.0.0.1 1234
    # Trying 127.0.0.1...
    # Connected to 127.0.0.1.
    # Escape character is '^]'.
    # stop
    # Connection closed by foreign host.
    #
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("127.0.0.1", 1234))
    sock.setblocking(False)
    sock.listen(1)

    # Cria descritores de arquivo de temporizador no modo não bloqueante.
    num = 3
    fds = [os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
           for _ in range(num)]
    select_fds = fds + [sock]

    # Inicia o temporizador com os.timerfd_settime() em segundos.
    # O temporizador 1 dispara a cada 0,25 segundos; o timer 2 a cada 0,5 segundos; etc.
    for i, fd in enumerate(fds, start=1):
       os.timerfd_settime(fd, initial=i/4, interval=i/4)

    timeout = 3
    try:
        conn = None
        is_active = True
        while is_active:
           # Aguarda o temporizador expirar por 3 segundos.
           # select.select() retorna uma lista de objetos ou descritores de arquivos.
           rfd, wfd, xfd = select.select(select_fds, select_fds, select_fds, timeout)
           for fd in rfd:
               if fd == sock:
                   # Verifica se há uma requisição de conexão.
                   print(f"Accepting connection {fd}")
                   conn, addr = sock.accept()
                   conn.setblocking(False)
                   print(f"Accepted connection {conn} from {addr}")
                   select_fds.append(conn)
               elif conn and fd == conn:
                   # Verifica se há dados para ler.
                   print(f"Reading data {fd}")
                   data = conn.recv(1024)
                   if data:
                       # Você deveria capturar exceção UnicodeDecodeError por segurança.
                       cmd = data.decode()
                       if cmd.startswith("stop"):
                           print(f"Stopping server")
                           is_active = False
                       else:
                           print(f"Unknown command: {cmd}")
                   else:
                       # Acabaram os dados, fecha a conexão
                       print(f"Closing connection {fd}")
                       select_fds.remove(conn)
                       conn.close()
                       conn = None
               elif fd in fds:
                   print(f"Reading timer {fd}")
                   count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)
                   print(f"Timer {fds.index(fd) + 1} expired {count} times")
               else:
                   print(f"Unknown file descriptor {fd}")
    finally:
        for fd in fds:
           os.close(fd)
        sock.close()
        sock = None