fix(ctl): prevent fork deadlock and file watcher triggers

Fix two issues with the control socket feature introduced in 25.1.0:

1. Fork deadlock (#3509): The control server runs asyncio in a background
   thread. When fork() is called to spawn workers, locks held by asyncio
   can remain locked in the child process, causing deadlocks. Fix by
   stopping the control server before fork and restarting after fork in
   the parent.

2. File watcher triggers (#3508): The default socket path `gunicorn.ctl`
   was created in the app directory, triggering file watchers in dev
   servers (e.g., GAE dev_appserver). Fix by changing the default path
   to `/tmp/gunicorn.ctl`.

Fixes #3508
Fixes #3509
This commit is contained in:
Benoit Chesneau 2026-02-19 15:57:41 +01:00
parent 2d4310116d
commit d3f80e8cfd
2 changed files with 10 additions and 3 deletions

View File

@ -686,11 +686,18 @@ class Arbiter:
self.app, self.timeout / 2.0,
self.cfg, self.log)
self.cfg.pre_fork(self, worker)
# Stop control server before fork to prevent deadlocks.
# The asyncio thread holds locks that would be stuck in the child.
self._stop_control_server()
pid = os.fork()
if pid != 0:
worker.pid = pid
self.WORKERS[pid] = worker
self._stats['workers_spawned'] += 1
# Restart control server in parent after fork
self._start_control_server()
return pid
# Do not inherit the temporary files of other workers

View File

@ -3123,7 +3123,7 @@ class ControlSocket(Setting):
cli = ["--control-socket"]
meta = "PATH"
validator = validate_string
default = "gunicorn.ctl"
default = "/tmp/gunicorn.ctl"
desc = """\
Unix socket path for control interface.
@ -3131,8 +3131,8 @@ class ControlSocket(Setting):
``gunicornc`` command-line tool. Commands include viewing worker
status, adjusting worker count, and graceful reload/shutdown.
By default, creates ``gunicorn.ctl`` in the working directory.
Set an absolute path for a fixed location (e.g., ``/var/run/gunicorn.ctl``).
By default, creates ``/tmp/gunicorn.ctl``. Set an absolute path for a
different location (e.g., ``/var/run/gunicorn.ctl``).
Use ``--no-control-socket`` to disable.