4.2 Thread Cancellation

Under normal circumstances, a thread terminates when it exits normally, either by returning from its thread function or by calling pthread_exit. However, it is possible for a thread to request that another thread terminate. This is called canceling a thread.

To cancel a thread, call pthread_cancel, passing the thread ID of the thread to be canceled. A canceled thread may later be joined; in fact, you should join a canceled thread to free up its resources, unless the thread is detached (see Section 4.1.5, "Thread Attributes"). The return value of a canceled thread is the special value given by PTHREAD_CANCELED.

Often a thread may be in some code that must be executed in an all-or-nothing fashion. For instance, the thread may allocate some resources, use them, and then deallocate them. If the thread is canceled in the middle of this code, it may not have the opportunity to deallocate the resources, and thus the resources will be leaked. To counter this possibility, it is possible for a thread to control whether and when it can be canceled.

A thread may be in one of three states with regard to thread cancellation.

·         The thread may be asynchronously cancelable. The thread may be canceled at any point in its execution.

·         The thread may be synchronously cancelable. The thread may be canceled, but not at just any point in its execution. Instead, cancellation requests are queued, and the thread is canceled only when it reaches specific points in its execution.

·         A thread may be uncancelable. Attempts to cancel the thread are quietly ignored.

When initially created, a thread is synchronously cancelable.

4.2.1 Synchronous and Asynchronous Threads

An asynchronously cancelable thread may be canceled at any point in its execution. A synchronously cancelable thread, in contrast, may be canceled only at particular places in its execution. These places are called cancellation points. The thread will queue a cancellation request until it reaches the next cancellation point.

To make a thread asynchronously cancelable, use pthread_setcanceltype. This affects the thread that actually calls the function. The first argument should be PTHREAD_CANCEL_ASYNCHRONOUS to make the thread asynchronously cancelable, or PTHREAD_CANCEL_DEFERRED to return it to the synchronously cancelable state. The second argument, if not null, is a pointer to a variable that will receive the previous cancellation type for the thread. This call, for example, makes the calling thread asynchronously cancelable.

 
pthread_setcanceltype  (PTHREAD_CANCEL_ASYNCHRONOUS,  NULL); 

What constitutes a cancellation point, and where should these be placed? The most direct way to create a cancellation point is to call pthread_testcancel. This does nothing except process a pending cancellation in a synchronously cancelable thread. You should call pthread_testcancel periodically during lengthy computations in a thread function, at points where the thread can be canceled without leaking any resources or producing other ill effects.

Certain other functions are implicitly cancellation points as well. These are listed on the pthread_cancel man page. Note that other functions may use these functions internally and thus will indirectly be cancellation points.

4.2.2 Uncancelable Critical Sections

A thread may disable cancellation of itself altogether with the pthread_setcancelstate function. Like pthread_setcanceltype, this affects the calling thread. The first argument is PTHREAD_CANCEL_DISABLE to disable cancellation, or PTHREAD_CANCEL_ENABLE to re-enable cancellation. The second argument, if not null, points to a variable that will receive the previous cancellation state. This call, for instance, disables thread cancellation in the calling thread.

 
pthread_setcancelstate  (PTHREAD_CANCEL_DISABLE,  NULL); 

Using pthread_setcancelstate enables you to implement critical sections. A critical section is a sequence of code that must be executed either in its entirety or not at all; in other words, if a thread begins executing the critical section, it must continue until the end of the critical section without being canceled.

For example, suppose that you're writing a routine for a banking program that transfers money from one account to another. To do this, you must add value to the balance in one account and deduct the same value from the balance of another account. If the thread running your routine happened to be canceled at just the wrong time between these two operations, the program would have spuriously increased the bank's total deposits by failing to complete the transaction. To prevent this possibility, place the two operations in a critical section.

You might implement the transfer with a function such as process_transaction, shown in Listing 4.6. This function disables thread cancellation to start a critical section before it modifies either account balance.

Listing 4.6 (critical-section.c) Protect a Bank Transaction with a Critical Section
#include   <pthread.h> 
#include   <stdio.h> 
#include   <string.h> 
 
/*  An  array  of  balances  in  accounts,  indexed  by  account  number.  */ 
 
float*  account_balances; 
 
/*  Transfer  DOLLARS  from  account  FROM_ACCT  to  account  TO_ACCT.  Return 
    0  if  the  transaction  succeeded,  or  1  if  the  balance  FROM_ACCT  is 
    too  small.  */ 
 
int  process_transaction  (int  from_acct,  int to_acct,  float dollars) 
{
  int  old_cancel_state; 
 
  /*  Check  the  balance  in  FROM_ACCT.  */ 
  if  (account_balances[from_acct]  <<  dollars) 
    return  1; 
   /* Begin  critical  section.  */ 
   pthread_setcancelstate  (PTHREAD_CANCEL_DISABLE, &old_cancel_state); 
   /* Move  the  money.  */ 
   account_balances[to_acct]  +=  dollars; 
   account_balances[from_acct]  -=  dollars; 
   /* End  critical  section.  */ 
   pthread_setcancelstate (old_cancel_state,  NULL); 
 
   return 0; 
} 

Note that it's important to restore the old cancel state at the end of the critical section rather than setting it unconditionally to PTHREAD_CANCEL_ENABLE. This enables you to call the process_transaction function safely from within another critical section—in that case, your function will leave the cancel state the same way it found it.

4.2.3 When to Use Thread Cancellation

In general, it's a good idea not to use thread cancellation to end the execution of a thread, except in unusual circumstances. During normal operation, a better strategy is to indicate to the thread that it should exit, and then to wait for the thread to exit on its own in an orderly fashion. We'll discuss techniques for communicating with the thread later in this chapter, and in Chapter 5, "Interprocess Communication."