Terminating threads
A thread automatically terminates when it returns from its entry-point routine.
A thread can also explicitly terminate itself or terminate any other thread in the process, using a mechanism called cancelation. Because all threads share the same data space, a thread must perform cleanup operations at termination time; the threads library provides cleanup handlers for this purpose.
Exiting a thread
A process can exit at any time when a thread calls the exit subroutine. Similarly, a thread can exit at any time by calling the pthread_exit subroutine.
Calling the exit subroutine terminates the entire process, including all its threads. In a multithreaded program, the exit subroutine should only be used when the entire process needs to be terminated; for example, in the case of an unrecoverable error. The pthread_exit subroutine should be preferred, even for exiting the initial thread.
Calling the pthread_exit subroutine terminates the calling thread. The status parameter is saved by the library and can be further used when joining the terminated thread. Calling the pthread_exit subroutine is similar, but not identical, to returning from the thread's initial routine. The result of returning from the thread's initial routine depends on the thread:
- Returning from the initial thread implicitly calls the exit subroutine, thus terminating all the threads in the process.
- Returning from another thread implicitly calls the pthread_exit subroutine. The return value has the same role as the status parameter of the pthread_exit subroutine.
To avoid implicitly calling the exit subroutine, to use the pthread_exit subroutine to exit a thread.
Exiting the initial thread (for example, by calling the pthread_exit subroutine from the main routine) does not terminate the process. It terminates only the initial thread. If the initial thread is terminated, the process will be terminated when the last thread in it terminates. In this case, the process return code is 0.
The following program displays exactly 10 messages in each language. This is accomplished by calling the pthread_exit subroutine in the main routine after creating the two threads, and creating a loop in the Thread routine.
#include <pthread.h> /* include file for pthreads - the 1st */
#include <stdio.h> /* include file for printf() */
void *Thread(void *string)
{
int i;
for (i=0; i<10; i++)
printf("%s\n", (char *)string);
pthread_exit(NULL);
}
int main()
{
char *e_str = "Hello!";
char *f_str = "Bonjour !";
pthread_t e_th;
pthread_t f_th;
int rc;
rc = pthread_create(&e_th, NULL, Thread, (void *)e_str);
if (rc)
exit(-1);
rc = pthread_create(&f_th, NULL, Thread, (void *)f_str);
if (rc)
exit(-1);
pthread_exit(NULL);
}
The pthread_exit subroutine releases any thread-specific data, including the thread's stack. Any data allocated on the stack becomes invalid, because the stack is freed and the corresponding memory may be reused by another thread. Therefore, thread synchronization objects (mutexes and condition variables) allocated on a thread's stack must be destroyed before the thread calls the pthread_exit subroutine.
Unlike the exit subroutine, the pthread_exit subroutine does not clean up system resources shared among threads. For example, files are not closed by the pthread_exit subroutine, because they may be used by other threads.
Canceling a thread
The thread cancelation mechanism allows a thread to terminate the execution of any other thread in the process in a controlled manner. The target thread (that is, the one that is being canceled) can hold cancelation requests pending in a number of ways and perform application-specific cleanup processing when the notice of cancelation is acted upon. When canceled, the thread implicitly calls the pthread_exit((void *)-1) subroutine.
The cancelation of a thread is requested by calling the pthread_cancel subroutine. When the call returns, the request has been registered, but the thread may still be running. The call to the pthread_cancel subroutine is unsuccessful only when the specified thread ID is not valid.
Cancelability state and type
The cancelability state and type of a thread determines the action taken upon receipt of a cancelation request. Each thread controls its own cancelability state and type with the pthread_setcancelstate and pthread_setcanceltype subroutines.
Cancelability State | Cancelability Type | Resulting Case |
---|---|---|
Disabled | Any (the type is ignored) | Disabled cancelability |
Enabled | Deferred | Deferred cancelability |
Enabled | Asynchronous | Asynchronous cancelability |
- Disabled cancelability. Any cancelation request is set
pending, until the cancelability state is changed or the thread is
terminated in another way.
A thread should disable cancelability only when performing operations that cannot be interrupted. For example, if a thread is performing some complex file-save operations (such as an indexed database) and is canceled during the operation, the files may be left in an inconsistent state. To avoid this, the thread should disable cancelability during the file save operations.
- Deferred cancelability. Any cancelation request is set
pending, until the thread reaches the next cancelation point. It is
the default cancelability state.
This cancelability state ensures that a thread can be cancelled, but limits the cancelation to specific moments in the thread's execution, called cancelation points. A thread canceled on a cancelation point leaves the system in a safe state; however, user data may be inconsistent or locks may be held by the canceled thread. To avoid these situations, use cleanup handlers or disable cancelability within critical regions. For more information, see Using Cleanup Handlers .
- Asynchronous cancelability. Any cancelation request is
acted upon immediately.
A thread that is asynchronously canceled while holding resources may leave the process, or even the system, in a state from which it is difficult or impossible to recover. For more information about async-cancel safety, see Async-Cancel Safety.
Async-cancel safety
A function is said to be async-cancel safe if it is written so that calling the function with asynchronous cancelability enabled does not cause any resource to be corrupted, even if a cancelation request is delivered at any arbitrary instruction.
Any function that gets a resource as a side effect cannot be made async-cancel safe. For example, if the malloc subroutine is called with asynchronous cancelability enabled, it might acquire the resource successfully, but as it was returning to the caller, it could act on a cancelation request. In such a case, the program would have no way of knowing whether the resource was acquired or not.
For this reason, most library routines cannot be considered async-cancel safe. It is recommended that you use asynchronous cancelability only if you are sure only to perform operations that do not hold resources and only to call library routines that are async-cancel safe.
The following subroutines are async-cancel safe; they ensure that cancelation will be handled correctly, even if asynchronous cancelability is enabled:
- pthread_cancel
- pthread_setcancelstate
- pthread_setcanceltype
An alternative to asynchronous cancelability is to use deferred cancelability and to add explicit cancelation points by calling the pthread_testcancel subroutine.
Cancelation points
Cancelation points are points inside of certain subroutines where a thread must act on any pending cancelation request if deferred cancelability is enabled. All of these subroutines may block the calling thread or compute indefinitely.
An explicit cancelation point can also be created by calling the pthread_testcancel subroutine. This subroutine simply creates a cancelation point. If deferred cancelability is enabled, and if a cancelation request is pending, the request is acted upon and the thread is terminated. Otherwise, the subroutine simply returns.
Other cancelation points occur when calling the following subroutines:
- pthread_cond_wait
- pthread_cond_timedwait
- pthread_join
The pthread_mutex_lock and pthread_mutex_trylock subroutines do not provide a cancelation point. If they did, all functions calling these subroutines (and many functions do) would provide a cancelation point. Having too many cancelation points makes programming very difficult, requiring either lots of disabling and restoring of cancelability or extra effort in trying to arrange for reliable cleanup at every possible place. For more information about these subroutines, see Using Mutexes.
Cancelation points occur when a thread is executing the following functions:
Function | |
---|---|
aio_suspend | close |
creat | fcntl |
fsync | getmsg |
getpmsg | lockf |
mq_receive | mq_send |
msgrcv | msgsnd |
msync | nanosleep |
open | pause |
poll | pread |
pthread_cond_timedwait | pthread_cond_wait |
pthread_join | pthread_testcancel |
putpmsg | pwrite |
read | readv |
select | sem_wait |
sigpause | sigsuspend |
sigtimedwait | sigwait |
sigwaitinfo | sleep |
system | tcdrain |
usleep | wait |
wait3 | waitid |
waitpid | write |
writev |
A cancelation point can also occur when a thread is executing the following functions:
Function Function Function | ||
---|---|---|
catclose | catgets | catopen |
closedir | closelog | ctermid |
dbm_close | dbm_delete | dbm_fetch |
dbm_nextkey | dbm_open | dbm_store |
dlclose | dlopen | endgrent |
endpwent | fwprintf | fwrite |
fwscanf | getc | getc_unlocked |
getchar | getchar_unlocked | getcwd |
getdate | getgrent | getgrgid |
getgrgid_r | getgrnam | getgrnam_r |
getlogin | getlogin_r | popen |
printf | putc | putc_unlocked |
putchar | putchar_unlocked | puts |
pututxline | putw | putwc |
putwchar | readdir | readdir_r |
remove | rename | rewind |
endutxent | fclose | fcntl |
fflush | fgetc | fgetpos |
fgets | fgetwc | fgetws |
fopen | fprintf | fputc |
fputs | getpwent | getpwnam |
getpwnam_r | getpwuid | getpwuid_r |
gets | getutxent | getutxid |
getutxline | getw | getwc |
getwchar | getwd | rewinddir |
scanf | seekdir | semop |
setgrent | setpwent | setutxent |
strerror | syslog | tmpfile |
tmpnam | ttyname | ttyname_r |
fputwc | fputws | fread |
freopen | fscanf | fseek |
fseeko | fsetpos | ftell |
ftello | ftw | glob |
iconv_close | iconv_open | ioctl |
lseek | mkstemp | nftw |
opendir | openlog | pclose |
perror | ungetc | ungetwc |
unlink | vfprintf | vfwprintf |
vprintf | vwprintf | wprintf |
wscanf |
The side effects of acting upon a cancelation request while suspended during a call of a function is the same as the side effects that may be seen in a single-threaded program when a call to a function is interrupted by a signal and the given function returns [EINTR]. Any such side effects occur before any cancelation cleanup handlers are called.
Whenever a thread has cancelability enabled and a cancelation request has been made with that thread as the target and the thread calls the pthread_testcancel subroutine, the cancelation request is acted upon before the pthread_testcancel subroutine returns. If a thread has cancelability enabled and the thread has an asynchronous cancelation request pending and the thread is suspended at a cancelation point waiting for an event to occur, the cancelation request will be acted upon. However, if the thread is suspended at a cancelation point and the event that it is waiting for occurs before the cancelation request is acted upon, the sequence of events determines whether the cancelation request is acted upon or whether the request remains pending and the thread resumes normal execution.
Cancelation example
In the following example, both "writer" threads are canceled after 10 seconds, and after they have written their message at least five times.
#include <pthread.h> /* include file for pthreads - the 1st */
#include <stdio.h> /* include file for printf() */
#include <unistd.h> /* include file for sleep() */
void *Thread(void *string)
{
int i;
int o_state;
/* disables cancelability */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &o_state);
/* writes five messages */
for (i=0; i<5; i++)
printf("%s\n", (char *)string);
/* restores cancelability */
pthread_setcancelstate(o_state, &o_state);
/* writes further */
while (1)
printf("%s\n", (char *)string);
pthread_exit(NULL);
}
int main()
{
char *e_str = "Hello!";
char *f_str = "Bonjour !";
pthread_t e_th;
pthread_t f_th;
int rc;
/* creates both threads */
rc = pthread_create(&e_th, NULL, Thread, (void *)e_str);
if (rc)
return -1;
rc = pthread_create(&f_th, NULL, Thread, (void *)f_str);
if (rc)
return -1;
/* sleeps a while */
sleep(10);
/* requests cancelation */
pthread_cancel(e_th);
pthread_cancel(f_th);
/* sleeps a bit more */
sleep(10);
pthread_exit(NULL);
}
Timer and sleep subroutines
Timer routines execute in the context of the calling thread. Thus, if a timer expires, the watchdog timer function is called in the thread's context. When a process or thread goes to sleep, it relinquishes the processor. In a multithreaded process, only the calling thread is put to sleep.
Using cleanup handlers
Cleanup handlers provide a portable mechanism for releasing resources and restoring invariants when a thread terminates.
Calling cleanup handlers
Cleanup handlers are specific to each thread. A thread can have several cleanup handlers; they are stored in a thread-specific LIFO (last-in, first-out) stack. Cleanup handlers are all called in the following cases:
- The thread returns from its entry-point routine.
- The thread calls the pthread_exit subroutine.
- The thread acts on a cancelation request.
A cleanup handler is pushed onto the cleanup stack by the pthread_cleanup_push subroutine. The pthread_cleanup_pop subroutine pops the topmost cleanup handler from the stack and optionally executes it. Use this subroutine when the cleanup handler is no longer needed.
The cleanup handler is a user-defined routine. It has one parameter, a void pointer, specified when calling the pthread_cleanup_push subroutine. You can specify a pointer to some data that the cleanup handler needs to perform its operation.
In the following example, a buffer is allocated for performing some operation. With deferred cancelability enabled, the operation can be stopped at any cancelation point. In that case, a cleanup handler is established to release the buffer.
/* the cleanup handler */
cleaner(void *buffer)
{
free(buffer);
}
/* fragment of another routine */
...
myBuf = malloc(1000);
if (myBuf != NULL) {
pthread_cleanup_push(cleaner, myBuf);
/*
* perform any operation using the buffer,
* including calls to other functions
* and cancelation points
*/
/* pops the handler and frees the buffer in one call */
pthread_cleanup_pop(1);
}
Using deferred cancelability ensures that the thread will not act on any cancelation request between the buffer allocation and the registration of the cleanup handler, because neither the malloc subroutine nor the pthread_cleanup_push subroutine provides any cancelation point. When popping the cleanup handler, the handler is executed, releasing the buffer. More complex programs may not execute the handler when popping it, because the cleanup handler should be thought of as an "emergency exit" for the protected portion of code.
Balancing the push and pop operations
The pthread_cleanup_push and pthread_cleanup_pop subroutines should always appear in pairs within the same lexical scope; that is, within the same function and the same statement block. They can be thought of as left and right parentheses enclosing a protected portion of code.
#define pthread_cleanup_push(rtm,arg) { \
/* other statements */
#define pthread_cleanup_pop(ex) \
/* other statements */ \
}
Adhere to the balancing rule for the pthread_cleanup_push and pthread_cleanup_pop subroutines to avoid compiler errors or unexpected behavior of your programs when porting to other systems.
In AIX®, the pthread_cleanup_push and pthread_cleanup_pop subroutines are library routines, and can be unbalanced within the same statement block. However, they must be balanced in the program, because the cleanup handlers are stacked.
Subroutine | Description |
---|---|
pthread_attr_destroy | Deletes a thread attributes object. |
pthread_attr_getdetachstate | Returns the value of the detachstate attribute of a thread attributes object. |
pthread_attr_init | Creates a thread attributes object and initializes it with default values. |
pthread_cancel | Requests the cancelation of a thread. |
pthread_cleanup_pop | Removes, and optionally executes, the routine at the top of the calling thread's cleanup stack. |
pthread_cleanup_push | Pushes a routine onto the calling thread's cleanup stack. |
pthread_create | Creates a new thread, initializes its attributes, and makes it runnable. |
pthread_equal | Compares two thread IDs. |
pthread_exit | Terminates the calling thread. |
pthread_self | Returns the calling thread's ID. |
pthread_setcancelstate | Sets the calling thread's cancelability state. |
pthread_setcanceltype | Sets the calling thread's cancelability type. |
pthread_testcancel | Creates a cancelation point in the calling thread. |