Commit Graph
2 Commits
Author SHA1 Message Date
Vito 93bf24f47d fix(monitor): keep watchdog threads from holding the exit (#561) (#569)
* fix(monitor): keep watchdog threads from holding the exit (#561)

watchdog 7.0.0 on Windows starts a plain threading.Thread per watch for
its ReadDirectoryChangesW reader (DirectoryChangeReader.start). Created
from ComfyUI's main thread it is not a daemon, and the interpreter joins
every non-daemon thread before it runs any atexit callback, so the
package teardown that would stop the observer never ran and the process
never exited.

Start the observer from a short-lived daemon thread, so every thread it
creates inherits the daemon flag. Run observer.stop(), which joins the
emitters without a limit, on a daemon thread and wait for it only until
the stop deadline; a later stop waits for the same call. Lift the
watchdog<7 cap.

Closes #561

* test(exit): exempt watchdog 7's process-wide inotify reader (#561)

watchdog 7 on Linux reads every inotify watch through one InotifyFD
daemon thread per process that no observer stop ends, by design. The
exit and package tests counted every watchdog BaseThread as the
package's, so the singleton failed them once the cap was lifted. Exclude
it, and require instead that no non-daemon thread is left at exit.
2026-10-31 21:31:55 +00:00
Vito f3b9f61f11 fix(threads): stop background work at exit, on cleanup and on the tests' unload (#301) (#374)
* fix(threads): stop background work at exit, on cleanup and on the tests' unload (#301)

- __init__.py: _stop_background_work() detaches the monitor's callbacks,
  stops the monitor, closes the preview worker and shuts the prompt
  cleanup thread down against one 10 s deadline, once. It runs from
  atexit (ComfyUI never runs aiohttp's cleanup hooks), from on_cleanup
  where a host runs it, and from the tests' package unload; it removes
  its own atexit callback. Registered after the history hook, so at exit
  it runs before the history dispose.
- AutoThumbnailer.close/close_auto_thumbnails take a timeout and return
  whether the worker ended.
- Tests: teardown result, once-only, on_cleanup, atexit order, a failing
  step or a second Ctrl+C never skipping the tracker, one shared
  deadline, three repeated loads leaving no thread, and a child process
  that exits normally with the image worker alive.

* fix(threads): refuse new previews before the monitor stops; isolate the exit child (#301)

Review findings:
- The teardown makes the API refuse new previews before it detaches the
  monitor's callbacks: an image in hand that already read the preview
  queue no longer starts a preview worker during exit (Python 3.12+
  refuses that thread and logs a traceback). New
  refuse_auto_thumbnails().
- The teardown imports its bounds inside each step and falls back to the
  standard logger, so a failing import never skips the other steps.
- The exit child keeps its library and log in its own folder
  (PROMPT_MANAGER_DATA_DIR), reports watchdog's threads too, fails with
  the child's output on a hang, and rejects "Error while stopping".
- TESTING.md: "Background threads at exit".
2026-10-27 02:53:02 -07:00