32.6a — What Is Asynchronous Cancellation?
With the default DEFERRED type, cancellation fires only at specific cancellation points. With ASYNCHRONOUS type, the rules change dramatically:
In asynchronous mode, a pending cancellation may be delivered at any machine-language instruction — not just at designated points. The OS can preempt the thread mid-operation and terminate it.
32.6b — Why Asynchronous Cancellation is Dangerous
Problem 1: Cleanup Handlers Cannot Know the State
In deferred mode, if a thread is canceled at pthread_cond_wait(), we know exactly the thread’s state: the mutex is locked (or being released atomically), buf is allocated. The cleanup handler can rely on this.
With asynchronous cancellation, the thread could be stopped at any point — before malloc(), between malloc() and the lock, or after the lock. The handler has no way to know which steps completed:
Problem 2: Most Library Functions Are Not Async-Cancel-Safe
A thread that is asynchronously cancelable cannot safely call almost any library function, because those functions may have internal state that gets corrupted if the thread is canceled mid-way. This includes:
32.6c — The Only Three Async-Cancel-Safe Functions
SUSv3 explicitly requires only three functions to be async-cancel-safe — safe to call from a thread that uses asynchronous cancellation:
pthread_testcancel() — which is always a cancellation point — is NOT listed as async-cancel-safe, because it does more complex work.32.6d — When Is Asynchronous Cancellation Actually Useful?
Given all the dangers, there is exactly one scenario where asynchronous cancellation is genuinely safe and appropriate:
/* This loop is SAFE for async cancellation:
pure arithmetic, no resources, no library calls */
for (long long i = 0; i < HUGE_NUMBER; i++) {
result += i * i * i; /* arithmetic only */
}
/* If canceled mid-loop: nothing to clean up, no corruption */
The practical advantage over using pthread_testcancel() is slightly faster cancellation response — the thread doesn’t need to reach a test point. But given the risks, pthread_testcancel() is almost always the better choice even for compute loops, because it’s safer if the loop ever needs modification.
PTHREAD_CANCEL_ASYNCHRONOUS entirely. Use deferred cancellation with pthread_testcancel() for compute loops. The deferred mode is predictable, debuggable, and extensible. Asynchronous mode is a maintenance hazard — future developers may add a malloc() to the loop not knowing the thread is async-cancelable.Code Example 1: Enabling Asynchronous Cancellation
Shows how to switch to asynchronous mode for a pure compute loop, then safely switch back to deferred mode before doing any resource operations.
/* async_cancel.c — Async mode for pure compute, then revert */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
static void *
computeThread(void *arg)
{
int oldtype;
long long result = 0;
/* Phase 1: Switch to ASYNC for pure compute section
ONLY safe because: no malloc, no mutex, no library calls */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
printf("Thread: starting pure compute (async cancel enabled)\n");
/* This loop contains only arithmetic — safe for async cancel */
for (long long i = 0; i < 5000000000LL; i++) {
result += (i % 1000) * (i % 777);
/* NO malloc, NO mutex, NO library calls in this loop */
}
/* Phase 2: Revert to DEFERRED before any resource operations */
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
/* From here: resource operations are safe again */
printf("Thread: compute done (result=%lld), now doing resource work\n",
result);
/* Any resource allocation or I/O goes here in DEFERRED mode */
sleep(1); /* cancellation point — any pending cancel fires here */
printf("Thread: all done\n");
return (void *)(intptr_t)result;
}
int
main(void)
{
pthread_t tid;
void *res;
pthread_create(&tid, NULL, computeThread, NULL);
sleep(1); /* Thread is in async compute phase */
printf("Main: canceling thread (it is in async mode)\n");
pthread_cancel(tid);
pthread_join(tid, &res);
printf("Main: thread %s\n",
res == PTHREAD_CANCELED ? "was canceled" : "finished normally");
return 0;
}
sleep(1) in deferred mode.Code Example 2: Demonstrating the Danger — Unsafe Async Usage
This example shows WHY async cancellation is dangerous with malloc(). DO NOT use this pattern — it is shown for educational purposes only.
/* async_danger.c — EDUCATIONAL: shows WHY async cancel is unsafe
⚠️ DO NOT use this pattern in real code ⚠️ */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void
cleanupHandler(void *arg)
{
/* ❌ Problem: handler can't know if malloc() completed or not.
If buf is NULL (cancel before malloc), free(NULL) is ok.
If cancel was INSIDE malloc() — heap is corrupted regardless. */
printf("cleanup: attempting free(%p) — may be corrupted!\n", arg);
free(arg); /* This is unsafe if malloc was mid-call */
/* ❌ Another problem: mutex may or may not be locked.
Unlocking an unlocked mutex is undefined behavior. */
printf("cleanup: attempting mutex unlock — may deadlock!\n");
pthread_mutex_unlock(&mtx); /* undefined if not actually locked */
}
static void *
unsafeThread(void *arg)
{
void *buf = NULL;
/* ⚠️ WRONG: enable async cancel before resource operations */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_cleanup_push(cleanupHandler, buf); /* buf is NULL here */
/* Cancel can happen ANYWHERE below — cleanup can't handle it */
buf = malloc(1024); /* ← cancel inside malloc? heap corrupted */
pthread_mutex_lock(&mtx); /* ← cancel here? mutex stays locked */
/* ... do work ... */
pthread_mutex_unlock(&mtx);
free(buf);
pthread_cleanup_pop(0);
return NULL;
}
int
main(void)
{
pthread_t tid;
void *res;
printf("⚠️ This is an UNSAFE demo — do not copy this pattern!\n");
pthread_create(&tid, NULL, unsafeThread, NULL);
usleep(100); /* Cancel almost immediately — likely mid-operation */
pthread_cancel(tid);
pthread_join(tid, &res);
printf("Result: %s (program may be in corrupted state)\n",
res == PTHREAD_CANCELED ? "CANCELED" : "NORMAL");
return 0;
}
buf=NULL pointer (because cleanup_push was called before malloc), and has no way to know if the mutex was locked. This is the fundamental unsolvable problem with async cancellation and resources.Interview Questions — Section 32.6
pthread_cancel(), pthread_setcancelstate(), and pthread_setcanceltype(). These are the only Pthreads functions guaranteed safe to call from a thread with PTHREAD_CANCEL_ASYNCHRONOUS.pthread_testcancel() in deferred mode is usually preferable for safety and maintainability.pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype) before doing resource operations, and restore with pthread_setcanceltype(oldtype, NULL) afterward. Or use pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, ...) to block all cancellation.