Clause
← Toda a documentação

Traduzido do original em inglês · 06042fa · 2026-10-09 · Ler em inglês

Runtime

clause_runtime é uma biblioteca C++23 sem LLVM. Detém os contextos, os heaps dos processos, os átomos, os módulos carregados e o registo do ciclo de vida, e executa processos Erlang em workers do escalonador (processos). Todas as APIs são internas ao projeto; as chamadas do anfitrião têm de ser serializadas por runtime e não podem sobrepor-se a uma execução do programa, cujos workers se sincronizam entre si (threads).

Ligação

Ligar exatamente um runtime compilado para o alvo através de Clause::generated_program:

add_executable(harness harness.cpp)
target_link_libraries(harness PRIVATE Clause::generated_program)

Traz o arquivo, os cabeçalhos de ABI/runtime e o C++23, mas não o LLVM. link_consumer.cpp mostra um ciclo de vida completo.

Ciclo de vida

runtime.hpp:

Ordem de desmontagem: fechar os registos do escalonador → destruir os contextos → libertar os registos de código → destruir o serviço do escalonador → tabela de átomos por último. Os handles de funções resolvidas mantêm vivos o código e as grafias dos átomos após a desmontagem do runtime, mas nunca um processo.

Memória dos processos

Cada contexto detém um ProcessHeap: um único bloco de heap criado pela sua primeira alocação, com tamanho max(min_heap_words, request), mais uma cadeia de fragmentos de heap pertencentes ao mesmo processo. As palavras só se movem quando o anfitrião chama collect() num ponto seguro.

O heap segue o desenho clássico do ERTS; runtime-heap.md é o seu contrato (disposição, áreas, dimensionamento, admissão, raízes, recolha).

Servidor de código e builtins

Cada runtime detém um CodeServer (code_server.hpp, callable.hpp):

auto functions = std::make_unique<ModuleRegistry>();
auto added = functions->add("identity", 1,
    [](ProcessContext &, std::span<const Term> args) -> CallResult<Term> { return args.front(); });
auto loaded = context.code_server().load({"native_demo", CodeImage::linked(), std::move(functions)});
auto fn = context.code_server().resolve({.module = "native_demo", .function = "identity", .arity = 1});

Registo do escalonador

SchedulerService (scheduler.hpp) regista apenas o ciclo de vida dos processos; não executa código.

Os esboços de desenho dos workers e dos processos encontram-se em runtime/design/ e runtime/include/{scheduler,process}.hpp. As mensagens estão implementadas (processos): um envio copia a mensagem para o heap do recetor e acrescenta-a à sua caixa de entrada de sinais (runtime/include/mailbox.hpp).

Threads

Os workers do escalonador (step 56 do plano, workers) executam processos em várias threads de um runtime. Os serviços que partilham são sincronizados; tudo o resto fica confinado à thread que executa o seu processo ou protegido pelo mutex do executor.

Saída padrão

RuntimeOptions::standard_output (output.hpp) recebe os bytes de erlang:display/1 e, mais tarde, de standard_io. Por omissão, escreve no stdout do processo através do stdio de C (com buffer); um destino do anfitrião devolve false para indicar uma escrita falhada.

erlang:display/1 apresenta o seu argumento no estilo de display, escreve o texto e uma mudança de linha numa só escrita e devolve true. Os limites de apresentação e as escritas rejeitadas tornam-se estados de infraestrutura (resource_limit, output_failure, ...) no canal verificado, nunca exceções Erlang.

Arranque do programa

CLAUSE_main_v1 (startup.hpp, runtime/src/startup/) executa um programa inteiro para o main gerado: verifica a ABI de cada descritor, inicia um runtime por omissão, regista todos os módulos antes de qualquer código de entrada, constrói o argv no contexto de entrada, chama a entrada e converte o resultado no código de saída dos executáveis. Os relatórios vão para o stderr depois de o stdout ser esvaziado; o contexto e o runtime são desmontados por ordem em todos os caminhos. CLAUSE_halt_v1 implementa erlang:halt/0,1 (abort chama std::abort).

Serviços adiados

Estes emitem uma linha [feature] notimpl (funcionalidades) e não alteram nenhum estado:

FronteiraErro
TermFactory::port (ainda sem portas, step 53 do plano), reference(ReferenceIdentity) e function(FunctionIdentity)TermError::not_implemented
SchedulerService::run / executeSchedulerError::not_implemented
CodeServer::unloadCodeError::not_implemented
dispatch_builtin num BIF catalogadoStatus::not_implemented