POSIX Threads Programming in C

  • Post author:
  • Post last modified:February 29, 2024
  • Reading time:8 mins read

POSIX threads

A process is an execution environment in an operating system. A process has code and data segments which are initialized from a program during an exec system call. A process has a thread of execution, wherein instructions are executed as per the value of the program counter register. Associated with a process is a stack and stack pointer has the address of the top of the stack.

Operating systems support multiple processes in a typical time sharing environment. They switch processes (fast) by saving the processor registers and loading another set of previously saved registers. Just like supporting multiple processes, modern operating systems support multiple threads of execution for a process. Rather than having one (saved copy of) stack pointer and one program counter for a process, they have one set of registers (including stack pointer and the program counter) for each thread of execution for a process. The global data and file pointers are shared between threads. So, it's OK if some threads wish to block (wait) for some event like arrival of data on a socket connection; other threads can do something useful in the meantime. So, essentially, we have concurrent multiple flows of execution without incurring the overhead of multiple processes.

POSIX stands for Portable Operating System Interface, a set of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. POSIX.1c (IEEE Std 1003.1c-1995) deals with Thread extensions. POSIX threads refer to the API defined by the POSIX.1c standard. The Native POSIX Thread Library (NPTL) is an implementation of POSIX threads for Linux. NPTL was developed at Red Hat and has been a part of Linux since version 2.6. POSIX threads are commonly known as Pthreads.

1.0 Basic pthread calls

We can get started with Pthreads with four basic calls, pthread_create, pthread_join, pthread_cancel and pthread_exit. A process has the default main thread after an exec system call. The main thread starts executing the main function of the program.

1.1 pthread_create

#include <pthread.h>

int pthread_create (pthread_t *thread, const pthread_attr_t *attr,
                    void *(*start_routine) (void *), void *arg);

pthread_create starts a new thread in the calling process. The first argument, thread, is a pointer to the type pthread_t in which the thread id is returned. The second argument, attr is a pointer to desired attributes and can be NULL if you are happy with the default attributes. The last parameter, arg, is a pointer and provides a way to pass data to the start_routine. The third parameter, start_routine is the function that the newly created thread starts executing. The start_routine must have the prototype,

void *start_routine (void *arg);

pthread_create returns zero on success and a nonzero error number in the case of failure.

1.2 pthread_join

#include <pthread.h>

int pthread_join (pthread_t thread, void **retval);

pthread_join waits for the thread identified by the first argument to terminate. The calling thread's id cannot be the first argument which means a thread cannot wait for itself to terminate. Also, two threads cannot wait for each other to terminate. In all these cases the error EDEADLK is returned. If retval is not NULL, the pointer to the exit status of the thread is copied in *retval. However, if the thread was cancelled, PTHREAD_CANCELED is copied in *retval.

1.3 pthread_cancel

#include <pthread.h>

int pthread_cancel (pthread_t thread);

pthread_cancel cancels the thread.

1.4 pthread_exit

#include <pthread.h>

void pthread_exit (void *retval);

pthread_exit terminates the calling thread. A thread can pass an exit value by passing a pointer to the exit value in the pthread_exit call. The exit value should have a scope such that it is visible to all threads which means that it should be a global variable at the top of the program.

2.0 pthread error reporting

Pthread calls return zero on success and if there is an error, that error is returned.

3.0 pthread example program in C

In the example program, the main thread creates ten threads. Then it waits for the threads to terminate, printing the status returned by each thread. The last thread was cancelled which is recorded in the output.

/*
 *
 *   threads-example.c: Program to demonstrate pthreads in C
 */

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

void *ethread (void *arg);

char ret_status [10] [100];

int main (int argc, char **argv)
{
    pthread_t tid [10];
    int i, r;
    void *status;
    char buffer [128];

    // Create 10 threads
    int thread_no [10];
    for (i = 0; i < 10; i++) {
        thread_no [i] = i;
        if ((r = pthread_create (&tid [i], NULL, ethread, (void *) &thread_no [i])) != 0) {
            strerror_r (r, buffer, sizeof (buffer));
            fprintf (stderr, "Error = %d (%s)\n", r, buffer); exit (1);
        }
    }
    if ((r = pthread_cancel (tid [9])) != 0) {
        strerror_r (r, buffer, sizeof (buffer));
        fprintf (stderr, "Error = %d (%s)\n", r, buffer); exit (1);
    }
    // Wait for threads to terminate
    for (i = 0; i < 10; i++) {
        if ((r = pthread_join (tid [i], &status)) != 0) {
            strerror_r (r, buffer, sizeof (buffer));
            fprintf (stderr, "Error = %d (%s)\n", r, buffer); exit (1);
        }

        if (status == PTHREAD_CANCELED)
            printf ("i = %d, status = CANCELED\n", i);
        else
            printf ("i = %d, status = %s\n", i, (char *) status);
    }
    exit (0);
}

// ethread: example thread
void *ethread (void *arg)
{
    int my_id = *((int *) arg);

    // Take a nap
    sleep (1);

    // say hello and terminate
    printf ("Thread %d: Hello World!\n", my_id);

    sprintf (ret_status [my_id], "Thread %d: %d", my_id, my_id + 10);

    if (my_id == 9) sleep (10);

    // pass your id to the thread waiting for you to terminate
    // using pthread_join.
    pthread_exit (ret_status [my_id]);
}

We can compile and run the above program as shown below.

$ gcc threads-example.c -o threads-example -lpthread
$ ./threads-example 
Thread 4: Hello World!
Thread 0: Hello World!
Thread 1: Hello World!
i = 0, status = Thread 0: 10
i = 1, status = Thread 1: 11
Thread 5: Hello World!
Thread 6: Hello World!
Thread 7: Hello World!
Thread 3: Hello World!
Thread 2: Hello World!
Thread 8: Hello World!
i = 2, status = Thread 2: 12
i = 3, status = Thread 3: 13
i = 4, status = Thread 4: 14
i = 5, status = Thread 5: 15
i = 6, status = Thread 6: 16
i = 7, status = Thread 7: 17
i = 8, status = Thread 8: 18
i = 9, status = CANCELED

4.0 See also

Share

Karunesh Johri

Software developer, working with C and Linux.
5 2 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments