LibXev é um loop de eventos de plataforma cruzada. A LibXev fornece uma abstração unificada de loop de eventos para IO não bloqueador, temporizadores, sinais, eventos e muito mais que funcionam em macOS, Windows, Linux e WebAssembly (navegador e Wasi). Está escrito no ZIG, mas exporta uma API compatível com C (que o torna compatível com qualquer idioma que possa se comunicar com as APIs C).
Status do projeto :? Qualidade instável, alfa-ish. A lista de recursos é bastante boa em várias plataformas, mas há muitos recursos ausentes. O projeto não foi bem testado em ambientes do mundo real e há muitas frutas de baixa resgate para otimização de desempenho. Também não estou prometendo nenhuma compatibilidade da API neste momento. Se você deseja uma implementação de loop de eventos generalizada pronta, alta qualidade e de alta qualidade, confira Libuv, Libev, etc.
Por que uma nova biblioteca de loop de eventos? Algumas razões. Primeiro, acho que o Zig não possui um loop de eventos generalizados comparável ao Libuv nos recursos ("generalizados" sendo uma palavra -chave aqui). Segundo, eu queria construir uma biblioteca como essa em torno dos padrões de design de io_uring, até imitando seu estilo em cima de outras primitivas (crédito a esta incrível postagem do blog). Três, eu queria uma biblioteca de loop de eventos que pudesse ser construída para o WebAssembly (tanto Wasi quanto independentes) e que realmente não se encaixava bem nos objetivos do estilo API das bibliotecas existentes sem trazer algo super pesado como o Emscriptten. A motivação para essa biblioteca principalmente está coçando minha própria coceira!
Plataforma cruzada. Linux ( io_uring e epoll ), macOS ( kqueue ), WebAssembly + Wasi ( poll_oneoff , Thread e não tempos de execução não-thread). (O suporte ao Windows está planejado e em breve)
API do Proactor. O trabalho é submetido ao loop de eventos LibXev e o chamador é notificado sobre a conclusão do trabalho, em oposição à prontidão do trabalho.
Zero Alocações de tempo de execução. Isso ajuda a tornar o desempenho do tempo de execução mais previsível e torna a LibXev bem adequada para ambientes incorporados.
Timers, TCP, UDP, arquivos, processos. APIs agnósticas de plataforma de alto nível para interagir com temporizadores, soquetes TCP/UDP, arquivos, processos e muito mais. Para plataformas que não suportam o ASYNC IO, as operações de arquivo são agendadas automaticamente para um pool de threads.
Pool de threads genéricos (opcional). Você pode criar um pool de threads genéricos, configurar sua utilização de recursos e usá -lo para executar tarefas de fundo personalizadas. O pool de threads é usado por alguns back-end para realizar tarefas não bloqueadoras que não possuem APIs confiáveis que não bloqueiam (como operações de arquivos locais com kqueue ). O pool de threads pode ser compartilhado em vários threads e loops de eventos para otimizar a utilização de recursos.
API de baixo nível e alto nível. A API de alto nível é agnóstica da plataforma, mas tem algum comportamento opinativo e flexibilidade limitada. A API de alto nível é recomendada, mas a API de baixo nível é sempre uma escotilha de fuga disponível. A API de baixo nível é específica da plataforma e fornece um mecanismo para os usuários da LIBXEV espremer o desempenho máximo. A API de baixo nível é abstração suficiente acima da interface do SO para facilitar o uso sem sacrificar o desempenho notável.
Agitação de árvores (zig). Este é um recurso do ZIG, mas beneficia substancialmente bibliotecas como o LibXev. O ZIG incluirá apenas chamadas de função e recursos que você realmente usa. Se você não usar um tipo específico de observador de alto nível (como soquetes UDP), a funcionalidade relacionada a essa abstração não é compilada em seu binário final. Isso permite que a Funcionalidade Opcional de Suporte da LibXev, que possa ser considerada "inchaço" em alguns casos, mas o usuário final não precisa pagar por isso.
Livre de dependência. A LibXev não tem outras dependências além das APIs do sistema operacional interno em tempo de execução. A biblioteca C depende da libc. Isso facilita muito o composto cruzado.
Há muitos recursos ausentes que eu ainda quero adicionar:
E mais ...
Há muito espaço para melhorias no desempenho, e quero ficar totalmente claro que não fiz muito trabalho de otimização. Ainda assim, o desempenho parece bom. Eu tentei portar muitos dos benchmarks Libuv para usar a API LibXev.
Não vou postar resultados específicos de referência até que eu tenha um ambiente melhor para executá -los. Como uma generalização muito ampla , você não deve notar uma desaceleração usando o LIBXEV em comparação com outros loops de eventos importantes. Isso pode diferir em uma base característica a recurso e, se você puder mostrar um desempenho realmente ruim em um problema, estou interessado em resolvê-lo!
O exemplo abaixo mostra um programa idêntico escrito em ZIG e em C que usa o LIBXEV para executar um único temporizador 5S. Isso é quase bobo o quão simples é, mas é apenas transmitir a sensação geral da biblioteca, em vez de um caso de uso prático.
| Zig | C |
const xev = @import ( "xev" );
pub fn main () ! void {
var loop = try xev . Loop . init (.{});
defer loop . deinit ();
const w = try xev . Timer . init ();
defer w . deinit ();
// 5s timer
var c : xev.Completion = undefined ;
w . run ( & loop , & c , 5000 , void , null , & timerCallback );
try loop . run ( .until_done );
}
fn timerCallback (
userdata : ? * void ,
loop : * xev.Loop ,
c : * xev.Completion ,
result : xev . Timer . RunError ! void ,
) xev.CallbackAction {
_ = userdata ;
_ = loop ;
_ = c ;
_ = result catch unreachable ;
return .disarm ;
} | # include < stddef . h >
# include < stdio . h >
# include < xev . h >
xev_cb_action timerCallback ( xev_loop * loop , xev_completion * c , int result , void * userdata ) {
return XEV_DISARM ;
}
int main ( void ) {
xev_loop loop ;
if ( xev_loop_init ( & loop ) != 0 ) {
printf ( "xev_loop_init failure n " );
return 1 ;
}
xev_watcher w ;
if ( xev_timer_init ( & w ) != 0 ) {
printf ( "xev_timer_init failure n " );
return 1 ;
}
xev_completion c ;
xev_timer_run ( & w , & loop , & c , 5000 , NULL , & timerCallback );
xev_loop_run ( & loop , XEV_RUN_UNTIL_DONE );
xev_timer_deinit ( & w );
xev_loop_deinit ( & loop );
return 0 ;
} |
Essas instruções são apenas para usuários do ZIG a jusante. Se você estiver usando a API C para LibXev, consulte a seção "Build".
Este pacote funciona com o gerenciador de pacotes ZIG introduzido no ZIG 0.11. Crie um arquivo build.zig.zon como este:
.{
. name = "my-project" ,
. version = "0.0.0" ,
. dependencies = .{
. libxev = .{
. url = "https://github.com/mitchellh/libxev/archive/<git-ref-here>.tar.gz" ,
. hash = "12208070233b17de6be05e32af096a6760682b48598323234824def41789e993432c" ,
},
},
} E em seu build.zig :
const xev = b . dependency ( "libxev" , .{ . target = target , . optimize = optimize });
exe . addModule ( "xev" , xev . module ( "xev" ));? A documentação é um trabalho em andamento. ?
Atualmente, a documentação está disponível em três formas: páginas , exemplos e comentários de código. No futuro, planejo escrever guias detalhados e documentação da API em forma de site, mas isso não está disponível no momento.
As páginas do homem são relativamente detalhadas! xev(7) fornecerá uma boa visão geral de toda a biblioteca. xev-zig(7) e xev-c(7) fornecerão visões gerais da API ZIG e C, respectivamente. A partir daí, estão disponíveis páginas manuais da API-Specifc, como xev_loop_init(3) . Esta é a melhor documentação atualmente.
Existem várias maneiras de navegar nas páginas do homem. O mais imediatamente amigável é apenas navegar pelas fontes de página do Raw Man no docs/ Diretório no seu navegador da Web. A fonte da página do homem é uma sintaxe do tipo Markdown, para que ele esteja bem no seu navegador via Github.
Outra abordagem é executar zig build -Dman-pages e as páginas do homem estarão disponíveis no zig-out . Isso exige que o SCDOC seja instalado (está disponível na maioria dos gerentes de pacotes). Depois de construir as páginas do homem, você pode renderizá -las por caminho:
$ man zig-out/share/man/man7/xev.7
E a abordagem final é instalar a LibXev através do seu gerenciador de pacotes favorito (se e quando disponível), o que, esperançosamente, deve colocar as páginas do seu homem no caminho do seu homem, para que você possa fazer man 7 xev .
Existem exemplos disponíveis nos examples/ pasta. Os exemplos estão disponíveis em C e ZIG, e você pode dizer qual é qual é o uso da extensão do arquivo.
Para criar um exemplo, use o seguinte:
$ zig build -Dexample-name=_basic.zig
...
$ zig-out/bin/example-basic
...
O valor -Dexample-name deve ser o nome do arquivo, incluindo a extensão.
O código do zig é bem comentado. Se você se sentir confortável lendo comentários, pode encontrar muitas informações dentro deles. A fonte está no src/ diretório.
A construção requer a instalação do mais recente Zig Nightly. O LibXev não tem outras dependências de construção.
Depois de instalado, zig build install por conta própria construirá a biblioteca completa e produzirá um diretório compatível com FHS no zig-out . Você pode personalizar o diretório de saída com o sinalizador --prefix .
Libxev possui um conjunto de testes grande e crescente. Para executar os testes para a plataforma atual:
$ zig build test
...Isso executará todos os testes para todos os recursos suportados para a plataforma de host atual. Por exemplo, no Linux, isso executará o conjunto completo de testes io_uring e epoll.
Você pode criar e executar testes para outras plataformas, compilando o executável de teste, copiando-o para uma máquina de destino e executando-o. Por exemplo, o abaixo mostra como compilar cruzado e criar os testes para macOS a partir do Linux:
$ zig build -Dtarget=aarch64-macos -Dinstall-tests
...
$ file zig-out/bin/xev-test
zig-out/bin/xev-test: Mach-O 64-bit arm64 executableWasi é um caso especial. Você pode executar testes para o WASI se tiver o Wasmtime instalado:
$ zig build test -Dtarget=wasm32-wasi -Dwasmtime
...