"App not responding" in the dock menu

I have a bundled macOS application. This is a non-interactive application where I m performing some task on the worker thread while the main thread waits for this task to be completed. Sometimes this task can be time consuming.

I have observed that when I run the application using the bundle( like double click or open command) I can see the OS marking my application as not responding( this is evident as the app icon toggles in the dock and then it states not responding).

Although If I run the unix executable in the bundle, the app runs and I do not see the not responding status anywhere.

I wanted to understand If this is happening because my main thread is in a waiting state? If yes, what could I do to resolve it because my application logic demands the main thread to wait for the worker thread to complete its task. Is there some way to use some event loop like GCD?

Note: I cannot use the delegates(Appkit) event loop because my application will be run in non-GUI context.

Answered by DTS Engineer in 795194022

First off, we need to clarify the vocabulary here, as the term "context" has a very specific meaning in macOS that's much narrower than what you're using. What I'd actually recommend here is that you start by reading the timeless classic "TN2083: Daemons and Agents", which lays out how execution contexts work in macOS.

However, the critical point here is that what "user context" means in macOS is that a process is part of the users login session/execution environment. Case in point, all of these cases:

  1. Double click on the app.

  2. Open up Terminal.app and run the app through "open".

  3. Open up Terminal.app and run the executable directly.

...are running in the "user context". I think what's confused the issue here is that user context isn't defined by "what" you are (like GUI vs. non-GUI). The "open" command line tool is actually a great example of this- open only really works when run inside the user context because what it actually does (use LaunchServices to "open stuff") is inherently tied to the user interface.

With that background, let me jump back to here:

Note: I cannot use the delegates(Appkit) event loop because my application will be run in non-GUI context.

Is that actually true? If so, how/why is it being run that way? This would typically be done using a launch daemon, but it would be pretty odd to have a launch daemon that was also a double clickable app.

Moving back to the question here:

I wanted to understand If this is happening because my main thread is in a waiting state?

Sort of. More specifically, the system expects "apps" to have a functioning main runloop it can deliver events to. Apps are then marked as "not responding" if their main runloop is blocked for to long or, as in your case, non-existant.

Quick answer to here:

Is there some way to use some event loop like GCD?

I don't think GCD itself will work here but I haven't actually tried. dispatch_main exists to provide daemon's that operate below CoreFoundation with an architecture option similar to the runloop, however, I don't think it will satisfy the WindowServer.

If yes, what could I do to resolve it because my application logic demands the main thread to wait for the worker thread to complete its task.

Two different options here:

a) You may want to consider reworking your code into your core "tool" (which will be a pure-command line tool) and your "app" component (which is what would run when launched the app was double clicked). Your app component would then execute your command line tool to do whatever needed to be done. The advantage of this approach is that it makes it easy for give the user other interaction options, even if you don't want to create a full app. Things using the app icon to provide progress/status information, a dock menu for minor interactions, or simply posting information to the user through notifications. Keep in mind that running "as an app" doesn't mean your app has a menu bar or any windows. You can basically make this do whatever you want.

For most use case this is the right choice, since having an app you can launch but that doesn't actually DO anything isn't all that useful.

b) If you're ONLY goal is to prevent the "not responding" status, then you can use a very minimal main that runs the runloop "itself" to do that. Here's a very simply implementation I just threw together:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [NSTimer scheduledTimerWithTimeInterval: 0.1 repeats: YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"Yo");
        }];
        while(1) {
            [[NSRunLoop mainRunLoop] run];
        }
    }
    return 0;
}

