Show HN: Uma implementação pura em C89 de canais Go, com selects bloqueantes

Uma nova biblioteca open source que implementa canais no estilo Go e selects bloqueantes em C89 gerou debate sobre se mirar um padrão C tão antigo é uma vantagem de portabilidade ou uma restrição desnecessária que prejudica a qualidade da API. Comentadores entram em detalhes de design como convenções de nomenclatura, uso de `bool`, semântica de select, hooks de alocação e casos-limite de concorrência como bugs de time-of-check/time-of-use quando canais são fechados, enquanto comparam o projeto a alternativas como libmill, libdill e ZeroMQ. Além da crítica técnica, vários participantes refletem sobre tom e etiqueta em revisões públicas de código, destacando a tensão entre feedback rigoroso e a manutenção de um ambiente acolhedor para mantenedores voluntários.

Recepção geral

  • Muitos comentadores acham a ideia de canais no estilo Go em C elegante e útil, especialmente para ambientes restritos ou embarcados.
  • Vários expressam apreço pelo esforço do autor e pelo foco em portabilidade, mesmo que pessoalmente não usem C89.

C89 vs padrões C mais novos

  • Um lado argumenta que C89 está ultrapassado: C11/C17 (e em parte C99) estão amplamente disponíveis, e as restrições de apenas C89 pioram a clareza da API (por exemplo, a falta de bool) e a qualidade do código.
  • Outros defendem C89 como um alvo prático de portabilidade para compiladores antigos, toolchains embarcadas e projetos construídos com TinyCC ou outros ambientes limitados.
  • Alguns apontam que a biblioteca não é estritamente C89 de qualquer forma (declarações/instruções misturadas, threads POSIX, certas extensões), argumentando que o rótulo de “C89 puro” é enganoso; o autor depois esclarece que quer dizer “compila com -std=c89” em vez de pureza formal estrita.

Feedback sobre API e design

  • As sugestões incluem:
    • Usar um tipo parecido com bool para retornos de estado sempre que possível.
    • Preferir a nomenclatura _create/_destroy em vez de _dispose.
    • Evitar o sufixo _t porque o POSIX o reserva (outros dizem que isso é, na prática, irrelevante).
    • Permitir alocadores personalizados, hooks de logging e flags de debug.
    • Expor um descritor de arquivo para que os canais possam integrar-se com loops de eventos existentes (select/epoll/kqueue/io_uring).
  • Alguns destacam que essas são decisões de design no nível do header, difíceis de alterar depois sem quebrar a ABI.

Semântica de concorrência e armadilhas

  • Preocupação com while (!closed(chan)) { … }: o clássico bug de time-of-check/time-of-use, caso o canal seja fechado entre a verificação e a operação.
  • Comparações com Go: a operação de receive retorna atomically tanto o valor quanto o estado “aberto/fechado”; esse padrão é recomendado em vez de uma sondagem separada com closed().
  • Foram levantadas dúvidas sobre o que acontece com mensagens enfileiradas ao fechar e sobre o comportamento ao enviar para um canal fechado; os comentadores querem isso claramente documentado.

Comparações com outras bibliotecas e modelos

  • Projetos relacionados mencionados: libmill/libdill (CSP com corrotinas e multiplexação de I/O), sockets inproc do ZeroMQ, libthread do Plan 9, CSP e atores.
  • Esclarecimento de que esta biblioteca usa threads do SO, não corrotinas de nível de usuário, portanto não pode ser misturada com segurança com schedulers de corrotinas que esperam primitivas não bloqueantes.

Comunidade e tom

  • Há uma discussão meta considerável sobre o tom das revisões: alguns acham que as críticas iniciais foram excessivamente duras; outros enfatizam que o feedback técnico em si é valioso se for entregue com mais cuidado.