Cancellation Points

 

Chapter 32 — Section 32.3
Cancellation Points
Specific functions where a deferred cancellation request is checked and acted upon

32.3a — What Is a Cancellation Point?

When a thread has cancellation state ENABLED and type DEFERRED (the default for all new threads), a cancellation request is not acted upon immediately. Instead, it waits until the thread calls one of a specific set of functions known as cancellation points.

Think of a cancellation point as a “safe checkpoint” where the system says: “This is a good moment to check if someone wants me to stop — I’m about to block anyway.”

Timeline: When Does Cancellation Actually Fire?
Main calls
pthread_cancel()
request pending
Thread
runs code
still running
Thread calls
sleep()/read()/…
fires here!
Thread
Terminated

Most cancellation point functions are ones that can block the thread for an indefinite time — like waiting for I/O, sleeping, or waiting on a mutex/condition variable. These are natural “pause points” where it’s safe to terminate.

32.3b — SUSv3: Required Cancellation Points

SUSv3 mandates that the following functions must be cancellation points if provided by the implementation. These are the functions where a pending deferred cancellation is guaranteed to fire.

accept()
aio_suspend()
clock_nanosleep()
close()
connect()
creat()
fcntl(F_SETLKW)
fsync()
fdatasync()
mq_receive()
mq_send()
mq_timedreceive()
mq_timedsend()
msgrcv()
msgsnd()
msync()
nanosleep()
open()
pause()
poll()
pread()
pselect()
pthread_cond_timedwait()
pthread_cond_wait()
pthread_join()
pthread_testcancel()
read()
readv()
recv()
recvfrom()
recvmsg()
select()
sem_timedwait()
sem_wait()
send()
sendmsg()
sendto()
sigpause()
sigsuspend()
sigtimedwait()
sigwait()
sigwaitinfo()
sleep()
system()
tcdrain()
usleep()
wait()
waitid()
waitpid()
write()
writev()

32.3c — Optional & Implementation-Defined Points

SUSv3 Optional Points
SUSv3 allows (but doesn’t require) implementations to make these additional functions cancellation points: stdio functions dlopen API syslog API nftw() popen() semop() unlink()
A portable program must handle the possibility that calling these may trigger cancellation.
glibc Extras (Linux-specific)
glibc marks many additional nonstandard functions as cancellation points — any function that might block for I/O or another reason is a candidate. This is a Linux implementation detail and not guaranteed on other systems.

SUSv4 Changes to the Cancellation Points List
ADDED (Required)
openat()
New open-at-directory-fd syscall added to required list
MOVED (→ Optional)
sigpause()
Demoted from required to optional cancellation point
REMOVED
usleep()
Dropped entirely from the SUSv4 standard

32.3d — Joining a Canceled Thread: PTHREAD_CANCELED

When a thread is canceled via deferred cancellation:

1
It terminates at the cancellation point.
2
If not detached, it becomes a zombie thread until joined.
3
When another thread calls pthread_join(), the retval pointer receives the special constant PTHREAD_CANCELED.
void *retval;
pthread_join(tid, &retval);

if (retval == PTHREAD_CANCELED) {
    printf("Thread was canceled\n");
} else {
    printf("Thread exited normally, value = %ld\n", (long)retval);
}

Code Example 1: TLPI Listing 32-1 — Thread Canceled at sleep()

The classic example from the book. Worker runs infinite loop with sleep(). Main cancels after 3 seconds. Cancellation fires at the sleep() cancellation point.

/* thread_cancel.c — Modeled after TLPI Listing 32-1 */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

static void *
threadFunc(void *arg)
{
    int j;

    printf("New thread started\n");
    for (j = 1; ; j++) {
        printf("Loop %d\n", j);   /* printf() MAY be a cancellation point */
        sleep(1);              /* sleep() IS a cancellation point — fires here */
    }

    /* NOTREACHED */
    return NULL;
}

