Skip to content

Commit 2ba14e4

Browse files
committed
Correcciones actualizadas para el botón de Detener (SIGKILL) de la terminal
1 parent 2fd7695 commit 2ba14e4

3 files changed

Lines changed: 88 additions & 53 deletions

File tree

core/executor.py

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,35 @@
1313
import time
1414
from typing import Dict, Any
1515

16-
if multiprocessing.current_process().name == 'MainProcess':
17-
input_queue = multiprocessing.Queue()
18-
gui_queue = multiprocessing.Queue()
19-
wait_flag = multiprocessing.Value('b', False)
20-
else:
21-
input_queue = None
22-
gui_queue = None
23-
wait_flag = None
16+
# Variables globales dinámicas (Se regenerarán en cada ejecución)
17+
input_queue = None
18+
gui_queue = None
19+
wait_flag = None
2420

2521
current_process = None
2622
current_process_lock = threading.Lock()
2723

28-
def _global_log_worker():
24+
# Worker independiente: Nace y muere con cada ejecución
25+
def _log_worker_routine(g_queue):
2926
import gevent
3027
while True:
31-
if gui_queue is not None:
32-
while not gui_queue.empty():
33-
try:
34-
msg = gui_queue.get_nowait()
35-
if msg == "__TRIGGER_INPUT__":
36-
eel.trigger_frontend_input()()
37-
else:
38-
eel.api_realtime_log(msg)()
39-
except Exception:
40-
pass
28+
# Extraemos todo lo que el proceso haya escupido
29+
while not g_queue.empty():
30+
try:
31+
msg = g_queue.get_nowait()
32+
if msg == "__TRIGGER_INPUT__":
33+
eel.trigger_frontend_input()
34+
else:
35+
eel.api_realtime_log(msg)
36+
except Exception:
37+
pass
38+
39+
with current_process_lock:
40+
if current_process is None or not current_process.is_alive():
41+
if g_queue.empty():
42+
break
4143
gevent.sleep(0.05)
4244

