Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion system/manager/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ def main() -> None:


if __name__ == "__main__":
unblock_stdout()
# Skip unblock_stdout on macOS - os.forkpty() causes crashes with raylib/AppKit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? i believe this used to work

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is described here: #36785

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see the issue, but why does it happen now?

Copy link
Author

@zgohr zgohr Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MetaDrive's raylib initializes AppKit before manager.py runs. unblock_stdout() then calls os.forkpty(), which triggers macOS CoreFoundation's fork safety crash (can't fork after Cocoa init). Doesn't affect on-device since no GUI frameworks are loaded before manager starts.

The Objective-C runtime can't be both thread-safe and fork-safe. After AppKit/CoreFoundation initializes, it may have threads running or state that can't be safely duplicated via fork(). Apple's solution: crash with the message:

The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().

The rule is: after fork(), you must immediately exec() a new program (which replaces the process image). But os.forkpty() in unblock_stdout() forks and continues running Python in the child — no exec().

Workarounds:

  • spawn instead of fork (what multiprocessing uses by default on macOS now)
  • OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES env var (must be set before process starts)
  • Or just skip the fork entirely on macOS (what my fix does)

My fix is cleanest approach since unblock_stdout() isn't critical, from what I can tell. Even from looking back at the git history, it's not clear why it's there in the first place. But take for instance it's there to potentially speed up on-device logging pipelines; since the sim runs in terminal, blocking shouldn't be a concern.

if sys.platform != 'darwin':
unblock_stdout()

try:
main()
Expand Down