1313import time
1414from 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
2521current_process = None
2622current_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
4947ALLOWED_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
139157def 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).' }
0 commit comments