Overview: Two Dimensions of Control
When a thread receives a cancellation request, how it responds is controlled by two independent settings — state and type. Think of them as two gates the request must pass through.
Arrives
State
Type
Terminates
If state is DISABLED, the request stays pending — never reaches Gate 2. If state is ENABLED, the type decides timing: DEFERRED waits for a safe cancellation point; ASYNCHRONOUS allows cancellation anywhere.
32.2a — Cancellation State: pthread_setcancelstate()
The Two State Values
The oldstate Parameter — Saving & Restoring
pthread_setcancelstate() stores the previous state in *oldstate. This lets you temporarily disable cancellation and then restore it afterward:
int oldstate;
/* Disable cancellation — save old state */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
/* ... critical section: all steps MUST complete ... */
update_database();
write_log_entry();
flush_buffer();
/* Restore previous state (could be ENABLE or DISABLE) */
pthread_setcancelstate(oldstate, NULL);
NULL for oldstate if you don’t need the previous value. But SUSv3 does not guarantee this is supported on all systems. For portable code, always pass a valid int * variable even if you don’t use the result.32.2b — Cancellation Type: pthread_setcanceltype()
The Two Type Values
sleep(), read(), write(), etc. Safe and predictable.Like oldstate, always pass a non-NULL oldtype pointer for portable code even if you won’t use the returned value.
32.2c — Inheritance: fork() and exec()
What happens to a thread’s cancellation state and type when it calls fork() or exec()? The rules are clear:
| Syscall | Effect on Cancellation State | Effect on Cancellation Type |
|---|---|---|
| fork() | Child inherits the calling thread’s current state (ENABLE or DISABLE) | Child inherits the calling thread’s current type (DEFERRED or ASYNC) |
| exec() | Reset to default: PTHREAD_CANCEL_ENABLE | Reset to default: PTHREAD_CANCEL_DEFERRED |
exec(), a completely new program image is loaded. All previous thread state is irrelevant — the new program starts with a single fresh main thread with default cancellation settings. The old custom settings from the previous program have no meaning in the new program.Decision Matrix: State + Type Combinations
| State | Type | What Happens to Cancel Request? |
|---|---|---|
| DISABLE | Any | Request stays pending. Thread continues running. Will fire when state is re-enabled. |
| ENABLE | DEFERRED | Thread waits until it reaches a cancellation point (e.g., sleep, read, write). Then terminates. ← Default & recommended |
| ENABLE | ASYNCHRONOUS | Thread may be canceled at any instruction — even inside malloc() or mutex lock. Very dangerous. Rarely safe. |
Code Example 1: Temporarily Disabling Cancellation
A thread disables cancellation during a critical section (multi-step database update), then re-enables it.
/* cancel_disable.c - Protect a critical section from cancellation */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
static void *
workerThread(void *arg)
{
int oldstate;
printf("Thread: running, default state=ENABLE, type=DEFERRED\n");
/* Phase 1: Normal work (cancellation ENABLED) */
printf("Thread: doing normal work (cancellable)\n");
sleep(2); /* cancellation point — if cancel pending, fires here */
/* Phase 2: Critical section — DISABLE cancellation */
printf("Thread: entering critical section — disabling cancel\n");
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
/* These three steps MUST all complete together */
printf("Thread: step 1 — writing record\n");
sleep(1); /* sleep() here is NOT a cancellation point (cancel disabled) */
printf("Thread: step 2 — updating index\n");
sleep(1);
printf("Thread: step 3 — committing transaction\n");
sleep(1);
/* Phase 3: Restore previous state */
printf("Thread: critical section done — restoring cancel state\n");
pthread_setcancelstate(oldstate, NULL);
/* If a cancel was pending, it fires at the next cancellation point */
printf("Thread: back to normal work\n");
sleep(10); /* Any pending cancel request fires here */
printf("Thread: finished normally\n");
return NULL;
}
int
main(void)
{
pthread_t tid;
void *res;
pthread_create(&tid, NULL, workerThread, NULL);
sleep(3); /* Wait until thread is inside critical section */
printf("Main: sending cancel (thread is in critical section)\n");
pthread_cancel(tid); /* Cancel pending — but critical section is protected */
pthread_join(tid, &res);
printf("Main: thread %s\n",
(res == PTHREAD_CANCELED) ? "was canceled" : "finished normally");
return 0;
}
/* Expected output:
Thread: running, default state=ENABLE, type=DEFERRED
Thread: doing normal work (cancellable)
Thread: entering critical section — disabling cancel
Thread: step 1 — writing record
Main: sending cancel (thread is in critical section)
Thread: step 2 — updating index
Thread: step 3 — committing transaction
Thread: critical section done — restoring cancel state
Main: thread was canceled
[cancel fires at sleep(10) after state is re-enabled]
*/
Code Example 2: Checking & Saving Cancel Type
Demonstrates saving/restoring cancel type, and shows the oldtype portability pattern.
/* cancel_type.c - Saving and restoring cancel type */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
static void *
threadFunc(void *arg)
{
int oldstate, oldtype;
/* Always use non-NULL for portability (SUSv3 requirement) */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
printf("Thread: old state=%s, old type=%s\n",
oldstate == PTHREAD_CANCEL_ENABLE ? "ENABLE" : "DISABLE",
oldtype == PTHREAD_CANCEL_DEFERRED ? "DEFERRED" : "ASYNC");
printf("Thread: sleeping (deferred cancel — fires only at cancel point)\n");
sleep(10); /* cancellation point — will catch pending cancel */
printf("Thread: woke up normally\n");
return NULL;
}
int
main(void)
{
pthread_t tid;
void *res;
pthread_create(&tid, NULL, threadFunc, NULL);
sleep(1);
printf("Main: canceling thread\n");
pthread_cancel(tid);
pthread_join(tid, &res);
printf("Main: thread status = %s\n",
res == PTHREAD_CANCELED ? "CANCELED" : "NORMAL");
return 0;
}
Interview Questions — Section 32.2
PTHREAD_CANCEL_ENABLE — the thread accepts cancellations. Default type is PTHREAD_CANCEL_DEFERRED — cancellation is delayed until the next cancellation point.pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, ...) again, the pending request is re-evaluated, and if the type is DEFERRED, it fires at the next cancellation point.int oldstate; and pass &oldstate.