Intro to Backgrounding: Running Finite-Length Tasks

Duration

15 minutes

Goals

The goal of this lab will be to cancel an operation that is executed as a Finite-Length Task, in case the available background time expires. The application we use to demonstrate this will calculate decimals of Pi and looks like this:

This application will begin calculating decimals of Pi when you press the Start Calculating button. It will then continue to run when you background the application because the calculation is wrapped in a Finite-Length Task - just as you created in the first exercise.

Since there are an infinite number of digits in Pi, the application will continue to execute in the background until the maximum available background time has been reached and iOS terminates the application.

Your goal is to fix this behavior by adding cancellation support to the logic using a CancellationToken.

What is a cancellation token?

You will introduce two new behaviors into the application with this support:

  1. Allow the user to stop the ongoing calculation by pressing the button once more.
  2. Prevent the application from being terminated if it runs out of background time.

Required assets

The provided Exercise 2 folder contains a sub-folder named Lab.Start with a solution you can should as a starting point. There is also a Lab.Completed folder with the completed code if you want to compare your solution.

Attention:This exercise will not work correctly on the iOS Simulators. Currently applications running on the simulators are not suspended when running in the background. You can still complete this lab on simulator but the behavior will not match a real device. For best results use a physical iOS device.

Exercise Overview

Use the following high-level steps to complete the exercise. There are step-by-step directions below that which you can refer to if you need a hint or some additional guidance.

  1. To allow cancellation of a System.Threading.Tasks.Task, you can use a CancellationTokenSource into the main view controller.
  2. Inside HandleCalculatePi method, a Task is started that runs the calculation independently of the UI thread. To ensure that the calculation continues when the application is backgrounded, it is wrapped into a Finite-Length Task using the same technique you used in the first exercise.
  3. Both, the Task and the call to PiHelper.CalcPi() accept a CancellationToken as parameters which you can get from the CancellationTokenSource.
  4. If new decimals have been calculated, the callback UpdateUi() will be triggered. In there, add a check if cancellation of the operation has been requested.
  5. With the checks in place you will need to add code to cancel the task. This can happen if the use taps the "Stop calculating" button or if the app is backgrounded and runs out of background time.

Tip: If you are doing this exercise live in a session, make sure to make good use of the instructor, they are online to answer any questions you have!

Steps

Below are the step-by-step instructions to implement the exercise.

Add a CancellationTokenSource

  1. Open the CancellableTaskViewController.cs source file.
  2. Add a new private CancellationTokenSource field into the class and assign it to a new object.

Show code

Pass the token around

The CancellationTokenSource is responsible for creating the CancellationToken which we will use to cancel a specific operation - in this case our Pi calculation.

  1. Use the Token property of the CancellationTokenSource to retrieve the CancellationToken.
  2. Pass the CancellationToken to the PiHelper.CalcPimethod - there is a placeholder being passed in currently. PiHelper is already coded to regularly check if cancellation was requested and stop the calculation.
  3. You should also pass the token into the Task.Run call which creates the task. This is an optimization: if the token is canceled before the task is started, then the runtime will not schedule the Task and it will be reported as canceled.

Show code

Check if cancellation was requested

You might have noticed that we have a try/catch wrapper around the Task.Run call. This is used to catch an OperationCancelledException. This is the exception reported to our code when a task is canceled.

Each time PiHelper.Calc() has calculated new digits, it will trigger a callback to update the UI - this was the first parameter we passed into the method (UpdateUi). Since this method is called periodically by the background thread, we can use it to cancel the operation when cancellation is requested.

We can do this in two different ways.

  1. Calling token.ThrowIfCancellationRequested will throw an exception if the token has been signaled and the operation is supposed to be canceled.
  2. Alternatively, you can throw a OperationCancelledException - this is what the method does under the covers. This is a special exception that is caught by the Task Parallel Framework. In response, it will mark the task as canceled and re-throw the exception when the task result is consumed (e.g. await or Task.Result).

Perform the following steps.

  1. Locate the UpdateUi method in the view controller.
  2. Use the CancellationToken from your cancellation token source and call the ThrowIfCancellationRequested method at the top of the UpdateUi method. This will check the token and throw an exception if it needs to be canceled.

Show code

User-initiated cancellation

You will need to cancel the task if the user presses the button once more or if the background time is about to expire.

  1. Start by calling Cancel on the CancellationTokenSource when the "Stop Calculating" button is pressed. The handler for this is the HandleCalculatePi() method.
  2. Because the token could be canceled, we need to reset it each time we start the Pi calculator. Add a call to re-create the TaskCancellationSource into the HandleCalculatePi method when you are starting a new calculation.
  3. Run the application and try out your new logic to cancel the task by pressing the button.

Show code

System-initiated cancellation

The second situation that will lead to cancellation is when the available background time expires. This will invoke an expiration handler which is passed into the iOS UIApplication.BeginBackgroundTask method.

  1. Locate the HandleBackgroundTimeExpires method - this is the callback passed to the UIApplication.BeginBackgroundTask method. It will be called when iOS has expired the background operation.
  2. Add a call to cancel the task through the CancellationTokenSource. This should end our operation, and as a result, end the iOS background task.
  3. If you have time, run the application and let it timeout. It should cancel automatically and no longer terminate the application.

Show code

Summary

In this exercise you have wrapped an important operation into a Finite-Length Task and added cancellation support.

Go Back