Scheduled activities: 1 Scheduling by DAS

Having given the history of how macOS currently runs scheduled background activities, this article starts to explain how they work in more detail.

launchd or in-app

The great majority, if not all, scheduled background activities are set up by launchd automatically during startup. These are configured by property list files stored in special folders inside the standard Library folders. This isn’t the only mechanism, though: apps and processes can also use the NSBackgroundActivityScheduler API to do this. I will explore and demonstrate that in a later article.

Daemons and agents

Apple distinguishes four main types of background process in macOS:

  • Login Items, which are started by launchd but aren’t managed by it, run in a user context, and can have a user interface. These are usually small apps started when the user logs in, and continue to run until they log out or quit them. These are often used to launch other helpers automatically, but may simply provide convenient access to app features, such as through a menu bar tool.
  • XPC services, managed by launchd, run in a user context, and normally don’t have any user interface. XPC is cross-process communication, and widely used by these processes and services.
  • Launch Daemons, managed by launchd, run in a system context, and can’t have any user interface. These are standalone background processes running as root before the user logs in, and normally communicate indirectly with user processes by XPC.
  • Launch Agents, managed by launchd, run in a user context, and shouldn’t have any user interface. These are normally run on the user’s behalf, and communicate with other processes and daemons.

These are associated with the various LaunchDaemons and LaunchAgents folders. Because launch daemons run in a system context, there’s no folder for them in the Home Library, which can only concern itself with agents run in that user’s context. Third-party apps developed for Ventura that have helpers and property lists should now follow its new scheme, where those are supplied in the app bundle’s Resources and Library folders, allowing Ventura to manage them through System Settings.

Property lists controlling macOS services are complex, and may be interwoven between daemons and agents. For example, scans performed by XProtect ‘Remediator’ have both agent and daemon property lists, located respectively in LaunchAgents and LaunchDaemons folders. Time Machine’s backupd is simpler in having only two property lists, both in LaunchDaemons, as an XPC system service.

Scheduling backups

The launch daemon configuration file com.apple.backupd-helper.plist schedules an XPC service com.apple.backupd-auto with the following key-value pairs for its background activity:

  • Interval: 3600 seconds = 1 hour, the average interval between repeats,
  • Delay: 3600 seconds = 1 hour, the time period before starting,
  • GracePeriod: 1800 seconds = 30 minutes, the time period to allow before scheduling becomes more aggressive,
  • Repeating: true
  • AllowBattery: true allowing backups when running on battery,
  • PowerNap: true
  • Priority: Utility setting the Quality of Service (QoS).

During startup, these property lists and the dictionaries they contain are assembled into a list of activities for Duet Activity Scheduler, DAS. Those for most if not all macOS background activities are put in the group com.apple.dasd.default. Entries contain the contents of the dictionary for each activity for DAS to use when rescoring activities to determine which should proceed, and which should not.

Should it proceed?

Once loaded up, DAS rescores all activities every couple of minutes or so. For activities still to be run within their time window, it records its decision in the log.

In this case, this mediaanalysisd activity fails to score highly enough, and DAS decides not to run it at this time.
501:com.apple.mediaanalysisd.filesystem:2CAABE:[
{name: DeviceActivityPolicy, policyWeight: 20.000, response: {Decision: Must Not Proceed, Score: 0.00, Rationale: [{deviceActivity == 1}]}}
{name: ThermalPolicy, policyWeight: 5.000, response: {Decision: Absolutely Must Not Proceed, Score: 0.00, Rationale: [{thermalLevel > 0}]}}
], FinalDecision: Absolutely Must Not Proceed}

When it’s time to run a backup, DAS records how it arrives at that decision.
0:com.apple.backupd-auto:CC9325:[
{name: DeviceActivityPolicy, policyWeight: 2.000, response: {Decision: Can Proceed, Score: 0.40}}
{name: ThermalPolicy, policyWeight: 5.000, response: {Decision: Can Proceed, Score: 0.20, Rationale: }}
] sumScores:25.320000, denominator:30.520000, FinalDecision: Can Proceed FinalScore: 0.829620}
'0:com.apple.backupd-auto:CC9325' CurrentScore: 0.829620, ThresholdScore: 0.088099 DecisionToRun:1
With <private> ...Tasks pre-running in group [com.apple.dasd.default] are 1!

Scoring has become complex, and isn’t documented. Most or all scoring requires a ThermalPolicy, many require a DeviceActivityPolicy too, and you’ll also come across others such as PowerNapPolicy. DAS appears to make a separate decision on each policy, then total weighted scores and divide by a denominator to arrive at a final floating point score, which must exceed a set threshold before it can make the decision to run the activity.

Running the activity

Once DAS has decided that an activity can proceed, it makes a request to Centralized Task Scheduling (CTS) to run the activity.
Running activities : <private>
REQUESTING START: 0:com.apple.backupd-auto:CC9325
Setting timer (isWaking=1, activityRequiresWaking=0) between <private> and <private> for <private>

CTS then responds, and writes a series of log entries, starting with
Initiating XPC Activity: com.apple.backupd-auto (0x7f7882b0db70)

DAS then records more details.
STARTING: <private>
STARTING activity 0:com.apple.backupd-auto:CC9325 <private>!
Activity <private> has preventDeviceSleep 0. PluggedIn state: 1
With <private> ...Tasks running in group [com.apple.dasd.default] are 5!
Started tracking activity <private> for <private>
Activity 0:com.apple.backupd-auto:CC9325 has been running for 4.986921946207683e-07 minutes
Current load for group com.apple.dasd.default is 1.000000. Long running activities are <private>
0:com.apple.backupd-auto:CC9325:[
{name: DeviceActivityPolicy, policyWeight: 2.000, response: {Decision: Can Proceed, Score: 0.40}}
{name: ThermalPolicy, policyWeight: 5.000, response: {Decision: Can Proceed, Score: 0.20, Rationale: }}
] sumScores:25.320000, denominator:30.520000, FinalDecision: Can Proceed FinalScore: 0.829620}

Scheduling the next run

Once the activity has completed, DAS records that.
COMPLETED 0:com.apple.backupd-auto:CC9325 at priority 30 <private>!
Stopped tracking activity <private> for <private>
NO LONGER RUNNING 0:com.apple.backupd-auto:CC9325 ...Tasks running in group [com.apple.dasd.default] are 4!

CTS requests that the next run should be scheduled by DAS.
Rescheduling XPC Activity: com.apple.backupd-auto (0x7f7882b0db70)

To which DAS responds:
SUBMITTING: 0:com.apple.backupd-auto:8481FE

The XPC activity state is set by CTS.
_xpc_activity_set_state_from_cts: com.apple.backupd-auto (0x7fd88e5040c0), set activity state to 1
_xpc_activity_end_running: com.apple.backupd-auto (0x7fd88e5040c0) seqno: 0.

Finally, DAS adds that next backup to its schedule, giving its intended time window.
Submitted Activity: 0:com.apple.backupd-auto:8481FE at priority 30 with interval 3600 (Sat Jan 21 22:28:37 2023 - Sat Jan 21 22:28:37 2023)
Activity <private>: Optimal Score 0.7889 at <private> (Valid Until: <private>)

Tool

The easiest way to watch this all happening is using the DAS Scheduling log window in my free utility Mints.