Files
pss/src/core/engine.ts
T

949 lines
34 KiB
TypeScript

import type {
Process,
ProcessRuntime,
SchedulerConfig,
SimTick,
StateLabel,
Thread,
ThreadRuntime,
} from "./types";
import { algorithms } from "./algorithms/index";
import type { BaseAlgorithm } from "./algorithms/index";
import { ThreadScheduler } from "./thread-scheduler";
/**
* SimulationEngine — Núcleo de execução da simulação de escalonamento.
*
* Responsável por avançar o simulador tick a tick, aplicando o algoritmo
* de escalonamento configurado e mantendo o estado de todos os processos.
*
* Suporta dois modos de operação:
* - Processos simples: comportamento original, sem alterações.
* - Processos com threads: escalonamento em dois níveis:
* 1. Nível de processo — algoritmo global do SchedulerConfig.
* 2. Nível de thread — ThreadScheduler com política intra-processo.
*
* Modelos de threads suportados:
* MANY_TO_ONE — Uma kernel thread por processo. I/O bloqueia todo o processo.
* ONE_TO_ONE — Uma kernel thread por thread de usuário. Threads bloqueiam
* independentemente; o processo só bloqueia se todas as threads
* estiverem em I/O.
* MANY_TO_MANY — M kernel threads para N threads de usuário (M ≤ N).
* Slots são liberados durante I/O e reatribuídos dinamicamente.
*
* Ciclo de vida de um único tick (método `step()`):
* 1. Chegada — New → Ready para processos/threads que chegam neste tick.
* 2. Preempção — Algoritmo decide se processo em CPU deve ceder lugar.
* 3. Despacho — Seleciona e despacha próximo processo (e thread).
* 4. Snapshot — Captura estado completo (processos + threads).
* 5. Passagem do tempo — Decrementa bursts, acumula tempos.
* 6. Aging — Envelhecimento de prioridade anti-starvation.
* 7. Conclusões — Trata término de bursts de CPU/IO (processo e threads).
*/
export class SimulationEngine {
// ─── Campos privados — processos ──────────────────────────────────────────
private readonly _processes: Process[];
private readonly _config: SchedulerConfig;
private readonly _algorithm: BaseAlgorithm;
private _tick: number;
private _runtimes: Map<string, ProcessRuntime>;
private _cpuProcess: ProcessRuntime | null;
private _readyQueue: ProcessRuntime[];
private _ioQueue: ProcessRuntime[];
private _contextSwitchRemaining: number;
private _prevCpuProcessId: string | null;
// ─── Campos privados — threads ────────────────────────────────────────────
/** threadRuntimes[processId][threadId] = ThreadRuntime */
private _threadRuntimes: Map<string, Map<string, ThreadRuntime>>;
/** Escalonador intra-processo por processo */
private _threadSchedulers: Map<string, ThreadScheduler>;
/** Thread atualmente ativa na CPU para cada processo com threads */
private _activeThreads: Map<string, ThreadRuntime | null>;
/** Threads em I/O por processo */
private _threadIoQueues: Map<string, ThreadRuntime[]>;
/** Fila de threads prontas intra-processo */
private _readyThreadQueues: Map<string, ThreadRuntime[]>;
/** MANY_TO_MANY: slots de kernel disponíveis por processo */
private _kernelSlotsAvailable: Map<string, number>;
/** MANY_TO_MANY: threads aguardando slot de kernel */
private _kernelWaitQueues: Map<string, ThreadRuntime[]>;
// ─── Construtor ───────────────────────────────────────────────────────────
constructor(processes: Process[], config: SchedulerConfig) {
if (processes.length === 0) throw new Error("At least one process required");
this._processes = processes;
this._config = config;
this._algorithm = algorithms[config.algorithm];
this._tick = -1;
this._cpuProcess = null;
this._readyQueue = [];
this._ioQueue = [];
this._contextSwitchRemaining = 0;
this._prevCpuProcessId = null;
// Inicializa runtimes de processo
this._runtimes = new Map(
processes.map((p) => [p.id, SimulationEngine._initProcessRuntime(p, config)]),
);
// Inicializa estruturas de threads
this._threadRuntimes = new Map();
this._threadSchedulers = new Map();
this._activeThreads = new Map();
this._threadIoQueues = new Map();
this._readyThreadQueues = new Map();
this._kernelSlotsAvailable = new Map();
this._kernelWaitQueues = new Map();
for (const p of processes) {
if (this._hasThreads(p.id)) {
this._initThreadStructures(p);
}
}
}
// ─── API pública ──────────────────────────────────────────────────────────
step(): SimTick {
if (this.isFinished) throw new Error("Simulation is already finished");
this._tick++;
this._handleArrivals();
this._checkPreemption();
this._dispatchNext();
const snapshot = this._snapshot();
this._advanceTime();
this._applyAging();
this._handleCpuCompletion();
this._handleIoCompletions();
return snapshot;
}
runAll(): SimTick[] {
const results: SimTick[] = [];
while (!this.isFinished) {
results.push(this.step());
if (results.length > 10_000) {
throw new Error("Simulation exceeded 10,000 ticks — check for infinite loop");
}
}
return results;
}
get isFinished(): boolean {
for (const rt of this._runtimes.values()) {
if (rt.state !== "Terminated") return false;
}
return true;
}
get currentTick(): number {
return this._tick;
}
get runtimeStates(): ProcessRuntime[] {
return Array.from(this._runtimes.values());
}
/** Retorna todos os runtimes de threads de todos os processos. */
get threadRuntimeStates(): ThreadRuntime[] {
const result: ThreadRuntime[] = [];
for (const threadMap of this._threadRuntimes.values()) {
for (const trt of threadMap.values()) {
result.push(trt);
}
}
return result;
}
/** Informa se um processo possui threads definidas. */
processHasThreads(processId: string): boolean {
return this._hasThreads(processId);
}
// ─── Fases do ciclo de simulação ──────────────────────────────────────────
/**
* Fase 1 — Chegada de processos (e inicialização de threads).
*/
private _handleArrivals(): void {
for (const p of this._processes) {
const rt = this._runtimes.get(p.id)!;
if (p.arrivalTime === this._tick && rt.state === "New") {
this._transition(rt, "Ready");
this._readyQueue.push(rt);
if (this._hasThreads(p.id)) {
// Transiciona todas as threads New → Ready na fila intra-processo
const threadMap = this._threadRuntimes.get(p.id)!;
const readyQueue = this._readyThreadQueues.get(p.id)!;
const model = p.threadModel ?? "ONE_TO_ONE";
for (const trt of threadMap.values()) {
if (trt.state === "New") {
if (model === "MANY_TO_MANY") {
const slotsAvail = this._kernelSlotsAvailable.get(p.id) ?? 0;
if (slotsAvail > 0) {
this._kernelSlotsAvailable.set(p.id, slotsAvail - 1);
this._transitionThread(trt, "Ready");
readyQueue.push(trt);
} else {
// Sem slot disponível → espera kernel thread
trt.kernelWaiting = true;
this._transitionThread(trt, "Ready");
this._kernelWaitQueues.get(p.id)!.push(trt);
}
} else {
this._transitionThread(trt, "Ready");
readyQueue.push(trt);
}
}
}
}
}
}
}
/**
* Fase 2 — Verificação de preempção no nível de processo.
*/
private _checkPreemption(): void {
if (this._cpuProcess === null || this._readyQueue.length === 0) return;
const candidate = this._algorithm.select(this._readyQueue, this._config);
if (
candidate !== null &&
this._algorithm.shouldPreempt(this._cpuProcess, candidate, this._config)
) {
const cpu = this._cpuProcess;
// Retorna thread ativa à fila intra-processo (se processo com threads)
if (this._hasThreads(cpu.processId)) {
const activeThread = this._activeThreads.get(cpu.processId) ?? null;
if (activeThread !== null) {
this._transitionThread(activeThread, "Ready");
this._readyThreadQueues.get(cpu.processId)!.unshift(activeThread);
this._activeThreads.set(cpu.processId, null);
}
}
this._transition(cpu, "Ready");
this._readyQueue.push(cpu);
this._cpuProcess = null;
}
}
/**
* Fase 3 — Despacho de processo (e thread).
*/
private _dispatchNext(): void {
if (this._contextSwitchRemaining > 0) return;
if (this._cpuProcess !== null || this._readyQueue.length === 0) return;
const next = this._algorithm.select(this._readyQueue, this._config);
if (next === null) return;
const idx = this._readyQueue.indexOf(next);
if (idx !== -1) this._readyQueue.splice(idx, 1);
if (
this._config.contextSwitchTime > 0 &&
this._prevCpuProcessId !== null &&
this._prevCpuProcessId !== next.processId
) {
this._readyQueue.unshift(next);
this._contextSwitchRemaining = this._config.contextSwitchTime;
this._prevCpuProcessId = next.processId;
} else {
if (next.startTick === null) next.startTick = this._tick;
next.quantumRemaining = this._algorithm.getQuantumForProcess(next, this._config);
this._transition(next, "Running");
this._prevCpuProcessId = next.processId;
this._cpuProcess = next;
// Despacha thread intra-processo (se aplicável)
if (this._hasThreads(next.processId)) {
this._dispatchThreadForProcess(next);
}
}
}
/**
* Fase 4 — Snapshot do estado.
*/
private _snapshot(): SimTick {
const states: Record<string, StateLabel> = {};
for (const [id, rt] of this._runtimes) {
states[id] = rt.state;
}
const contextSwitching = this._contextSwitchRemaining > 0;
const snap: SimTick = {
tick: this._tick,
cpuProcess: contextSwitching ? null : (this._cpuProcess?.processId ?? null),
ioProcesses: this._ioQueue.map((rt) => rt.processId),
readyQueue: this._readyQueue.map((rt) => rt.processId),
states,
contextSwitching,
ctxSwitchForProcess: contextSwitching
? (this._readyQueue[0]?.processId ?? null)
: null,
};
// Inclui estado de threads se houver processos com threads
if (this._threadRuntimes.size > 0) {
const threadStates: Record<string, Record<string, StateLabel>> = {};
const cpuThreads: Record<string, string | null> = {};
const kernelWaitingThreads: Record<string, string[]> = {};
for (const [processId, threadMap] of this._threadRuntimes) {
threadStates[processId] = {};
for (const [threadId, trt] of threadMap) {
threadStates[processId][threadId] = trt.state;
}
const active = this._activeThreads.get(processId) ?? null;
cpuThreads[processId] = active?.threadId ?? null;
const kwQueue = this._kernelWaitQueues.get(processId) ?? [];
if (kwQueue.length > 0) {
kernelWaitingThreads[processId] = kwQueue.map((t) => t.threadId);
}
}
snap.threadStates = threadStates;
snap.cpuThreads = cpuThreads;
if (Object.keys(kernelWaitingThreads).length > 0) {
snap.kernelWaitingThreads = kernelWaitingThreads;
}
}
return snap;
}
/**
* Fase 5 — Passagem do tempo.
*/
private _advanceTime(): void {
if (this._contextSwitchRemaining > 0) {
this._contextSwitchRemaining--;
}
if (this._cpuProcess !== null) {
const cpu = this._cpuProcess;
if (this._hasThreads(cpu.processId)) {
// CPU time acumulado no nível de processo
cpu.cpuTime++;
cpu.quantumRemaining--;
// Decrementa burst e tempo da thread ativa
const activeThread = this._activeThreads.get(cpu.processId) ?? null;
if (activeThread !== null) {
activeThread.remainingBurst--;
activeThread.cpuTime++;
activeThread.quantumRemaining--;
// Sincroniza processo com thread ativa
cpu.remainingBurst = activeThread.remainingBurst;
}
} else {
cpu.remainingBurst--;
cpu.cpuTime++;
cpu.quantumRemaining--;
}
}
// I/O de processos sem threads
for (const rt of this._ioQueue) {
if (!this._hasThreads(rt.processId)) {
rt.remainingBurst--;
rt.ioTime++;
}
// Processos com threads (MANY_TO_ONE): tempo de I/O acumulado abaixo
}
// I/O de threads (todos os modelos)
for (const [, threadIoQueue] of this._threadIoQueues) {
for (const trt of threadIoQueue) {
trt.remainingBurst--;
trt.ioTime++;
}
}
// Tempo de espera — fila de prontos de processos
for (const rt of this._readyQueue) {
rt.waitingTime++;
}
// Tempo de espera — fila de prontas de threads intra-processo
for (const [, threadReadyQueue] of this._readyThreadQueues) {
for (const trt of threadReadyQueue) {
trt.waitingTime++;
}
}
// Tempo de espera — threads em kernel wait (MANY_TO_MANY)
for (const [, kwQueue] of this._kernelWaitQueues) {
for (const trt of kwQueue) {
trt.waitingTime++;
}
}
}
/**
* Fase 6 — Aging de prioridade.
*/
private _applyAging(): void {
if (
!this._config.agingEnabled ||
this._tick === 0 ||
this._tick % this._config.agingInterval !== 0
) {
return;
}
for (const rt of this._readyQueue) {
rt.currentPriority = Math.max(1, rt.currentPriority - 1);
}
}
/**
* Fase 7a — Conclusão de burst de CPU.
*/
private _handleCpuCompletion(): void {
if (this._cpuProcess === null) return;
const cpu = this._cpuProcess;
if (this._hasThreads(cpu.processId)) {
this._handleThreadedCpuCompletion();
return;
}
// ── Processo sem threads (comportamento original) ──────────────────────
if (cpu.remainingBurst === 0) {
this._advanceBurst(cpu);
cpu.quantumRemaining = 1;
const process = this._processes.find((p) => p.id === cpu.processId)!;
const nextBurst = process.bursts[cpu.burstIndex];
if (nextBurst !== undefined && nextBurst.type === "io") {
this._transition(cpu, "Waiting");
this._ioQueue.push(cpu);
} else if (nextBurst !== undefined && nextBurst.type === "cpu") {
this._transition(cpu, "Ready");
this._readyQueue.push(cpu);
} else {
this._transition(cpu, "Terminated");
cpu.finishTick = this._tick + 1;
}
this._cpuProcess = null;
} else if (this._algorithm.isQuantumExpired(cpu, this._config)) {
this._transition(cpu, "Ready");
this._readyQueue.push(cpu);
this._cpuProcess = null;
}
}
/**
* Fase 7b — Conclusão de bursts de I/O.
*/
private _handleIoCompletions(): void {
// ── Processos sem threads ──────────────────────────────────────────────
const nextIoQueue: ProcessRuntime[] = [];
for (const rt of this._ioQueue) {
if (this._hasThreads(rt.processId)) {
// Processos com threads gerenciados em _handleThreadIoCompletions
nextIoQueue.push(rt);
continue;
}
if (rt.remainingBurst === 0) {
this._advanceBurst(rt);
const process = this._processes.find((p) => p.id === rt.processId)!;
if (rt.burstIndex < process.bursts.length) {
this._transition(rt, "Ready");
this._readyQueue.push(rt);
} else {
this._transition(rt, "Terminated");
rt.finishTick = this._tick + 1;
}
} else {
nextIoQueue.push(rt);
}
}
this._ioQueue = nextIoQueue;
// ── Threads em I/O ────────────────────────────────────────────────────
this._handleThreadIoCompletions();
}
// ─── Lógica de threads ────────────────────────────────────────────────────
/**
* Despacha a próxima thread intra-processo quando o processo recebe CPU.
*/
private _dispatchThreadForProcess(processRt: ProcessRuntime): void {
const processId = processRt.processId;
const readyThreadQueue = this._readyThreadQueues.get(processId)!;
const scheduler = this._threadSchedulers.get(processId)!;
const nextThread = scheduler.select(readyThreadQueue);
if (nextThread === null) {
// Nenhuma thread pronta — mantém processo em Running mas sem thread ativa
// (Pode ocorrer se todas as threads estão em I/O; ONE_TO_ONE pode despachar
// o processo mesmo assim para que ele "exista" na CPU enquanto threads esperam)
this._activeThreads.set(processId, null);
return;
}
const idx = readyThreadQueue.indexOf(nextThread);
if (idx !== -1) readyThreadQueue.splice(idx, 1);
if (nextThread.startTick === null) {
nextThread.startTick = this._tick;
nextThread.responseTime = this._tick - nextThread.arrivalTick;
}
nextThread.quantumRemaining = scheduler.quantum;
nextThread.kernelWaiting = false;
this._transitionThread(nextThread, "Running");
this._activeThreads.set(processId, nextThread);
// Sincroniza process runtime com thread ativa
processRt.remainingBurst = nextThread.remainingBurst;
if (processRt.startTick === null) {
processRt.startTick = this._tick;
processRt.responseTime = this._tick - processRt.arrivalTick;
}
}
/**
* Gerencia conclusão de burst de CPU para processos com threads.
*/
private _handleThreadedCpuCompletion(): void {
const cpu = this._cpuProcess!;
const processId = cpu.processId;
const process = this._processes.find((p) => p.id === processId)!;
const policy = process.threadSchedulingPolicy ?? "FCFS";
const scheduler = this._threadSchedulers.get(processId)!;
const activeThread = this._activeThreads.get(processId) ?? null;
if (activeThread === null) {
// Sem thread ativa — verifica se processo deve continuar ou aguardar
if (this._readyThreadQueues.get(processId)!.length > 0) {
this._dispatchThreadForProcess(cpu);
} else {
// Todas as threads em I/O: processo vai para Waiting
this._transition(cpu, "Waiting");
if (!this._ioQueue.includes(cpu)) this._ioQueue.push(cpu);
this._cpuProcess = null;
}
return;
}
if (activeThread.remainingBurst === 0) {
// ── CPU burst da thread concluído ────────────────────────────────────
this._activeThreads.set(processId, null);
this._handleThreadBurstComplete(cpu, activeThread, process);
// Se o quantum do processo também expirou neste mesmo tick, cede a CPU
// agora (após tratar o burst) em vez de dar um tick extra ao processo.
if (this._cpuProcess !== null && this._algorithm.isQuantumExpired(cpu, this._config)) {
const activeAfter = this._activeThreads.get(processId) ?? null;
if (activeAfter !== null) {
this._transitionThread(activeAfter, "Ready");
if (policy === "RR") {
this._readyThreadQueues.get(processId)!.push(activeAfter);
} else {
this._readyThreadQueues.get(processId)!.unshift(activeAfter);
}
this._activeThreads.set(processId, null);
}
this._transition(cpu, "Ready");
this._readyQueue.push(cpu);
this._cpuProcess = null;
}
} else if (scheduler.isQuantumExpired(activeThread)) {
// ── Quantum intra-processo expirou (RR): rotaciona thread ────────────
this._transitionThread(activeThread, "Ready");
this._readyThreadQueues.get(processId)!.push(activeThread); // reinserido no fim (FIFO)
this._activeThreads.set(processId, null);
// Seleciona próxima thread — processo permanece na CPU
this._dispatchThreadForProcess(cpu);
// Se não há próxima thread pronta, processo aguarda
if (this._activeThreads.get(processId) === null) {
this._transition(cpu, "Waiting");
if (!this._ioQueue.includes(cpu)) this._ioQueue.push(cpu);
this._cpuProcess = null;
}
} else if (this._algorithm.isQuantumExpired(cpu, this._config)) {
// ── Quantum de processo expirou: devolve processo à fila global ───────
// Thread volta à fila intra-processo
this._transitionThread(activeThread, "Ready");
if (policy === "RR") {
this._readyThreadQueues.get(processId)!.push(activeThread);
} else {
this._readyThreadQueues.get(processId)!.unshift(activeThread);
}
this._activeThreads.set(processId, null);
this._transition(cpu, "Ready");
this._readyQueue.push(cpu);
this._cpuProcess = null;
}
}
/**
* Trata conclusão do burst de CPU de uma thread específica.
* Decide o próximo estado da thread e do processo conforme o modelo.
*/
private _handleThreadBurstComplete(
processRt: ProcessRuntime,
thread: ThreadRuntime,
process: Process,
): void {
const processId = process.id;
const model = process.threadModel ?? "ONE_TO_ONE";
const threadDef = process.threads!.find((t) => t.tid === thread.threadId)!;
// Avança ao próximo burst da thread
thread.burstIndex++;
const nextBurst = threadDef.bursts[thread.burstIndex];
thread.quantumRemaining = 1; // sentinela anti-falsa-demoção
if (nextBurst !== undefined && nextBurst.type === "io") {
// ── Thread vai para I/O ───────────────────────────────────────────────
thread.remainingBurst = nextBurst.duration;
this._transitionThread(thread, "Waiting");
this._threadIoQueues.get(processId)!.push(thread);
// Para MANY_TO_MANY: libera slot de kernel
if (model === "MANY_TO_MANY") {
this._releaseKernelSlot(processId);
}
if (model === "MANY_TO_ONE") {
// Kernel thread bloqueado → bloqueia todo o processo
this._transition(processRt, "Waiting");
this._ioQueue.push(processRt);
this._cpuProcess = null;
} else {
// ONE_TO_ONE / MANY_TO_MANY: verifica se há outra thread pronta
this._trySelectNextThreadOrBlock(processRt, process);
}
} else if (nextBurst !== undefined && nextBurst.type === "cpu") {
// ── Thread tem próximo burst de CPU ───────────────────────────────────
thread.remainingBurst = nextBurst.duration;
this._transitionThread(thread, "Ready");
this._readyThreadQueues.get(processId)!.push(thread);
// Processo permanece na CPU; seleciona próxima thread
this._dispatchThreadForProcess(processRt);
} else {
// ── Thread terminada ──────────────────────────────────────────────────
this._transitionThread(thread, "Terminated");
thread.finishTick = this._tick + 1;
// Para MANY_TO_MANY: libera slot de kernel
if (model === "MANY_TO_MANY") {
this._releaseKernelSlot(processId);
}
// Verifica se processo deve terminar ou continuar com outra thread
if (this._allThreadsTerminated(processId)) {
this._transition(processRt, "Terminated");
processRt.finishTick = this._tick + 1;
this._cpuProcess = null;
} else if (model === "MANY_TO_ONE") {
// Seleciona próxima thread pronta (se houver)
this._trySelectNextThreadOrBlock(processRt, process);
} else {
this._trySelectNextThreadOrBlock(processRt, process);
}
}
}
/**
* Tenta selecionar a próxima thread pronta para o processo.
* Se não houver threads prontas e há threads em I/O: processo aguarda.
* Se não houver threads prontas e todas terminadas: processo termina.
*/
private _trySelectNextThreadOrBlock(
processRt: ProcessRuntime,
process: Process,
): void {
const processId = process.id;
const readyThreadQueue = this._readyThreadQueues.get(processId)!;
if (readyThreadQueue.length > 0) {
// Há threads prontas: seleciona a próxima
this._dispatchThreadForProcess(processRt);
} else if (this._allThreadsTerminated(processId)) {
this._transition(processRt, "Terminated");
processRt.finishTick = this._tick + 1;
this._cpuProcess = null;
} else {
// Todas as threads em I/O: processo aguarda
this._transition(processRt, "Waiting");
if (!this._ioQueue.includes(processRt)) {
this._ioQueue.push(processRt);
}
this._cpuProcess = null;
}
}
/**
* Gerencia conclusão de bursts de I/O para threads.
*/
private _handleThreadIoCompletions(): void {
for (const [processId, threadIoQueue] of this._threadIoQueues) {
const processRt = this._runtimes.get(processId)!;
const process = this._processes.find((p) => p.id === processId)!;
const model = process.threadModel ?? "ONE_TO_ONE";
const remaining: ThreadRuntime[] = [];
for (const trt of threadIoQueue) {
if (trt.remainingBurst === 0) {
// ── I/O da thread concluído ────────────────────────────────────────
const threadDef = process.threads!.find((t) => t.tid === trt.threadId)!;
trt.burstIndex++;
const nextBurst = threadDef.bursts[trt.burstIndex];
trt.quantumRemaining = 1; // sentinela
if (nextBurst !== undefined && nextBurst.type === "cpu") {
trt.remainingBurst = nextBurst.duration;
if (model === "MANY_TO_MANY") {
// Tenta adquirir slot de kernel
const slotsAvail = this._kernelSlotsAvailable.get(processId) ?? 0;
if (slotsAvail > 0) {
this._kernelSlotsAvailable.set(processId, slotsAvail - 1);
trt.kernelWaiting = false;
this._transitionThread(trt, "Ready");
this._readyThreadQueues.get(processId)!.push(trt);
} else {
// Sem slot: vai para kernel wait queue
trt.kernelWaiting = true;
this._kernelWaitQueues.get(processId)!.push(trt);
}
} else {
this._transitionThread(trt, "Ready");
this._readyThreadQueues.get(processId)!.push(trt);
}
} else {
// Thread termina após I/O (ou sem próximo burst)
this._transitionThread(trt, "Terminated");
trt.finishTick = this._tick + 1;
if (model === "MANY_TO_MANY") {
// Não havia slot em uso durante I/O; nada a liberar
}
}
// Decide estado do processo com base no modelo
this._updateProcessStateAfterThreadIo(processRt, process, model);
} else {
remaining.push(trt);
}
}
this._threadIoQueues.set(processId, remaining);
}
}
/**
* Atualiza o estado do processo após conclusão de I/O de uma thread.
*/
private _updateProcessStateAfterThreadIo(
processRt: ProcessRuntime,
process: Process,
model: string,
): void {
const processId = process.id;
if (model === "MANY_TO_ONE") {
// O processo estava bloqueado (Waiting) devido ao I/O da thread
// Agora a thread está pronta → processo volta para Ready
if (processRt.state === "Waiting") {
this._ioQueue = this._ioQueue.filter((r) => r.processId !== processId);
this._transition(processRt, "Ready");
this._readyQueue.push(processRt);
this._activeThreads.set(processId, null); // será selecionada no dispatch
}
} else {
// ONE_TO_ONE / MANY_TO_MANY
if (processRt.state === "Waiting") {
// Processo estava aguardando porque todas as threads estavam em I/O
// Verifica se alguma thread ficou pronta
const readyThreadQueue = this._readyThreadQueues.get(processId)!;
if (readyThreadQueue.length > 0) {
this._ioQueue = this._ioQueue.filter((r) => r.processId !== processId);
this._transition(processRt, "Ready");
this._readyQueue.push(processRt);
}
}
// Se processo está Running: a thread retornou; será selecionada na próxima rodada
// Se processo está Ready: a thread apenas entra na fila intra-processo
}
// Verifica se todas as threads terminaram
if (this._allThreadsTerminated(processId) && processRt.state !== "Terminated") {
this._ioQueue = this._ioQueue.filter((r) => r.processId !== processId);
this._readyQueue = this._readyQueue.filter((r) => r.processId !== processId);
this._transition(processRt, "Terminated");
processRt.finishTick = this._tick + 1;
}
}
// ─── MANY_TO_MANY — gerenciamento de slots de kernel ─────────────────────
/**
* Libera um slot de kernel e tenta promover a próxima thread da kernel wait queue.
*/
private _releaseKernelSlot(processId: string): void {
const kwQueue = this._kernelWaitQueues.get(processId)!;
if (kwQueue.length > 0) {
// Repassa o slot diretamente para a próxima thread aguardando
const next = kwQueue.shift()!;
next.kernelWaiting = false;
this._transitionThread(next, "Ready");
this._readyThreadQueues.get(processId)!.push(next);
} else {
this._kernelSlotsAvailable.set(
processId,
(this._kernelSlotsAvailable.get(processId) ?? 0) + 1,
);
}
}
// ─── Helpers internos ─────────────────────────────────────────────────────
private _transition(rt: ProcessRuntime, state: StateLabel): void {
rt.state = state;
}
private _transitionThread(trt: ThreadRuntime, state: StateLabel): void {
trt.state = state;
}
private _advanceBurst(rt: ProcessRuntime): void {
rt.burstIndex++;
const process = this._processes.find((p) => p.id === rt.processId)!;
const nextBurst = process.bursts[rt.burstIndex];
rt.remainingBurst = nextBurst !== undefined ? nextBurst.duration : 0;
}
private _hasThreads(processId: string): boolean {
const p = this._processes.find((proc) => proc.id === processId);
return (p?.threads?.length ?? 0) > 0;
}
private _allThreadsTerminated(processId: string): boolean {
const threadMap = this._threadRuntimes.get(processId);
if (!threadMap) return true;
for (const trt of threadMap.values()) {
if (trt.state !== "Terminated") return false;
}
return true;
}
// ─── Inicialização ────────────────────────────────────────────────────────
private static _initProcessRuntime(p: Process, config: SchedulerConfig): ProcessRuntime {
const isThreaded = (p.threads?.length ?? 0) > 0;
if (!isThreaded) {
const firstBurst = p.bursts[0];
if (firstBurst === undefined || firstBurst.type !== "cpu") {
throw new Error(`Process ${p.id}: first burst must be a CPU burst`);
}
}
return {
processId: p.id,
state: "New",
remainingBurst: isThreaded ? 1 : (p.bursts[0]?.duration ?? 0),
burstIndex: 0,
arrivalTick: p.arrivalTime,
startTick: null,
finishTick: null,
waitingTime: 0,
cpuTime: 0,
ioTime: 0,
responseTime: null,
quantumRemaining: config.quantum,
currentPriority: p.priority,
};
}
private _initThreadStructures(p: Process): void {
const processId = p.id;
const model = p.threadModel ?? "ONE_TO_ONE";
const policy = p.threadSchedulingPolicy ?? "FCFS";
const quantum = p.threadQuantum ?? 2;
const threads = p.threads!;
// Criação do mapa de runtimes de threads
const threadMap = new Map<string, ThreadRuntime>(
threads.map((t) => [
t.tid,
SimulationEngine._initThreadRuntime(t, processId, p.arrivalTime, quantum),
]),
);
this._threadRuntimes.set(processId, threadMap);
// Escalonador intra-processo
this._threadSchedulers.set(processId, new ThreadScheduler(policy, quantum));
// Estruturas de estado
this._activeThreads.set(processId, null);
this._threadIoQueues.set(processId, []);
this._readyThreadQueues.set(processId, []);
// MANY_TO_MANY: inicializa slots de kernel
if (model === "MANY_TO_MANY") {
const M =
p.kernelThreadCount ??
Math.max(1, Math.ceil(threads.length / 2));
this._kernelSlotsAvailable.set(processId, M);
this._kernelWaitQueues.set(processId, []);
} else {
this._kernelWaitQueues.set(processId, []);
}
}
private static _initThreadRuntime(
t: Thread,
processId: string,
arrivalTick: number,
quantum: number,
): ThreadRuntime {
const firstBurst = t.bursts[0];
if (firstBurst === undefined || firstBurst.type !== "cpu") {
throw new Error(`Thread ${t.tid}: first burst must be a CPU burst`);
}
return {
threadId: t.tid,
processId,
state: "New",
remainingBurst: firstBurst.duration,
burstIndex: 0,
arrivalTick,
startTick: null,
finishTick: null,
waitingTime: 0,
cpuTime: 0,
ioTime: 0,
responseTime: null,
quantumRemaining: quantum,
currentPriority: t.priority ?? 5,
};
}
}