Note that the timer is actually doing two different things here:

  1. (minor) It makes it easy to confirm that the code actually "works" and is running.

  2. (major) All of NSRunLoop's "run" methods immediately return unless they have "something" to do, so having the timer scheduled is what actually cause "run" to block here. In real usage you'd typically use the main thread for things like timers, callbacks, etc., which you'd setup before you called run. However, if you don't have ANY usage for the main thread, then you can also schedule the timer arbitrarily far in the future and then do all of your work on a background thread (which you'd kick off before calling "run"). What matters here is that the timer is scheduled, not that it actually runs.

One more detail- the code above will never exit on it's own because run will never return. While it's technically possible to make it return by ending whatever work you're doing, in practice that doesn't actually work all that well and isn't worth the trouble. If you're using this architecture, you should just call "exit" whenever you've finished your work, which is exactly with all our frameworks do.

-Kevin

I cannot use the delegates(Appkit) event loop because my application will be run in non-GUI context.

First, let’s be clear about terminology. In Apple parlance, an app is something that the user runs by double clicking the Finder (or from the Home screen on iOS, and likewise for other platforms). If something is running in a non-GUI context then it’s not an app.

So, how is your program being launched?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It is a non-interactive bundled application but it can be run in both gui and non-gui context.

In non-gui context(like ssh terminal), this is launched using daemons or using the unix executable in the bundle like ..

cd MyApp.app/Content/MacOS
./MyApp

In the gui context, the program can be launched by double clicking the bundled app or using 'open' command.

If something is running in a non-GUI context then it’s not an app.

For the above statement, If my application is capable of running in non-gui context, then what is it that the apple recommends doing. Should I be not creating a bundled application?

Accepted Answer

First off, we need to clarify the vocabulary here, as the term "context" has a very specific meaning in macOS that's much narrower than what you're using. What I'd actually recommend here is that you start by reading the timeless classic "TN2083: Daemons and Agents", which lays out how execution contexts work in macOS.

However, the critical point here is that what "user context" means in macOS is that a process is part of the users login session/execution environment. Case in point, all of these cases:

  1. Double click on the app.

  2. Open up Terminal.app and run the app through "open".

  3. Open up Terminal.app and run the executable directly.

...are running in the "user context". I think what's confused the issue here is that user context isn't defined by "what" you are (like GUI vs. non-GUI). The "open" command line tool is actually a great example of this- open only really works when run inside the user context because what it actually does (use LaunchServices to "open stuff") is inherently tied to the user interface.

With that background, let me jump back to here:

Note: I cannot use the delegates(Appkit) event loop because my application will be run in non-GUI context.

Is that actually true? If so, how/why is it being run that way? This would typically be done using a launch daemon, but it would be pretty odd to have a launch daemon that was also a double clickable app.

Moving back to the question here:

I wanted to understand If this is happening because my main thread is in a waiting state?

Sort of. More specifically, the system expects "apps" to have a functioning main runloop it can deliver events to. Apps are then marked as "not responding" if their main runloop is blocked for to long or, as in your case, non-existant.

Quick answer to here:

Is there some way to use some event loop like GCD?

I don't think GCD itself will work here but I haven't actually tried. dispatch_main exists to provide daemon's that operate below CoreFoundation with an architecture option similar to the runloop, however, I don't think it will satisfy the WindowServer.

If yes, what could I do to resolve it because my application logic demands the main thread to wait for the worker thread to complete its task.

Two different options here:

a) You may want to consider reworking your code into your core "tool" (which will be a pure-command line tool) and your "app" component (which is what would run when launched the app was double clicked). Your app component would then execute your command line tool to do whatever needed to be done. The advantage of this approach is that it makes it easy for give the user other interaction options, even if you don't want to create a full app. Things using the app icon to provide progress/status information, a dock menu for minor interactions, or simply posting information to the user through notifications. Keep in mind that running "as an app" doesn't mean your app has a menu bar or any windows. You can basically make this do whatever you want.

For most use case this is the right choice, since having an app you can launch but that doesn't actually DO anything isn't all that useful.

b) If you're ONLY goal is to prevent the "not responding" status, then you can use a very minimal main that runs the runloop "itself" to do that. Here's a very simply implementation I just threw together:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [NSTimer scheduledTimerWithTimeInterval: 0.1 repeats: YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"Yo");
        }];
        while(1) {
            [[NSRunLoop mainRunLoop] run];
        }
    }
    return 0;
}

