[object Object]

← back to Exo

exo: handle -c flag for multiprocessing helpers in frozen apps

9afc1043ef93f28322646621a1dffb99893b12e4 · 2025-12-23 15:11:49 +0000 · Jake Hillion

When Python's multiprocessing spawns child processes on macOS (using the
"spawn" method), it also spawns helper processes like the resource tracker
by executing:

    ./frozen_app -c "from multiprocessing.resource_tracker import main; main()"

A frozen PyInstaller app doesn't understand `-c` natively - it just runs
main(). This causes the resource tracker to fail silently.

This adds a minimal `-c` handler that intercepts the flag, extracts the
inline code, and exec()s it before main() runs. This is required for the
Process() spawn in runner_supervisor.py to work correctly in the DMG.

Note that the pyinstaller docs say `freeze_support` is supposed to make
this work, but it doesn't.

Test plan:

Hardware setup: 3x Mac Studio M3 Ultra connected all-to-all with TB5

- Built a DMG[0].
- Installed on the Macs.
- Started an instance. Got an error this time in ~/.exo/exo.log. The
  last DMG from main doesn't show anything when an instance starts, this
  now shows the errors.

[0] https://github.com/exo-explore/exo/actions/runs/20464409279/job/58804485197

Files touched

Diff

commit 9afc1043ef93f28322646621a1dffb99893b12e4
Author: Jake Hillion <jake@hillion.co.uk>
Date:   Tue Dec 23 15:11:49 2025 +0000

    exo: handle -c flag for multiprocessing helpers in frozen apps
    
    When Python's multiprocessing spawns child processes on macOS (using the
    "spawn" method), it also spawns helper processes like the resource tracker
    by executing:
    
        ./frozen_app -c "from multiprocessing.resource_tracker import main; main()"
    
    A frozen PyInstaller app doesn't understand `-c` natively - it just runs
    main(). This causes the resource tracker to fail silently.
    
    This adds a minimal `-c` handler that intercepts the flag, extracts the
    inline code, and exec()s it before main() runs. This is required for the
    Process() spawn in runner_supervisor.py to work correctly in the DMG.
    
    Note that the pyinstaller docs say `freeze_support` is supposed to make
    this work, but it doesn't.
    
    Test plan:
    
    Hardware setup: 3x Mac Studio M3 Ultra connected all-to-all with TB5
    
    - Built a DMG[0].
    - Installed on the Macs.
    - Started an instance. Got an error this time in ~/.exo/exo.log. The
      last DMG from main doesn't show anything when an instance starts, this
      now shows the errors.
    
    [0] https://github.com/exo-explore/exo/actions/runs/20464409279/job/58804485197
---
 src/exo/__main__.py | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/src/exo/__main__.py b/src/exo/__main__.py
index bd18145c..d2febaee 100644
--- a/src/exo/__main__.py
+++ b/src/exo/__main__.py
@@ -1,7 +1,39 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Sequence
 from multiprocessing import freeze_support
+from typing import Final
 
 from exo.main import main
 
+INLINE_CODE_FLAG: Final[str] = "-c"
+
+
+def _maybe_run_inline_code(argv: Sequence[str]) -> bool:
+    """
+    Reproduce the bare minimum of Python's `-c` flag so multiprocessing
+    helper processes (for example the resource tracker) can execute.
+    """
+
+    try:
+        flag_index = argv.index(INLINE_CODE_FLAG)
+    except ValueError:
+        return False
+
+    code_index = flag_index + 1
+    if code_index >= len(argv):
+        return False
+
+    inline_code = argv[code_index]
+    sys.argv = ["-c", *argv[code_index + 1 :]]
+    namespace: dict[str, object] = {"__name__": "__main__"}
+    exec(inline_code, namespace, namespace)
+    return True
+
+
 if __name__ == "__main__":
+    if _maybe_run_inline_code(sys.argv):
+        sys.exit(0)
     freeze_support()
     main()

← 70c423f5 feat: conform to XDG Base Directory Specification on Linux (  ·  back to Exo  ·  mlx: update to 0.30.1 and align coordinator naming with MLX 1c1792f5 →