gestion_table_tournante_libre/src/application/generation_travailleur.js
Mathieu Benoit b969304c2a [ADD] generation: a pool of workers over the CPUs, cores setting
Generation ran in a single worker, one proposition after another, using
one core. A pool of workers now takes propositions by rank, as many as
the cores set: automatic by default (every logical CPU), or a count from
1 to that number. Each proposition is still computed from its own derived
seed, so the result is byte-identical whatever the core count, ordered by
identifier. One progress bar sums the workers; Cancel stops them all; the
duration estimate counts the cores. The setting is per session and never
written. Checked: 2872 node, 586 browser, 62 long; 5 workers in Electron.

--- FR ---

[ADD] génération : réserve de travailleurs sur les processeurs, cœurs

La génération tournait dans un seul travailleur, une proposition après
l'autre, sur un seul cœur. Une réserve de travailleurs prend maintenant les
propositions par rang, autant que de cœurs réglés : automatique par défaut
(tous les processeurs logiques), ou un nombre de 1 à ce total. Chaque
proposition se calcule toujours depuis sa graine dérivée : le résultat est
identique à l'octet quel que soit le nombre de cœurs, rangé par
identifiant. Une seule barre ; Annuler arrête tout ; l'estimation compte
les cœurs. Réglage de séance, jamais écrit. Vérifié : 2872, 586, 62.

Assisted-by: Claude Opus 5.5
2026-10-08 04:35:21 -04:00

73 lines
4 KiB
JavaScript

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// Le côté travailleur de la génération (§ 5.7, § 5.10) : la recherche du
// moteur hors du fil de l'interface, une proposition par message. Un
// travailleur reçoit le rang d'une proposition, la calcule par proposerUne,
// et poste ses avancements puis la proposition ou son erreur ; il attend
// ensuite le rang suivant, que l'exécuteur lui donne ou non :
//
// → { type: 'proposer', configuration, reglages, rang }
// ← { type: 'avancement', fait, total } au plus une fois toutes les
// CADENCE_AVANCEMENT progressions
// du moteur, fait croissant ; fait
// et total comptent dans la seule
// proposition, total valant arret
// ← { type: 'proposition', proposition } celle de rang rang de rechercher,
// telle quelle
// ← { type: 'erreur', code, details } REGLAGE_HORS_DOMAINE {reglage}
// d'exigerReglages, avant tout
// calcul ; puis, par
// versErreurGeneration, le code et
// les détails d'une
// ErreurConfiguration
// ← { type: 'faute', message, pile } une erreur que versErreurGeneration
// ne convertit pas — un rang hors
// de 1 à nombre compris : une faute
// de programmation, son texte et sa
// pile, que l'exécuteur relève
// comme une faute
//
// Le travailleur ne garde rien d'un message à l'autre : chaque proposition
// se calcule à partir du seul message qui la demande. L'annulation est
// terminate() du côté de l'interface : immédiate, sans résultat partiel ; le
// travailleur ne lit aucun signal. Un message d'un autre type ne lance rien.
// Le gestionnaire ne lève jamais : le canal d'erreur du travailleur ne porte
// ainsi que l'échec de son script — qui ne se charge ou ne se compile pas —,
// et l'exécuteur ne confond pas les deux.
import { proposerUne } from '../moteur/recherche.js';
import { ErreurGeneration, exigerReglages, versErreurGeneration } from './generation.js';
/** Progressions du moteur — une toutes les 1 000 itérations — entre deux messages d'avancement. */
export const CADENCE_AVANCEMENT = 50;
/**
* Branche une portée de travailleur — { onmessage, postMessage } — sur
* proposerUne, selon les messages de l'en-tête du module.
*
* @param {{onmessage: ((evenement: {data: unknown}) => void)|null, postMessage: (message: Object) => void}} portee
*/
export function brancherTravailleur(portee) {
portee.onmessage = ({ data }) => {
if (data === null || typeof data !== 'object' || data.type !== 'proposer') return;
let progressions = 0;
const progression = (fait, total) => {
progressions += 1;
if (progressions % CADENCE_AVANCEMENT === 0) portee.postMessage({ type: 'avancement', fait, total });
};
try {
exigerReglages(data.reglages);
const proposition = proposerUne(data.configuration, data.reglages, data.rang, { progression });
portee.postMessage({ type: 'proposition', proposition });
} catch (erreur) {
const convertie = versErreurGeneration(erreur);
if (convertie instanceof ErreurGeneration) {
portee.postMessage({ type: 'erreur', code: convertie.code, details: convertie.details });
} else {
const message = String(convertie);
const pile = typeof convertie?.stack === 'string' ? convertie.stack : message;
portee.postMessage({ type: 'faute', message, pile });
}
}
};
}