Clause
← Toute la documentation

Traduit de l'original anglais · 06042fa · 2026-10-09 · Lire en anglais

Runtime

clause_runtime est une bibliothèque C++23 sans LLVM. Elle possède les contextes, les tas de processus, les atomes, les modules chargés et la gestion du cycle de vie, et exécute les processus Erlang sur des workers de l'ordonnanceur (processus). Toutes les API sont internes au projet ; les appels de l'hôte doivent être sérialisés par runtime et ne pas chevaucher l'exécution d'un programme, dont les workers se synchronisent entre eux (threads).

Édition de liens

Lier exactement un runtime compilé pour la cible via Clause::generated_program :

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

Cela apporte l'archive, les en-têtes ABI/runtime et C++23, mais pas LLVM. link_consumer.cpp montre un cycle de vie complet.

Cycle de vie

runtime.hpp :

Ordre de destruction : fermer les enregistrements de l'ordonnanceur → détruire les contextes → libérer les enregistrements de code → détruire le service d'ordonnancement → la table des atomes en dernier. Les handles de fonction résolus maintiennent le code et l'orthographe des atomes en vie après la destruction du runtime, mais jamais un processus.

Mémoire des processus

Chaque contexte possède un ProcessHeap : un unique bloc de tas créé par sa première allocation et dimensionné à max(min_heap_words, request), plus une chaîne de fragments de tas appartenant au même processus. Les mots ne se déplacent que lorsque l'hôte appelle collect() à un point sûr.

Le tas suit la conception classique d'ERTS ; runtime-heap.md en est le contrat (disposition, zones, dimensionnement, admission, racines, collecte).

Serveur de code et builtins

Chaque runtime possède un 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});

Gestion de l'ordonnanceur

SchedulerService (scheduler.hpp) enregistre uniquement le cycle de vie des processus ; il n'exécute aucun code.

Des esquisses de conception pour les workers et les processus se trouvent dans runtime/design/ et runtime/include/{scheduler,process}.hpp. Les messages sont implémentés (processus) : un envoi copie le message dans le tas du destinataire et l'ajoute à sa boîte de réception des signaux (runtime/include/mailbox.hpp).

Threads

Les workers de l'ordonnanceur (plan step 56, workers) exécutent les processus sur plusieurs threads d'un même runtime. Les services qu'ils partagent sont synchronisés ; tout le reste reste confiné au thread qui exécute son processus ou protégé par le mutex de l'exécuteur.

Sortie standard

RuntimeOptions::standard_output (output.hpp) reçoit erlang:display/1 et, plus tard, les octets de standard_io. Par défaut, l'écriture se fait sur le stdout du processus via stdio de C (avec tampon) ; un récepteur de l'hôte renvoie false pour signaler une écriture échouée.

erlang:display/1 rend son argument dans le style d'affichage, écrit le texte et un saut de ligne en une seule écriture et renvoie true. Les limites de rendu et les écritures refusées deviennent des statuts d'infrastructure (resource_limit, output_failure, ...) dans le canal vérifié, jamais des exceptions Erlang.

Démarrage du programme

CLAUSE_main_v1 (startup.hpp, runtime/src/startup/) exécute un programme entier pour le main généré : il vérifie l'ABI de chaque descripteur, démarre un runtime par défaut, enregistre tous les modules avant tout code d'entrée, construit argv dans le contexte d'entrée, appelle l'entrée et convertit le résultat en code de sortie des exécutables. Les rapports vont sur stderr après le vidage de stdout ; le contexte et le runtime sont détruits dans l'ordre sur chaque chemin. CLAUSE_halt_v1 implémente erlang:halt/0,1 (abort appelle std::abort).

Services différés

Ceux-ci rapportent une ligne [feature] notimpl (fonctionnalités) et ne changent aucun état :

FrontièreErreur
TermFactory::port (pas encore de ports, plan step 53), reference(ReferenceIdentity) et function(FunctionIdentity)TermError::not_implemented
SchedulerService::run / executeSchedulerError::not_implemented
CodeServer::unloadCodeError::not_implemented
dispatch_builtin sur un BIF cataloguéStatus::not_implemented