Note that the timer is actually doing two different things here:

  1. (minor) It makes it easy to confirm that the code actually "works" and is running.

  2. (major) All of NSRunLoop's "run" methods immediately return unless they have "something" to do, so having the timer scheduled is what actually cause "run" to block here. In real usage you'd typically use the main thread for things like timers, callbacks, etc., which you'd setup before you called run. However, if you don't have ANY usage for the main thread, then you can also schedule the timer arbitrarily far in the future and then do all of your work on a background thread (which you'd kick off before calling "run"). What matters here is that the timer is scheduled, not that it actually runs.

One more detail- the code above will never exit on it's own because run will never return. While it's technically possible to make it return by ending whatever work you're doing, in practice that doesn't actually work all that well and isn't worth the trouble. If you're using this architecture, you should just call "exit" whenever you've finished your work, which is exactly with all our frameworks do.

-Kevin

Thanks for the response.

On a different note, I have an application that is meant to be run as a daemon. What is it that the apple recommends on how to create the daemon application(in terms of creating the event loop)?

From apple documentations, I understand that we should be using only daemon safe frameworks for creating a daemon application. According to it, we are not allowed to use Appkit( hence we cannot use the delegate implicit event loop).

What is the alternative to it, how to create the event loop now?

Reordering things a bit for clarity...

According to it, we are not allowed to use Appkit( hence we cannot use the delegate implicit event loop).

The implicit delegate pattern is a characteristic of our ObjectiveC APIs, NOT just AppKit. Generally speaking, unless an API specifically documents where it's callbacks run, it probably relies on an implicit runloops. Note that this applies to many Foundation APIs.

From apple documentations, I understand that we should be using only daemon safe frameworks for creating a daemon application.

Just to clarify here, "daemon safe" really means "this framework doesn't interact with things that daemon's aren't supposed to interact with". For example, "Foundation" is daemon safe because it was designed as a support framework which was inherently "non-GUI", not because of an explicit "we must make sure this is daemon safe". As a different flavor of this, the DiskArbitration framework is daemon safe because it's the API side of diskarbirtationd... the daemon that that handles volume auto-mounting. More broadly, most of the frameworks listed as daemon safe in the document are in that list because they're the same frameworks our daemon's use.

On a different note, I have an application that is meant to be run as a daemon. What is it that the apple recommends on how to create the daemon application(in terms of creating the event loop)?

In terms of "direct" system APIs, the three basic choices are:

  1. Directly use NSRunLoop (or CFRunLoop). Note that the main function I sent earlier is this basic approach.

  2. Use dispatchMain.

  3. Do your own thing. Strictly speaking, nothing requires you to use either of these approaches. You could write a daemon entirely in POSIX, which simply use the main thread for "work" and doesn't return.

My "default" answer here would probably be #1, as the provides access to the broadest possible API set with the lowest possibility of failure. However, this really does depend on what you're actually trying to do and what APIs you're planning to use. For example, if my daemon was written in C and built around DispatchIO, then DispatchMain might be a more straightforward choice. I wouldn't be using Foundation (or any other ObjC framework) and I was already architected around DispatchIO, so why mess with a framework that I wasn't going to use.

One caution on that point- these are decisions that should actually be thought through, not based on implicit expectation or guesswork. For example, many EndpointSecurity client are built around GCD because, as far as I can tell, our sample happens to have been built around GCD. However:

  1. The sample was only written as a basic demonstration tool, not as a representative of our intended architecture.

  2. The engineer who wrote the sample happened to be very familiar and comfortable with it.

Many ES clients would be FAR better off using NSOperation/NSOperationQueue* but used GCD because instead because they never actually considered what the right choice.

*Architecturally, NSOperationQueue is basically "GCD plus other things that are useful". That "useful" list includes a base class for work objects (not everything needs to be a block) and direct support for cancellation, both of which are extremely useful. If you're trying to retrofit cancellation into GCD, you should be using NSOperation.

What is the alternative to it, how to create the event loop now?

As I noted earlier, the main function I sent earlier is daemon safe. However, again, the right choice here really depends on what your actually doing.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

"App not responding" in the dock menu
 
 
Q