POSIX Thread — Pthread Library

pthread_create( ) , pthread_join( )

A. Yigit Ogun
6 min readSep 18, 2022

Pthread library is a programming library that is generally developed in UNIX derivative operating systems so that the concept of thread can be coded with the C language. The library is referred to as the POSIX library in UNIX environment (and therefore also in LINUX environment) and it is derived from the combination of the initial letter P of this library and the word thread (pthread).

As it is known, applications/processes are executed by the operating system and each program can only perform one operation in a time period. Thread is used to solve this problem of single processing at the same time. Thread stands for “sub-thread” of the application and performs the task assigned to it. Therefore, an application with more than one thread becomes capable of doing more than one job at the same time. In addition to the benefit of doing more than one job at the same time, the use of threads provides us many benefits. Basic thread operations can be done using the library. Below, some basic functions used in the library will be given with their definitions.

Representation of creation of two worker threads by master thread

1) Pthread Creation

#include <pthread.h>  int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);                                                 Returns: 0 if OK, error number on failure

After adding pthread library to our project, a thread is created with the pthread_create() function as shown above. The “tidp” pointer of type pthread_t carries the ID value of the thread created as a result of the success of the pthread_create() function.

In this way, this identity value will be used in some operations that may be needed for this thread. The attr(attribute) argument is used to specify the thread properties to be created. However, if this value is set to NULL, the thread is created with default properties. The start_rtn argument is the address of the function where the thread will run. The arg parameter carries the address of the parameter to be passed to the function that the thread will run.

With the creation of the thread, the function specified in the thread start_rtn address starts to run. But it is not clear whether the thread function or the piece of code that wants to create a thread runs first. As an example, when we look at the following snippet, first printf(“func foo \n”); line or printf(“ calling thread \n”); It is not clear which line runs first.

void * foo(void *arg){    printf("func foo \n");}
pthread_create(&tid, NULL, foo, NULL);
printf(" calling thread \n");

An error value is returned when the pthread_create() function fails. However, the following important information should be given for these error situations. In case of an error, the pthread_create() function does not write the error value to the global errno variable like other POSIX functions. In this case, the error is only seen in the function from which it was called, with the return value of the function.

#include <stdio.h>#include <pthread.h>pthread_t tid;void * threadFunc(void *arg){   printf("Thread function \n");   return((void *)0);}int main(void){   int err;   err = pthread_create(&tid, NULL, threadFunc, NULL);   if (0 != err)      printf("can’t create thread \n");   printf("main thread \n");   sleep(1);   
return 0;
}

The above code is given as an example to create threads. But as mentioned earlier, there is no guarantee whether the thread function or the main function will be called first. Also, the necessity of the sleep(1) call here is due to this inconsistency. Because in the case of running the main function first, the main function can terminate before the thread function is terminated. Such situations and synchronization create the difficulties of using threads anyway.

2) Pthread Termination

Threads can be terminated in three different ways.

  1. Exiting with return inside the thread function.
  2. Terminating by another thread (pthread_cancel() )
  3. Calling pthread_exit() in the thread

The use of these functions varies according to the place and purpose. Although these three uses basically terminate the thread, it is necessary to know the details of the three uses due to the way they work.

#include <pthread.h>
void pthread_exit(void *rval_ptr);

Calling the pthread_exit() function inside the thread ensures that the thread it is in is terminated from the point at which it was called. Attention should be paid to the use of the rval_ptr pointer. If the pthread_join() function is used for the terminated thread, the rval_ptr pointer is used by pthread_join(). In this usage, the error is usually made to load the address of the auto-lived variable for the rval_ptr pointer. If this address will be used after the pthread_join() call, the rval_ptr loaded address must also be valid after the thread is terminated. The rval_ptr content is usually associated with the value that describes the reason for the thread termination. Of course, the user can also upload the address of the data he wants by following the usage rule. If you do not want to deal with the content of this pointer, a NULL address can be given.

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
Returns: 0 if OK, error number on failure

In the pthread_join() declaration, **rval_ptr moves the data sent after the thread termination. This value is populated by the three different termination methods mentioned above. While the user can return the desired value with return and pthread_exit(), the content of rval_ptr becomes PTHREAD_CANCELED after using pthread_cancel().

pthread_join() blocks the thread at the call point until the related thread terminates. Of course, if the pthread_join function does not encounter an error, it blocks. For example, if the thread specified in the first argument of pthread_join() has already terminated, then EINVAL returns with an error value and the block does not occur.

Take a close look at the code snippet above. If we execute it, output will be like this:

As seen in the working example above, the compiled code was executed and the messages “Master thread” and “New created fiber” were printed on the screen, respectively.

Let’s walk thorough. During the execution of the code, a variable that can hold a thread information and produced from the pthread_t structure (struct) is defined in the main function. We will use this variable in later examples to access thread directly. This is the first parameter of the pthread_create function, which is the most important line of our example. This parameter should be considered as a call by reference process and in fact, it is to get the information of the new thread produced by giving a parameter during the thread production (pthread_create).

Then the message “Master thread” was printed on the screen. The point to be noted here is that there are at least two threads when producing a new thread. One is the newly produced thread and the other is the main thread, which is the thread that produces the other thread. This situation can be expressed as follows:

As can be seen in the representative picture above, two different threads were produced in the memory with a pthread_create() function called in the program flow and they continued to work at the same time. After the work in the main thread was finished, with pthread_join(), the newly produced thread was waited until it finishes.

--

--

A. Yigit Ogun

42 Heilbronn ✦ AWS Community Builder ✦ DevOps ✦ Cloud ✦ C/C++ ✦ GNU/Linux ✦ https://linktr.ee/ayogun