Home > Mobile >  Why does MacOS/iOS *force* the main thread to be the UI thread, and are there any workarounds?
Why does MacOS/iOS *force* the main thread to be the UI thread, and are there any workarounds?

Time:12-23

First off, I'd like to clarify that I'm not talking about concurrency here. I fully understand that having multiple threads modify the UI at the same time is bad, can give race conditions, deadlocks, bugs etc, but that's separate to my question.

I'd like to know why MacOS/iOS forces the main thread (ID 0, first thread, whatever) to be the thread on which the GUI must be used/updated/created on. see here, related:

on OSX/iOS the GUI must always be updated from the main thread, end of story.

I understand that you only ever want a single thread doing the acutal updating of the GUI, but why does that thread have to be ID 0?

(this is background info, TLDR below)
In my case, I'm making a rust app that uses a couple of threads to do things:

  • engine - does processing and calculations
  • ui - self explanatory
  • program/main - monitors other threads and generally synchronizes things

I'm currently doing something semi-unsafe and creating the UI on it's own thread, which works since I'm on windows, but the API is explicitly marked as BAD to use, and it's not cross compatible for MacOS/iOS for obvious reasons (and I want it to be as compatible as possible).

With the UI/engine threads (there may be more in the future), they are semi-unstable and could crash/exit early, outside of my control (external code). This has happened before, and so I want to have a graceful shutdown if anything goes wrong, hence the 'main' thread monitoring (among other things it does).

I am aware that I could just make Thread 0 the UI thread and move the program to another thread, but the app will immediately quit when the main thread quits, which means if the UI crashes the whole things just aborts (and I don't want this). Essentially, I need my main function on the main thread, since I know it won't suddenly exit and abort the whole app abruptly.

TL;DR

Overall, I'd like to know three things

  1. Why does MacOS/iOS enforce the GUI being on THread 0 (ignoring thread-safety outlined above)
  2. Are there any ways to bypass this (use a different thread for GUI), or will I simply need to sacrifice those platforms (and possible others I'm unaware of)?
  3. Would it be possible to do something like have the UI run as a separate process, and have it share some memory/communicate with the main process, using safe, simple rust?

p.s. I'm aware of this question, it's relevant but doesn't really answer my questions.

CodePudding user response:

Why does MacOS/iOS enforce the GUI being on Thread 0.

Because it's been that way for over 30 years now (since NeXTSTEP), and changing it would break just about every program out there, since almost every Cocoa app assumes this, and relies on it regularly, not just for the main thread, but also the main runloop, the main dispatch group, and now the main actor. External UI events (which come from other processes like the window manager) are delivered on thread 0. NSDistributedNotifications are delivered on thread 0. Signal handling, the list goes on. Yes, it is certainly possible for Darwin (which underlies Cocoa) to be rewritten to allow this. That's not going to happen. I'm not sure what other answer you want.

Would it be possible to do something like have the UI run as a separate process, and have it share some memory/communicate with the main process, using safe, simple rust?

Absolutely. See XPC, which is explicitly for this purpose (communicating, not sharing memory; don't share memory, that's a mess). See sys-xpc for the Rust interface.

  • Related