Problem with NSSound playback in XPC service

Hello,

I run into an issue on Monterey (12.7.5). I have a bundled XPC service in my application which is displaying some stuff and playin sounds via NSSound.

I had a problem with playback due to service priority, so I use the trick with a reply block where I send a reply block to the service and basically just retain it and never call it.

This worked fine so far, but we have users, predominantly on Monterey, who are having a problem with sound playback. It's choppy and distorted when their machine is under load (where "load" often just means playing a video on YouTube in Chrome).

Is there anything else I can do to get the proper priority for my xpc service so I can avoid distorted sound?

Additionally the service type is Application and RunLoopType is NSRunLoop with JoinExistingSession set to true. The QoS level of main queue is 0x21 (user interactive) and I'm calling all the NSSound APIs on main queue.

Answered by DTS Engineer in 806879022

I run into an issue on Monterey (12.7.5).

Are you seeing this issue on later system versions? This area of the system has been under very active development, so it's entirely possible for you app to be doing "the right thing" and simply not work correctly on the older system because the scheduler isn't working "right".

I have a bundled XPC service in my application which is displaying some stuff and playin sounds via NSSound.

Is your XPC service directly presenting it's own window(s) or is this coming through your main app? Also, being precise, is this a true XPC service (meaning it was directly launched by the parent app as a helper service) or this a standalone component (for example, a login item) that communicates using XPC?

Jumping to your message:

HALC_ProxyIOContext::IOWorkLoop: skipping cycle due to overload ... HALS_OverloadMessage: Overload due to client running as an adaptive unboosted

So indeed it seems to be some priority problem?

Sort of, though the term "priority" can be misleading. The standard Unix model is built around the idea that proceses are assigned a relatively fixed priority which is then use to schedule execution time, but macOS never really used that model (even in 10.0) and it's "modern" evolution has only widened that gap.

In terms of the message here, the audio system heavily relies on mach real time thread scheduling. Note that this is an example of how the standard "Unix priority" model doesn't really "fit" how macOS works. Real time threads are basically scheduled in terms of a specific amount of runtime during a particular time interval. They're designed to provide predictable execution time with low latency, not to provide substansial "overall" time.

In any case, the first message happens anytime the HAL realizes it hasn't gotten the execution time it expected, which will cause audio glitches. The second message is a diagnostic message about "why" that's occurred. In this case, "adaptive unboosted" means that it's running in a process where it's priority was so low that it real time thread was not scheduled.

That leads to here:

I had a problem with playback due to service priority, so I use the trick with a reply block where I send a reply block to the service and basically just retain it and never call it.

Part of what XPC "does" is transfer priority between threads/processes, so a high priority process (like an interactive app) doesn't get stuck waiting on a low priority daemon. Holding on to a reply like this means that the system views your process views your service as blocking the main app, which should elevate your processes priority.

A few things you should look at here:

  • What is your main app doing when the glitches actually happen? If it's been moved to the background, then the issue may actually be that your main app isn't actually "foreground interactive". If you're not already doing so, you may want to try having your app using "beginActivityWithOptions" to ensure that it's remaining active.

  • What are the XPC interactions between your app and service? Do you ever "reset" your held reply block, or are you just holding a single reply "forever"? Particularly on a old system like this, you may find that periodically "resetting" the message (say, every minute) works better than simply holding the same message "forever".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

So I found following being all over the logs when issue occurs:

HALC_ProxyIOContext::IOWorkLoop: skipping cycle due to overload

The machine is i9 with 64GB of RAM basically idling.

Edit:

Found another message which seems to lead to source of the problem:

HALS_OverloadMessage: Overload due to client running as an adaptive unboosted 

So indeed it seems to be some priority problem?

Accepted Answer

I run into an issue on Monterey (12.7.5).

Are you seeing this issue on later system versions? This area of the system has been under very active development, so it's entirely possible for you app to be doing "the right thing" and simply not work correctly on the older system because the scheduler isn't working "right".

I have a bundled XPC service in my application which is displaying some stuff and playin sounds via NSSound.

Is your XPC service directly presenting it's own window(s) or is this coming through your main app? Also, being precise, is this a true XPC service (meaning it was directly launched by the parent app as a helper service) or this a standalone component (for example, a login item) that communicates using XPC?

Jumping to your message:

HALC_ProxyIOContext::IOWorkLoop: skipping cycle due to overload ... HALS_OverloadMessage: Overload due to client running as an adaptive unboosted

So indeed it seems to be some priority problem?

Sort of, though the term "priority" can be misleading. The standard Unix model is built around the idea that proceses are assigned a relatively fixed priority which is then use to schedule execution time, but macOS never really used that model (even in 10.0) and it's "modern" evolution has only widened that gap.

In terms of the message here, the audio system heavily relies on mach real time thread scheduling. Note that this is an example of how the standard "Unix priority" model doesn't really "fit" how macOS works. Real time threads are basically scheduled in terms of a specific amount of runtime during a particular time interval. They're designed to provide predictable execution time with low latency, not to provide substansial "overall" time.

In any case, the first message happens anytime the HAL realizes it hasn't gotten the execution time it expected, which will cause audio glitches. The second message is a diagnostic message about "why" that's occurred. In this case, "adaptive unboosted" means that it's running in a process where it's priority was so low that it real time thread was not scheduled.

That leads to here:

I had a problem with playback due to service priority, so I use the trick with a reply block where I send a reply block to the service and basically just retain it and never call it.

Part of what XPC "does" is transfer priority between threads/processes, so a high priority process (like an interactive app) doesn't get stuck waiting on a low priority daemon. Holding on to a reply like this means that the system views your process views your service as blocking the main app, which should elevate your processes priority.

A few things you should look at here:

  • What is your main app doing when the glitches actually happen? If it's been moved to the background, then the issue may actually be that your main app isn't actually "foreground interactive". If you're not already doing so, you may want to try having your app using "beginActivityWithOptions" to ensure that it's remaining active.

  • What are the XPC interactions between your app and service? Do you ever "reset" your held reply block, or are you just holding a single reply "forever"? Particularly on a old system like this, you may find that periodically "resetting" the message (say, every minute) works better than simply holding the same message "forever".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hello Kevin, I found the issue but I'll reply anyway as it may be useful to other people.

Are you seeing this issue on later system versions?

No, it looks like it's only Monterey, specifically the last two minor releases which I tested on.

Is your XPC service directly presenting it's own window(s) or is this coming through your main app?

Yes, I'm starting a NSApplication on main thread and the service listener on a second thread. That's only difference to regular XPCService template running just xpc_main().

If you're not already doing so, you may want to try having your app using "beginActivityWithOptions" to ensure that it's remaining active.

I tried this both in application and xpc service and it didn't had any effect. I even tried task_policy, setting main thread to user interactive.

Particularly on a old system like this, you may find that periodically "resetting" the message (say, every minute) works better than simply holding the same message "forever".

I tried exactly this and it works. I was sending the reply block only at the start of the application to kickstart the service and boost it. Sending the block again before I want a playback seems to fix the priority problem on Monterey and doesn't have any side effects on newer systems.

I still have a question though. Is there any recommendation how to investigate xpc service priorities (or QoS in general)? I tried launchctl print pid/13422/com.my.service and it gives handful of information but nothing I could figure out the boosting was not working correctly (spawn type seems to reflect only the startup priority). Also in Instruments I was using time profiler, system trace etc. and it shown the correct application lifecycle ("Foreground") but sound was still broken.

Problem with NSSound playback in XPC service
 
 
Q