43-
# Iniciamos el worker del log solo en el proceso principal
44-
if multiprocessing.current_process().name == 'MainProcess':
45-
eel.spawn(_global_log_worker)
46-
4745
# -------------------------------------
4846
# CONFIGURACIÓN DE SEGURIDAD
4947
ALLOWED_MODULES = {
@@ -118,72 +116,90 @@ def interactive_input(prompt=""):
118116
result_queue.put({'success': False, 'output': '', 'error': traceback.format_exc()})
119117

120118

121-
def kill_execution() -> bool:
122-
global current_process
123-
if current_process is None:
124-
return False
119+
def submit_input(val: str):
120+
global input_queue, wait_flag
121+
if input_queue is not None:
122+
input_queue.put(val)
123+
if wait_flag is not None:
124+
wait_flag.value = False
125125

126+
def kill_execution():
127+
global current_process, gui_queue, wait_flag
126128
with current_process_lock:
127-
if current_process is not None and current_process.is_alive():
129+
if current_process and current_process.is_alive():
128130
current_process.terminate()
129-
current_process.join(timeout=1)
131+
132+
# Vaciamos la cola para destruir "inputs fantasmas" de último milisegundo
133+
if gui_queue is not None:
134+
while not gui_queue.empty():
135+
try:
136+
gui_queue.get_nowait()
137+
except:
138+
break
139+
130140
if wait_flag is not None:
131141
wait_flag.value = False
142+
143+
current_process.join(timeout=1)
132144
current_process = None
133-
return True
134-
145+
146+
try:
147+
eel.api_realtime_log("\n[SISTEMA] Ejecución detenida forzosamente.")
148+
except:
149+
pass
150+
151+
return True
152+
135153
return False
136154

137155
# ---------------------------------------------
138156
# EJECUTOR PRINCIPAL
139157
def execute_user_code(code: str, timeout: int = 5) -> Dict[str, Any]:
140-
global current_process
141-
result = {'success': False, 'output': '', 'error': ''}
142-
143-
if input_queue is None or gui_queue is None or wait_flag is None:
144-
return {'success': False, 'output': '', 'error': 'El entorno de ejecución no está inicializado correctamente.'}
145-
158+
global current_process, input_queue, gui_queue, wait_flag
159+
146160
if current_process is not None and current_process.is_alive():
147-
return {'success': False, 'output': '', 'error': 'Ejecutor ocupado. Espere a que termine la ejecución actual.'}
148-
149-
while not input_queue.empty():
150-
try:
151-
input_queue.get_nowait()
152-
except queue.Empty:
153-
break
161+
return {'success': False, 'output': '', 'error': 'Ejecutor ocupado.'}
154162

155-
# Usamos la cola nativa en lugar del manager
163+
# Creamos colas completamente nuevas y limpias para cada ejecución.
164+
# Esto ignora cualquier candado roto que haya dejado un "Kill" anterior.
165+
input_queue = multiprocessing.Queue()
166+
gui_queue = multiprocessing.Queue()
167+
wait_flag = multiprocessing.Value('b', False)
156168
result_queue = multiprocessing.Queue()
169+
157170
process = multiprocessing.Process(
158171
target=_execution_target,
159172
args=(result_queue, input_queue, gui_queue, wait_flag, code)
160173
)
161174

162175
with current_process_lock:
163176
current_process = process
177+
164178
process.start()
179+
180+
# Iniciamos el obrero de la terminal específico para estas colas
181+
eel.spawn(_log_worker_routine, gui_queue)
165182

166183
time_elapsed = 0.0
167184
while process.is_alive() and time_elapsed < timeout:
168185
eel.sleep(0.05)
186+
# Solo sumamos tiempo si no estamos esperando input del usuario
169187
if not wait_flag.value:
170188
time_elapsed += 0.05
171189

190+
# Si se excedió el tiempo límite natural
172191
if process.is_alive():
173192
process.terminate()
174193
process.join(timeout=1)
175194
with current_process_lock:
176195
current_process = None
177-
wait_flag.value = False
178-
result['error'] = 'Tiempo excedido (Timeout). ¿El proceso se colgó?'
179-
return result
196+
return {'success': False, 'output': '', 'error': 'Tiempo excedido (Timeout). ¿El proceso se colgó?'}
180197

181198
with current_process_lock:
182199
current_process = None
183200

184201
try:
185202
result = result_queue.get_nowait()
203+
return result
186204
except queue.Empty:
187-
result = {'success': False, 'output': '', 'error': 'Error desconocido de procesamiento.'}
188-
189-
return result
205+
return {'success': False, 'output': '', 'error': 'Proceso terminado inesperadamente (posible SIGKILL).'}

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from core.ml_struct_rules import block_to_struct as ml_block_to_struct
3131
from storage import ml_exporter
3232
from storage import file_handler
33-
from core.executor import execute_user_code, input_queue, kill_execution
33+
from core.executor import execute_user_code, submit_input, kill_execution
3434
from core import ml_manager
3535
from estimators import memory_estimator
3636
# import maker_edu.auth
@@ -1085,7 +1085,7 @@ def api_get_grades():
10851085
@eel.expose
10861086
def api_provide_input(user_text):
10871087
"""Recibe la respuesta de la terminal visual y desbloquea el hilo de Python"""
1088-
input_queue.put(user_text)
1088+
submit_input(user_text)
10891089

10901090
# --------------------------------------------
10911091
# PUNTO DE ENTRADA PRINCIPAL

web/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,7 @@ <h3 className="text-red-400 font-bold uppercase text-xs mb-3 flex items-center g
14721472
});
14731473
const [showSettings, setShowSettings] = useState(false);
14741474
const [showTeacherDashboard, setShowTeacherDashboard] = useState(false);
1475+
const isExecutingRef = useRef(false);
14751476

14761477

14771478
useEffect(() => {
@@ -1488,6 +1489,24 @@ <h3 className="text-red-400 font-bold uppercase text-xs mb-3 flex items-center g
14881489
}
14891490
}, []);
14901491

1492+
useEffect(() => {
1493+
isExecutingRef.current = isExecuting;
1494+
}, [isExecuting]);
1495+
1496+
useEffect(() => {
1497+
window.eel.expose(api_realtime_log, 'api_realtime_log');
1498+
function api_realtime_log(text) {
1499+
setLogs(p => [...p, { time: new Date().toLocaleTimeString(), msg: text, type: 'output' }]);
1500+
}
1501+
1502+
window.eel.expose(trigger_frontend_input, 'trigger_frontend_input');
1503+
function trigger_frontend_input() {
1504+
if (isExecutingRef.current) {
1505+
setIsWaitingInput(true);
1506+
}
1507+
}
1508+
}, []);
1509+
14911510
const handleTerminalSubmit = async (e) => {
14921511
if (e.key === 'Enter') {
14931512
const val = terminalInput;

0 commit comments

Comments
 (0)