Aléas numériques

Linux, infosec and whatever crosses my mind.


» Basics of concurrency in C

Table of contents

I developed a lot in Go recently, which is notably known for its ease of implementation of concurrent programs. Even if there can be more advanced topics1 about concurrency, it is pretty straightforward to launch a function in a new goroutine (a lightweight thread managed by the Go runtime):

// this will launch the function foobar()
// in a new goroutine
go foobar()

Let’s see how can write concurrent programs in C.

Threads creation

The library pthread.h defines multiple functions that allow us to create and manage threads. For now, will only use two of them: pthread_create() and pthread_join().

The prototype of pthread_create() is the following:

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

This function is used to start a thread identified by pthread_t thread. We can see that it needs a few things, especially a function to run as a routine. It returns an int which will be the exit code of the routine.

The prototype of pthread_join() is way simpler:

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

The pthread_join() function waits for the thread specified by thread to terminate. If retval is not null, it will store the return value of the thread. Note that if the thread is already terminated when we call this function, it will return immediately.

With those two functions, we have everything we need to start a basic concurrent program:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void* hello_from_thread(void* thread_id);

int main(int argc, char** argv){
    pthread_t thread1, thread2;

    int thread_id1 = 1;
    int thread_id2 = 2;
    
    pthread_create(&thread1, NULL, hello_from_thread, (void*) thread_id1);
    pthread_create(&thread2, NULL, hello_from_thread, (void*) thread_id2);
    
    printf("hello from main\n");

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

void* hello_from_thread(void* thread_id) {
    int id = (int)thread_id;
    printf("hello from thread %d\n", id);
}

We can compile and run it:

$ gcc pthread1.c -o pthread1 -lpthread
$ ./pthread1
hello from main
hello from thread 2
hello from thread 1

Threads cancellation

A thread created by pthread_create() terminates in one of the following ways:

  • It calls pthread_exit(), specifying an exit status value that is available to another thread in the same process that calls pthread_join().
  • It returns from start_routine(). This is equivalent to calling pthread_exit() with the value supplied in the return statement.
  • It is canceled with pthread_cancel().
  • Any of the threads in the process calls exit(), or the main thread performs a return from main(). This causes the termination of all threads in the process.

In the previous example, the threads exited due to the end of start_routine().

Let’s try to cancel a thread with pthread_cancel(). The prototype of the function is:

int pthread_cancel(pthread_t thread);

We just have to indicate the thread to cancel.

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void* hello_from_thread(void* thread_id);

int main(int argc, char** argv){
    pthread_t thread;

    int thread_id1 = 1;
    
    pthread_create(&thread, NULL, hello_from_thread, (void*) thread_id1);
    
    printf("hello from main\n");
    sleep(2);

    // cancel the thread after approx. 2 seconds of execution
    pthread_cancel(thread);
    
    // pthread_join will return immediatly
    pthread_join(thread, NULL);

    return 0;
}

void* hello_from_thread(void* thread_id) {
    printf("start sleeping...\n");
    sleep(5);
    int id = (int)thread_id;
    printf("hello from thread %d\n", id);
}

If we compile this program and run it:

$ gcc pthread2.c -o pthread2 -lpthread
$ ./pthread2
hello from main
start sleeping...

Due to the cancellation that happens before the end of sleep(5) in the thread, it never prints “hello from thread 1”.

We have seen how we can create and cancel threads in C. Next time, we’ll dig deeper into threads synchronization!

Happy coding 🤓