> For the complete documentation index, see [llms.txt](https://concorrencia101.leandronsp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://concorrencia101.leandronsp.com/parte-i-concorrencia-no-sistema-operacional/e-o-i-o/chamadas-nao-bloqueantes.md).

# Chamadas não-bloqueantes

O sistema operacional fornece um recurso muito interessante de chamadas não-bloqueantes em I/O. Desta forma, o programa recebe imediatamente um *file descriptor* que representa o I/O solicitado, e depois recorre a recursos do sistema operacional para verificar quando o recurso está pronto. Assim, o programa não fica bloqueado.

Esta é a definição de **assincronismo**, ou **I/O assíncrono**.

<figure><img src="/files/RveM1iRybVAsd4KMmJgv" alt="" width="563"><figcaption></figcaption></figure>

Em Linux, chamadas de sistema como **select** ou **epoll** permitem controlar I/O assíncrono. Com select o programa verifica quais descritores estão prontos, enquanto que no epoll, o SO notifica o programa através de uma fila quais descritores estão prontos.

> Também não vamos por enquanto entrar nos detalhes do uso de select ou epoll, isto fica numa seção futura neste guia quando entrarmos na parte de implementação. Aqui, é importante *entendermos os conceitos*.

De qualquer forma, o programa, seja ele qual for, não precisa usar multi-thread para lidar com I/O - a não ser que realmente queira. Com apenas uma thread, é possível criar um "loop" que fica verificando no SO quais chamadas ficaram prontas.

Vamos, em pseudocódigo, escrever um loop com este propósito:

```
fun main():
    # Lista de descritores de arquivos
    
    input_fds  = [fd1, fd2, fd3]  # Descritores para monitorar leitura
    output_fds = [fd4]            # Descritores para monitorar escrita
    error_fds  = []               # Descritores para monitorar erros

    while true:
        # Chama a função select e espera até que algum descritor esteja pronto
        ready_read, ready_write, ready_error = 
	        select(input_fds, output_fds, error_fds)

        # Verifica os descritores prontos para leitura
        for fd in ready_read:
            data = read(fd)
            print("Dados lidos de fd:", fd, "->", data)

        # Verifica os descritores prontos para escrita
        for fd in ready_write:
            write(fd, "Mensagem de teste")
            print("Dados escritos em fd:", fd)

        # Verifica os descritores com erro
        for fd in ready_error:
            print("Erro detectado no descritor:", fd)

```

Enfim, a ideia aqui é ilustrar como seria um **loop de eventos** hipotético, que tanto ouvimos falar:

* iniciar loop
* passar para o select (ou epoll) a lista de descritores que queremos monitorar
* ler os descritores que estão prontos para leitura
* escrever nos descritores que estão prontos para escrita
* repetir o loop infinitamente

Repare que, ao termos um loop assíncrono de eventos, é extremamente importante toda e qualquer chamada ser **não-bloqueante**, caso contrário, se tivermos ao menos uma chamada bloqueante, o loop todo ficará bloqueado, e consequentemente a thread ficará bloqueada.

> Lembrem-se, com I/O assíncrono nunca podemos bloquear o loop. Toda chamada no I/O deve ser assíncrona.
