- É levantada a crítica de que o livro The Art of Multiprocessor Programming não aborda o conceito de futex, o que é lamentável
- O futex é um componente central da sincronização eficiente na programação paralela moderna, com desempenho superior aos locks tradicionais baseados em System V
- O futex tem uma estrutura que separa a aquisição do lock das funções de espera/despertar, reduzindo chamadas de sistema desnecessárias e overhead
- Estão incluídos exemplos e técnicas para implementar diretamente vários primitivos de concorrência com base em futex, como spinlock, mutex e lock recursivo
- O autor aponta um distanciamento entre a academia e o mercado ao criticar que o livro não trata de metodologias modernas de sincronização essenciais para a prática de engenharia
Introdução
- Phil Eaton iniciou um clube de leitura de The Art of Multiprocessor Programming, 2nd Edition
- Embora esse livro seja considerado uma referência na área de programação paralela, o autor aponta a falta de utilidade prática do conteúdo
- Em especial, critica o fato de que, apesar de dizer que se destina a alunos do último ano da graduação e da pós-graduação, ele não trata do futex, uma técnica central de sincronização
O que é futex — e por que isso importa
- Futex é a abreviação de “fast user space mutex”, mas na prática é menos um mutex e mais um primitivo de sincronização com suporte do SO para implementações modernas de lock
- No passado, a maioria dos locks era implementada com base em semáforos do System V IPC, o que trazia limitações de eficiência e escalabilidade
- Com a introdução do futex no Linux em 2002, ele apresentou desempenho de 20 a 120 vezes superior aos locks do System V em ambientes com 1000 tarefas simultâneas
- Outros sistemas operacionais, como Windows (2012) e macOS (2016), também adotaram mecanismos semelhantes
- Hoje, os locks de bibliotecas de sistema amplamente usadas, como pthreads, utilizam futex
Como o futex funciona e o que o diferencia
- Os semáforos tradicionais combinavam lock e espera, mas o futex separa a aquisição do lock do esperar/despertar
- Isso reduz delays e chamadas de sistema desnecessárias, e ao liberar o lock, se for certo que não há threads esperando, nem é preciso entrar no kernel
- A chamada de espera (
wait) do futex faz com que a thread espere “somente quando o valor em um endereço de memória específico estiver no estado desejado”, além de oferecer suporte a timeout
- A chamada de despertar (
wake) do futex acorda a quantidade desejada de threads a partir de uma lista interna de espera associada a um endereço de memória específico
- Como exige a verificação do valor real no endereço de memória, evita esperas desnecessárias quando o estado já mudou
Uso prático do futex — implementação direta
- Como o futex é um primitivo de baixo nível, usam-se tipos
atomic levando em conta questões de ordem das operações de memória do compilador e do hardware
- No Linux, é preciso chamar diretamente a system call de futex via
syscall; no macOS, usa-se a interface __ulock (mais recentemente, foi adicionada uma API mais simples)
- Em termos básicos, uma espera de futex retorna 0 em caso de sucesso e um código de erro em caso de falha (como timeout)
- Operações centrais baseadas em futex:
h4x0r_futex_wait_timespec() : espera se o valor esperado coincidir, com possibilidade de aplicar timeout
h4x0r_futex_wake() : acorda 1 ou todos os waiters
Exemplos práticos de implementação de mutex/spinlock/lock recursivo
Spinlock
- A forma mais simples de lock, funcionando apenas com um único bit (
atomic_fetch_or)
- Fica em loop infinito (“spin”) até obter o lock, mas em cenários de alta contenção desperdiça CPU e tem problemas estruturais, como unlock incorreto e risco de deadlock em chamadas recursivas
Mutex híbrido (“unsafe” mutex)
- Em geral, tenta primeiro com spinlock e, após certo número de falhas, muda para futex para fazer bloqueio eficiente
- Se não houver waiters, evita chamadas de sistema desnecessárias, e para quem está esperando também é possível minimizar as chamadas de sistema de wake
- Como não tem verificação rigorosa de ownership nem tratamento de recursão, recebe o nome de “unsafe”
Mutex com contador de waiters
- Um bit representa o estado do lock, e o restante é usado para contabilizar o número de waiters, com o objetivo de reduzir chamadas de sistema de wake desnecessárias
- Ainda não há tratamento de ownership nem de recursão
Mutex com gerenciamento de ownership
- Com o valor de
pthread_t, é possível rastrear claramente o dono do lock e seu estado, detectando problemas em unlocks incorretos ou uso recursivo
- Aquisição, liberação e gerenciamento de waiters do lock são todos controlados com operações atômicas rigorosas
Lock recursivo
- Adiciona um contador de profundidade (
depth) por thread, permitindo que a mesma thread adquira o lock repetidamente
- No
unlock, o depth é reduzido e, quando chega a 0, ocorre o unlock real e o despertar das threads
- Cada operação é implementada com operações atômicas e verificação rigorosa de ownership
Desafios restantes e a realidade da engenharia
- Quando a thread dona do lock termina de forma anormal ou morre, o gerenciamento do lock exige gestão adicional, como uma lista de controle separada e callbacks de encerramento
- Mesmo ao usar mutexes compartilhados entre processos, é preciso considerar de forma adicional como lidar com mudanças de estado
- O RW lock do POSIX não define o comportamento de aninhamento recursivo, e isso varia entre implementações, o que dificulta garantir segurança na prática
- O autor critica o fato de que o livro não inclui no currículo temas de concorrência realmente importantes na prática, como futex, locks recursivos e runtimes assíncronos
Conclusão
- The Art of Multiprocessor Programming está inclinado demais à perspectiva histórica ou teórica e não traz adequadamente conhecimentos práticos modernos importantes de programação paralela
- Se não tratar corretamente de componentes centrais de sincronização como o futex, que de fato operam nos sistemas reais, isso pode causar prejuízo prático aos futuros profissionais
- O autor enfatiza a necessidade de refletir conceitos mais atuais e complementar o conteúdo com material mais prático
Referências
- O exemplo completo de código pode ser visto no codeberg
Ainda não há comentários.