How to handle idle or hung background tasks

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Idle background tasks can cause unnecessary app termination. Handle idle (or hung) background tasks by handling cancellation and completely exiting the background task.

Background tasks are meant to be short lived tasks that complete their work quickly and return from the task. This behavior is required due to strict CPU and network resource constraints on background tasks. Background tasks are the only supported way to run code in the background when the app is suspended and during Connected Standby; resource usage constraints are in place to limit the impact background tasks can have on battery life.

To help enforce these constraints, the background task infrastructure detects background tasks that have become idle or that have stopped responding. If a background task has not utilized a minimum amount of CPU or network resource quota within a minimum amount of time then the background task is considered idle or hung. The minimum amount of time varies depending on the state of the system, for example whether it's in Connected Standby. When an idle or hung background task is detected, the background task is sent a cancellation notification - giving it the opportunity to exit cleanly. If the background task does not respond to the cancel notification by exiting completely within 5 seconds then the app is considered unresponsive and the system terminates it. (Note that this will generate a WER crash dump report, which is available on your Windows Store developer account.)

Background tasks typically become idle or hung are when they are waiting for an asynchronous operation to return in the Run() method. For example waiting for a response on a network socket, or a long file copy operation when the disk happens to be unresponsive. Follow these steps to cleanly handle background task cancellation even while awaiting async operations.

What you need to know

Technologies

Prerequisites

  • This topic applies to apps with background tasks.

Instructions

Step 1: Register a cancellation handler

Always register a cancellation handler for your background task. If the system detects the background task as idle or hung it calls the cancellation handler. Your background task must have a cancellation handler, and the cancellation handler must cause the task to exit completely within 5 seconds - or else your app gets terminated.

Step 2: Cancel async operations

Some tasks appear idle when they are awaiting asynchronous operations created in the Run() method. A simple way to cancel all pending async operations is to associate a single CancellationTokenSource with all your async operations. Then from your cancellation handler, simply call cancel on the CancellationTokenSource to cancel all pending async operations.

Step 3: Complete the background task deferral

In C#/C++/VB, the background task must always call Complete() on the background task deferral object after it has completed its work so that the task can be shut down. Without calling Complete() on the deferral the background task won't exit, the background task infrastructure will detect when it becomes idle, and it will send the task a cancellation notification.

Remarks

Best practices for idle or hung background tasks

Use the following best practices for idle or hung background tasks.

  1. Always associate a cancellation handler with your background tasks.
  2. In your cancellation handler, cancel all asynchronous operations and return within 5 seconds or your app will be terminated.
  3. Always call Complete() on your background task deferral object just before your C#/C++/VB background task exits.

Example code

The following background task registers a cancellation handler, cancels async operations, and calls Complete().

public sealed class ExampleClass:IBackgroundTask
{
    CancellationTokenSource cancelTokenSource = new CancellationTokenSource();

    async void Run(IBackgroundTaskInstance taskInstance)
    {
        BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
        HostName server = new HostName("contoso.com");
        StreamSocket sock = new StreamSocket();
        
        // Skipping other extraneous code.
        
        // Associate a cancellation handler with the background task.
        taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);

        try
        {    
            await sock.ConnectAsync(server, "http").AsTask(cancelTokenSource.Token);
            await sock.InputStream.ReadAsync(…).AsTask(cancelTokenSource.Token);                   
        }
        finally { deferral.Complete(); }
    }
    

    private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
        // The background task has been detected as idle or hung.
        // Cancel all pending async operations and return from the task.
        cancelTokenSource.Cancel(); 
    }
}

How to handle a cancelled background task