int
main(int argc, char *argv[])
{
    pthread_t thr;
    int       s;
    void *    res;

    s = pthread_create(&thr, NULL, threadFunc, NULL);
    if (s != 0) { perror("pthread_create"); exit(EXIT_FAILURE); }

    sleep(3);   /* Allow thread to run for 3 iterations */

    s = pthread_cancel(thr);
    if (s != 0) { perror("pthread_cancel"); exit(EXIT_FAILURE); }

    s = pthread_join(thr, &res);
    if (s != 0) { perror("pthread_join"); exit(EXIT_FAILURE); }

    if (res == PTHREAD_CANCELED)
        printf("Thread was canceled\n");
    else
        printf("Thread was not canceled (unexpected)\n");

    exit(EXIT_SUCCESS);
}
New thread started
Loop 1
Loop 2
Loop 3
Thread was canceled

Code Example 2: Cancellation at read() — I/O Blocking Point

Thread blocks on read() from a pipe. Main thread cancels it while it’s blocked, demonstrating that I/O functions are cancellation points.

/* cancel_on_read.c — Cancellation fires while blocked on read() */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

static int pipefd[2];   /* pipe: [0]=read end, [1]=write end */

static void *
readerThread(void *arg)
{
    char buf[64];
    ssize_t n;

    printf("Reader: waiting for data on pipe (blocking read)...\n");

    /* read() is a cancellation point — will fire if cancel pending */
    n = read(pipefd[0], buf, sizeof(buf) - 1);

    /* If we reach here, we were NOT canceled (data arrived) */
    buf[n] = '\0';
    printf("Reader: received: %s\n", buf);
    return NULL;
}

int
main(void)
{
    pthread_t tid;
    void *res;

    if (pipe(pipefd) != 0) { perror("pipe"); exit(EXIT_FAILURE); }

    pthread_create(&tid, NULL, readerThread, NULL);

    sleep(2);   /* Wait for reader to block on read() */

    printf("Main: canceling thread that is blocked on read()\n");
    pthread_cancel(tid);

    pthread_join(tid, &res);

    printf("Main: reader thread %s\n",
           res == PTHREAD_CANCELED ? "was canceled (read was a cancel point)"
                                   : "finished normally");

    close(pipefd[0]);
    close(pipefd[1]);
    return 0;
}
Reader: waiting for data on pipe (blocking read)…
Main: canceling thread that is blocked on read()
Main: reader thread was canceled (read was a cancel point)

Interview Questions — Section 32.3

Q1. What is a cancellation point? Give 5 examples.
Answer: A cancellation point is a function call at which a pending deferred cancellation request is checked and acted upon. Examples: sleep(), read(), write(), pthread_cond_wait(), sem_wait().
Q2. Is printf() a guaranteed cancellation point?
Answer: No. printf() is in the stdio group which is in SUSv3’s optional list — an implementation may make it a cancellation point but is not required to. A portable program must handle the possibility. On Linux/glibc it typically is, but don’t rely on it.
Q3. What value does pthread_join() return for a canceled thread?
Answer: The second argument (retval) is set to PTHREAD_CANCELED, which is defined as (void *)-1 on Linux. You compare: if (retval == PTHREAD_CANCELED).
Q4. What change did SUSv4 make to the cancellation points list?
Answer: SUSv4 added openat() to the required list, moved sigpause() from required to optional, and removed usleep() entirely from the standard.
Q5. If a thread never calls any cancellation point function, will pthread_cancel() ever take effect?
Answer: Not with DEFERRED type — the cancellation stays pending but never fires. This is the “compute-bound loop” problem. The solution is to periodically call pthread_testcancel() inside the loop — covered in Section 32.4.
Q6. Is pthread_testcancel() itself a cancellation point?
Answer: Yes. pthread_testcancel() is explicitly listed by SUSv3 as a required cancellation point. Its sole purpose is to act as a cancellation point — if cancellation is pending and enabled when called, the thread terminates there.
© EmbeddedPathashala | TLPI Chapter 32 | Section 32.3

Leave a Reply

Your email address will not be published. Required fields are marked *