Difference between revisions of "macOS App Nap"

From Lazarus wiki
Jump to navigationJump to search
(→‎External Links: Add entry)
m (Remove in progress warning)
Line 1: Line 1:
 
{{macOS_App_Nap}}
 
{{macOS_App_Nap}}
 
{{Warning| Work in progress ...}}
 
  
 
== Overview ==
 
== Overview ==

Revision as of 12:37, 13 February 2021

macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

English (en)

Overview

OS X Mavericks 10.9 introduced substantial power-saving features under the App Nap umbrella, especially for laptops. App Nap is based on several key principles:

  • The features work without the developer needing to modify existing applications.
  • The features keep the hardware as idle as possible given the demand for resources.
  • When a Mac is on battery power, only the work the user requests or that is absolutely essential is done.

App Nap is activated by the operating system when:

  • The application's windows are not visible; eg minimised to the dock or hidden by another application's window(s).
  • The application has not updated any visible part of an open window for some time.
  • The application is not playing audio.
  • The application is not using OpenGL graphics.
  • The application is in the background.
  • The application has not informed macOS that it is still active via IOKit power management or NSProcessInfo assertions.

The App Nap power-saving measures include:

  • Timer throttling: The frequency with which an application's timers are executed is reduced, increasing CPU idle time when running applications that, for example, check for data.
  • I/O throttling: Disk and network activity is assigned the lowest priority for applications that are "napping" thereby reducing the speed at which the application can read/write data from/to a device. This also reduces the likelihood that a napping application will impact an application which is actively being used.
  • Priority reduction: The UNIX process priority oil an application is reduced so that it receives less available CPU time.

Timer coalescing

Timer coalescing, although not an App Nap feature, was introduced in Mavericks at the same time. To maximise the amount of time that the CPU spends at idle, timer coalescing shifts the execution of timers by a small amount so that timers of multiple applications are executed at the same time. This is done by applying a time window to every timer based on the importance of the process. A timer can be executed at any time during this window, so may be shifted backward or forward a small amount so that it lines up with other timers that need to be executed at similar times. The timer windows are:

Process type Timer window
Application (default) 1ms
System daemon 70-90ms
Background process 80-120ms
Critical/Real time process 0ms

The NSTimer class in Mavericks introduced a new tolerance parameter which enables developers to tune how timely timer-event driven events need to be.

Managing App Nap

App Nap introduced a new API in NSProcessInfo which gives developers the ability to inform the operating system when an application is performing a long-running operation that may need to prevent App Nap or system sleep.

NSProcessInfoActivity = objccategory external (NSProcessInfo)
    function beginActivityWithOptions_reason (options: NSActivityOptions; reason: NSString): NSObjectProtocol; message 'beginActivityWithOptions:reason:';
    procedure endActivity (activity: NSObjectProtocol); message 'endActivity:';
    procedure performActivityWithOptions_reason_usingBlock (options: NSActivityOptions; reason: NSString; block: OpaqueCBlock); message 'performActivityWithOptions:reason:usingBlock:';

Be aware that failing to execute endActivity after a beginActivityWithOptions for an extended period of time can have significant negative impacts on the performance of your user's computer, so only use the minimum amount of time required.

Automatic termination

Automatic termination is a feature introduced in macOS 10.7 (Lion) that you must explicitly code for in your application. If it is enabled, then your application needs to save the current state of its user interface so that it can be restored later . The system can kill the underlying process for an auto-terminable application at any time, so saving this information maintains application continuity. The system usually kills an application’s underlying process some time after the user has closed all of its windows. However, the system may also kill an application with open windows if it is not currently visible on the screen.

To support automatic termination, you need to:

  • Declare support for automatic termination either programmatically or setting the NSSupportsAutomaticTermination key value to YES in the application's Info.plist file.
  • Support saving and restoring the application's window configurations.
  • Save the user’s data at appropriate times.
    • Single-window, library-style applications should implement strategies for saving data at appropriate checkpoints.
    • Multiwindow, document-based applications can use the autosaving and saveless documents capabilities in NSDocument.

You can also use the App Nap API to control automatic termination.

function beginActivityWithOptions_reason (options: NSActivityOptions; reason: NSString): NSObjectProtocol; message 'beginActivityWithOptions:reason:';
// Perform some task
procedure endActivity (activity: NSObjectProtocol); message 'endActivity:';

