Canceling a Thread

 

Chapter 32 — Section 32.1
Canceling a Thread
How to send a cancellation request to a running thread using pthread_cancel()

Why Do We Need Thread Cancellation?

Threads normally run until they finish their work and call pthread_exit() or return from their start function. But in real applications, you often need to stop a thread early — from outside that thread.

Consider these scenarios:

🖥️ GUI Application
User clicks “Cancel” on a long-running background task. The main (GUI) thread needs to stop the worker thread immediately.
🧮 Parallel Computation
One thread in a pool detects a fatal error. It needs to cancel all other worker threads to avoid wasted computation.
⏱️ Timeout Handling
A thread waiting for a network response for too long should be cancelled by a timer/watchdog thread.
🔄 Server Shutdown
When a server receives a shutdown signal, the main thread needs to cancel all active connection-handler threads.

POSIX Threads provides pthread_cancel() for exactly this purpose.

The pthread_cancel() Function

#include <pthread.h>
int pthread_cancel(pthread_t thread);
Returns: 0 on success, positive error number on error

Parameter Breakdown

Parameter Type Description
thread pthread_t Thread ID of the thread to cancel. Obtained from pthread_create().
return value int 0 on success. On error, returns a positive errno value (e.g., ESRCH if thread ID is invalid).

Key Behaviors — What Happens When You Call It?

1
Request is Sent — Not Executed Immediately
pthread_cancel() only sends a cancellation request. It does not block waiting for the target thread to terminate. The calling thread continues running after the call returns.
2
What Happens Next Depends on the Target Thread
The actual response — whether the target thread is canceled immediately, delayed, or ignores it — depends entirely on that thread’s cancellation state and cancellation type. (Covered in Section 32.2 and 32.3.)
3
You Must Still Join the Thread
After calling pthread_cancel(), if the thread is not detached, you must still call pthread_join() to reclaim its resources and avoid a zombie thread.
4
Detecting Cancellation via pthread_join()
When you join a canceled thread, the retval (second argument to pthread_join()) is set to the special constant PTHREAD_CANCELED. You can check this to know the thread was canceled rather than finishing normally.

⚠️ Important: Not Synchronous
Many beginners assume calling pthread_cancel() kills the thread immediately like a process kill signal. It does NOT. The target thread controls when (and whether) it responds. Always pair with pthread_join() to confirm termination.

Code Example 1: Basic pthread_cancel() Usage

A thread runs an infinite loop. The main thread sends a cancellation request after 3 seconds, then joins to confirm it terminated.

/* basic_cancel.c - Demonstrates pthread_cancel() */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

/* Thread function: runs an infinite loop, sleeping 1 sec each iteration */
static void *
workerThread(void *arg)
{
    int count = 0;

    printf("Worker: started\n");

    while (1) {
        count++;
        printf("Worker: loop iteration %d\n", count);
        sleep(1);    /* sleep() is a cancellation point */
    }

    /* Never reached normally */
    return NULL;
}

