Hola!
Gracias por responder.
Los threads no se terminan o cierran cuando el problema sucede. Siguen vivos pues en teoría no tienen que terminar, solo que están mal funcionando debido a que van a todo gas ya que de repente deja de funcionar el wait que hace el tmonitor dentro del objeto TThreadeQueue cuando se le llama al pop (en definitiva que se quede a la espera/dormido durante un tiempo a que llegue algo y sino al del tiempo da el timeout).
Tengo logs por todos lados que me centraron en que el tema estaba por aquí, lo he visto debuggando que falla aquí (tengo puesto que se queden los threads infinitamente en el pop pero salen todos los threads al instante sin que se haya hecho ningún push). Todo sucede a partir de un momento, afecta a un grupo de threads, más adelante en el tiempo afecta a otro grupo de threads, y así sigue. Lo que hay en común entre todos es el tmonitor que es la tripa de la clase.
El tmonitor tiene la función wait que espera que haya eventos o que llegue el timeout. Al debuggar pude llegar hasta aquí, pero ahí ya es código que bufffff complicado saber que pasa y por qué:
function TMonitor.Wait(ALock: PMonitor; Timeout: Cardinal): Boolean;
var
RecursionCount: Integer;
WaitingThread: TWaitingThread;
begin
WaitingThread.Next := nil;
WaitingThread.Thread := ALock.CheckOwningThread;
// This event should probably be cached someplace.
// Probably not on the instance since this is a per-thread-per-instance resource
WaitingThread.WaitEvent := MonitorSupport.NewWaitObject;
try
// Save the current recursion count for later
RecursionCount := ALock.FRecursionCount;
// Add the current thread to the waiting queue
QueueWaiter(WaitingThread);
// Set it back to almost released so the next Exit call actually drops the lock
ALock.FRecursionCount := 1;
// Now complete the exit and signal any waiters
ALock.Exit;
// Get in line for someone to do a Pulse or PulseAll
Result := MonitorSupport.WaitOrSignalObject(nil, WaitingThread.WaitEvent, Timeout) = WAIT_OBJECT_0;
// Got to get the lock back and block waiting for it.
ALock.Enter(INFINITE);
// Remove any dangling waiters from the list
RemoveWaiter(WaitingThread);
// Lets restore the recursion to return to the proper nesting level
ALock.FRecursionCount := RecursionCount;
finally
MonitorSupport.FreeWaitObject(WaitingThread.WaitEvent);
end;
end;
El problema empieza en la linea:
WaitingThread.WaitEvent := MonitorSupport.NewWaitObject;
pues devuelve 'nil', y en la llamada posterior
Result := MonitorSupport.WaitOrSignalObject(nil, WaitingThread.WaitEvent, Timeout) = WAIT_OBJECT_0;
sale de la función como un rayo pues no hay handle para el timeout claro...Entrando a la función MonitorSupport.NewWaitObject es donde ya no queda claro como va la cosa porque es código raro del system.pas que además si no debuggas no sabes llegar a él pues si lo buscas en diseño conduce a punteros a funciones...en fin...
La sensación es como si se le gastara la posibilidad de asignar más eventos de espera, y ello apunta a que puede ser que el TMonitor no los libera bien, quiero pensar.
No es por lanzar balones fuera, pero la aplicación a priori solo hace push y pop, y crea al inicio y destruye al final los objetos, así que no creo que haya algo que hace el código que he metido para provocarlo, aunque nunca se sabe.
No tengo claro como enfocar el tema. Solo se le ocurre hacer una prueba en limpio con un thread que hace pops y otro que hace push, tenerlo a todo trapo, ver si pasa lo mismo al de x horas. si es así es claro que el asunto es algún bug del tmonitor. En caso de que no suceda, ya apunta a que alguna otra parte de la aplicación le afecta al tmonitor