is equivalent to:

procedure disbleAutomaticTermination (reason: NSString);
// Perform some task
procedure enableAutomaticTermination (reason: NSString);

NSActivityOptions

Option Effect
NSActivityIdleDisplaySleepDisabled require the screen to stay powered on
NSActivityIdleSystemSleepDisabled prevent idle sleep
NSActivitySuddenTerminationDisabled prevent sudden termination
NSActivityAutomaticTerminationDisabled prevent automatic termination
NSActivityUserInitiated indicate the application is performing a user-requested action
NSActivityUserInitiatedAllowingIdleSystemSleep indicate the application is performing a user-requested action, but that the system can sleep on idle
NSActivityBackground indicate the application has initiated some kind of work, but not as the direct result of user request
NSActivityLatencyCritical indicate the activity requires the highest amount of timer and I/O precision available

Recommended usage

NSActivityUserInitiated: Used for finite length activities that the user has explicitly started. Examples include exporting or downloading a user specified file.

NSActivityBackground: Used for finite length activities that are part of the normal operation of your application but are not explicitly started by the user. Examples include autosaving, indexing, and automatic downloading of files.

NSActivityLatencyCritical: If your application requires high priority I/O, you can include the flag (using a bitwise OR). You should only use this flag for activities like audio or video recording that really do require high priority. You should only use this flag for activities like audio or video recording that really do require high priority.

Example code

unit Unit1;

{$mode objfpc}{$H+}
{$modeswitch objectivec1}

interface

uses
  Classes, SysUtils, Forms, Dialogs, StdCtrls, ExtCtrls, CocoaAll;

{ NSProcessInfo }

type
  NSProcessInfoActivity = objccategory external (NSProcessInfo)
    function beginActivityWithOptions_reason (options: NSActivityOptions; reason: NSString): NSObjectProtocol; message 'beginActivityWithOptions:reason:'; { available in 10_9, 7_0 }
    procedure endActivity (activity: NSObjectProtocol); message 'endActivity:'; { available in 10_9, 7_0 }
    procedure performActivityWithOptions_reason_usingBlock (options: NSActivityOptions; reason: NSString; block: OpaqueCBlock); message 'performActivityWithOptions:reason:usingBlock:'; { available in 10_9, 7_0 }
  end;

  { TForm1 }

  TForm1 = Class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;
  myActivityToken: NSObjectProtocol;

implementation

{$R *.lfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Memo1.Append('beginActivity');

  myActivityToken := NSProcessInfo.processInfo.beginActivityWithOptions_reason((NSActivityIdleSystemSleepDisabled or NSActivityUserInitiated or NSActivityAutomaticTerminationDisabled), NSSTR('No napping!'));

  if(myActivityToken = Nil) then
    begin
      Form1.Memo1.Append('No Token!');
    end;

  myActivityToken.retain;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Form1.Memo1.Append('endActivity!');

  if (myActivityToken <> Nil) then
    begin
      NSProcessinfo.processinfo.endActivity(myActivityToken);
      myActivityToken.release;
      myActivityToken := Nil;
    end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Close;
end;

end.

App Nap status

There are two ways of checking the App Nap status of your application.

Method 1: Activity Monitor

Open Applications > Utilities > Activity Monitor which shows the status of App Nap for all running applications as well as other useful information like the energy impact of each application on the system:

Activity Monitor app Nap.jpg

Method 2: pmset

Open an Applications > Utilities > Terminal and type the command:

pmset -g assertionslog

which will produce the following output when you run the example code above and click the BeginActivity and EndActivity buttons.

Time             Action      Type                          PID(Causing PID)    ID                  Name                                              
====             ======      ====                          ================    ==                  ====                                              
02/13 16:35:19   Created     PreventUserIdleSystemSleep    1188                0x40e40001880f      No napping!                                       
02/13 16:35:19   System wide status: PreventUserIdleSystemSleep: 1  
02/13 16:35:29   Released    PreventUserIdleSystemSleep    1188                0x40e40001880f      No napping!                                       
02/13 16:35:29   System wide status: PreventUserIdleSystemSleep: 0  


See also

External Links