int
main(void)
{
    pthread_t tid;
    void *retval;
    int s;

    /* Create the worker thread */
    s = pthread_create(&tid, NULL, workerThread, NULL);
    if (s != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    sleep(3);    /* Let worker run for 3 seconds */

    printf("Main: sending cancellation request\n");

    /* Send cancellation request — does NOT block */
    s = pthread_cancel(tid);
    if (s != 0) {
        perror("pthread_cancel");
        exit(EXIT_FAILURE);
    }

    printf("Main: cancel sent, now joining...\n");

    /* Join to wait for thread termination and reclaim resources */
    s = pthread_join(tid, &retval);
    if (s != 0) {
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }

    /* Check if thread was canceled or ended normally */
    if (retval == PTHREAD_CANCELED)
        printf("Main: thread was canceled (confirmed)\n");
    else
        printf("Main: thread ended normally\n");

    return 0;
}

# Compile and run:
$ gcc -o basic_cancel basic_cancel.c -lpthread
$ ./basic_cancel
Worker: started
Worker: loop iteration 1
Worker: loop iteration 2
Worker: loop iteration 3
Main: sending cancellation request
Main: cancel sent, now joining…
Main: thread was canceled (confirmed)
📌 What’s happening here
The worker’s sleep(1) call is a cancellation point. When the main thread calls pthread_cancel(), the pending request is delivered the next time the worker calls sleep(). The worker never reaches “loop 4”. pthread_join() then returns PTHREAD_CANCELED in retval.

Code Example 2: Canceling One Thread from Another Worker

Two worker threads run in parallel. Worker-2 detects a problem and cancels Worker-1 before the main thread joins both.

/* worker_cancel.c - One thread cancels another */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

/* Shared thread ID so worker2 can cancel worker1 */
static pthread_t worker1_tid;

static void *
worker1(void *arg)
{
    printf("Worker1: running, tid=%lu\n", (unsigned long)pthread_self());
    while (1) {
        printf("Worker1: doing work...\n");
        sleep(1);
    }
    return NULL;
}

static void *
worker2(void *arg)
{
    printf("Worker2: running, will cancel Worker1 after 2s\n");
    sleep(2);

    /* Simulate: worker2 detects an error, cancels worker1 */
    printf("Worker2: ERROR detected! Canceling Worker1\n");
    int s = pthread_cancel(worker1_tid);
    if (s != 0) {
        perror("pthread_cancel from worker2");
    }

    printf("Worker2: cancel request sent, exiting\n");
    return (void *)42;   /* Worker2 ends normally */
}

int
main(void)
{
    pthread_t w2_tid;
    void *res1, *res2;

    pthread_create(&worker1_tid, NULL, worker1, NULL);
    pthread_create(&w2_tid,      NULL, worker2, NULL);

    pthread_join(worker1_tid, &res1);
    pthread_join(w2_tid,      &res2);

    printf("Main: Worker1 status = %s\n",
           (res1 == PTHREAD_CANCELED) ? "CANCELED" : "NORMAL");
    printf("Main: Worker2 exit value = %ld\n", (long)res2);

    return 0;
}
Worker1: running, tid=139…
Worker2: running, will cancel Worker1 after 2s
Worker1: doing work…
Worker1: doing work…
Worker2: ERROR detected! Canceling Worker1
Worker2: cancel request sent, exiting
Main: Worker1 status = CANCELED
Main: Worker2 exit value = 42

Interview Questions — Section 32.1

Q1. What does pthread_cancel() do? Does it immediately kill the thread?
Answer: pthread_cancel() sends a cancellation request to the target thread and returns immediately without waiting. It does NOT immediately kill the thread. Whether and when the thread actually terminates depends on its cancellation state (enabled/disabled) and type (deferred/asynchronous).

Q2. After calling pthread_cancel(), do you still need to call pthread_join()?
Answer: Yes. If the thread is not detached, you must still call pthread_join() to reclaim its resources. Without joining, the canceled thread becomes a “zombie thread” — its resources are not freed until the process exits.

Q3. How do you detect that a thread was canceled (not normal exit) when joining it?
Answer: Check the return value in the second argument of pthread_join(). If the thread was canceled, it will be equal to the special constant PTHREAD_CANCELED. Example: if (retval == PTHREAD_CANCELED).

Q4. What is the return value of pthread_cancel() and what error can it report?
Answer: Returns 0 on success, or a positive error number on failure. A common error is ESRCH — meaning no thread with that thread ID was found (e.g., the thread already exited).

Q5. Can a thread cancel itself? What happens?
Answer: Yes, a thread can call pthread_cancel(pthread_self()). The behavior follows the same rules — it depends on the thread’s cancellation state and type. However, calling pthread_exit() is the cleaner and more direct way for a thread to terminate itself.

Q6. What is PTHREAD_CANCELED, and what type is it?
Answer: PTHREAD_CANCELED is a special constant value (defined as (void *)-1 on Linux). It is returned as the thread’s “exit status” when checked via pthread_join(). It is typed void * to be compatible with the retval parameter of pthread_join().

© EmbeddedPathashala | TLPI Chapter 32 | Section 32.1

Leave a Reply

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