POSIX Thread APIs

POSIX Portable Operating System Interface is a set of standard specified by the IEEE computer society which defines Application Programming Interface(API), header file,command line shells for software compatibility with variants of Unix and other operating system.

POSIX defines set of API, function, header file for threaded programming known as POSIX threads or Pthreads.

POSIX Thread APIs:
A few of the important API provided by the POSIX thread are:

pthread_create(): Create a thread

  • The pthread_create() function starts a new thread in the calling process.
  • The new thread starts execution by invoking start_routine(); arg is passed as the sole argument of start_routine().
int res =pthread_create (pthread_t *th_id, pthread_attr_t         *attr, void *(*start_routine), void *arg);

pthread_t *th_id:
Each thread is identified by id known as thread identifier, so this variable(pointer) points to address which holds the identifier for the new created thread.

pthread_attr_t *attr:
The attr argument points to pthread_attr_t structure whose contents are used at the time of creation of thread. It attr is NULL, then the thread is created with default values.

void *(*start_routine):
This is the address of the start routine which the new thread need to execute.

void * arg:
This is the sole argument of the start routine.

Return value:
It returns 0 on success and an error number of failure.

Notes:

  • Only one argument can be passed to the thread function, if required to pass multiple arguments pointer to a structure can be used.
  • Always the return type and passed argument should be (void*).
  • In the thread function, it can be typecasted accordingly.

Sample Code:

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

void *fun ( void *arg)
{
  printf("\n %s this is the thread function\n", (char *)arg);
}
  
int main()
{
  pthread_t th_id;
  int res = pthread_create (&th_id, NULL, fun, "Hello");
  if (res ! =0)
  {
     printf ("\n Thread creation failed %s\n", strerror(res));
     exit(0);
  }
  printf ("\n Thread created successfully id: %lu\n", th_id);
  return 0;
} 
OUTPUT:
[aprakash@wtl-lview-6 thread]$ gcc thread.c -lpthread
[aprakash@wtl-lview-6 thread]$ ./a.out 

Thread created successfully id: 139642010285824 
Hello this is the thread function

Join a thread: pthread_join() API

  • The pthread_join() function provides a simple mechanism allowing an application to wait for a thread to terminate, detaches the thread, then returns the thread’s exit status.
  • If the main thread terminates before the child thread, the child thread entry that is, it retains the identification (pthread_t value) and the void * return value that was returned from the thread’s start function or specified in a call to pthread_exit() will still be present in the system and results in a zombie thread.
  • When there are enough zombie threads accumulated, it will no longer be possible to create a new thread or a process.
  • Thus pthread_join() API makes sure that the calling thread does not terminate until the target or a child thread and at the same time clean up its entry, avoiding any zombie threads in a system.
  • This is similar to waitpid() API in the case of process.
  • There is no pthreads analog of waitpid(-1, &status, 0), that is, “join with any terminated thread”.
  • This is again a blocking call that is the calling thread will block until the target thread has terminated.
  • By default the threads run in joinable mode, it will not release any resources even after the end of the thread functions, until another thread calls pthread_join() with its ID.
  • Either pthread_join() or pthread_detach() should be called for each thread that an application creates, so that system resources for the thread can be released.
int pthread_join (pthread_t thread_id, void **retval);

pthread thread_id:
It is the thread to wait for that is the thread identifier filled while creating pthread_create().

void **retval:
If retval is not NULL, then pthread_join() copies the exit status of the target thread that is the value given by pthread_exit() into the location pointed by *retval.

Return Value:

It returns 0 in success and error number in the case of failure.

Diag-1: pthread_join() API

This image has an empty alt attribute; its file name is pthread_join.png

Sample Program:

The below code creates a thread from the main thread and calls pthread_join() so that the main thread does not terminate before the child thread. It does not collect the return status of the child by using pthread_exit(). pthread_exit() along with pthread_join() is handled under pthread_exit().

Please refer pthread_exit() to understand complete working pthread_join()

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

void *fun(void *input)
{
printf("\n This is a child thread function\n");
sleep(10);
printf("\n Child thread execution completed\n");
}

int main()
{
pthread_t th_id;
void *exit_status;
int res = pthread_create (&th_id, NULL, fun, NULL);
if (res !=0)
{
printf ("\n Thread creation failed: %s\n", strerror(res));
exit(0);
}

printf ("\n Main thread waiting for the child to finish\n");
res= pthread_join (th_id, NULL);
if (res !=0)
{
printf ("\n Thread join failed: %s\n", strerror(errno));
exit(0);
}

printf ("\n Thread joined successfully\n");
printf("\n Main thread started executing again..\n");
return 0;
}
Output:
[aprakash@wtl-lview-6 thread]$ gcc thread_join.c -lpthred
[aprakash@wtl-lview-6 thread]$ ./a.out

Main thread waiting for the child to finish
This is a child thread function
Child thread execution completed
Thread joined successfully
Main thread started executing again..

Terminate a thread:pthread_exit() API:

Any thread can terminate in three ways:

  • If the thread returns from its start routine.
  • If it is canceled by some other thread or cancels itself by using pthread_cancel() API.
  • If its calls pthread_exit() function from within itself.
  • A process can exit at any time when it calls the exit subroutine, Similarly, a thread can exit at any time by calling the pthread_exit subroutine.
  • The pthread_exit() function terminates the calling thread and returns the exit status via retval that( if the thread is joinable) is available to another thread in the same process that calls pthread_join()
  • When a thread terminates, process-shared resources (e.g,. mutex, condition variables, semaphore, and the file descriptors are not released.
  • When the thread count goes to 0 that is after the last thread in a process terminates, the process terminates by calling exit() with the exit status of zero, and all process-shared resources are released.
  • When thread needed to continue execution but no longer need the main thread. In that case, pthread_exit (NULL); can be used from the main thread which will let other threads continue even after the main thread exits.
  • Performing a return from the start function of any thread other than the main thread results in an implicit call to pthread_exit(), using the function’s return value as the thread’s exit status.
#include <pthread.h>
void pthread_exit (void *retval);

This can be used along with pthread_join() from another thread to get the exit status of thread.

#include <pthread.h>
int pthread_join (pthread_t th_id, void **retval);

Return value:
pthread_exit() API does not return anything to the caller, it always succeeds.

Sample Code:

The below code is used to pass the address of the structure to the pthread_join() API and the structure is populated in the thread function and is made available to the main thread by using pthread_exit() API.

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

struct student
{
char name[10];
int roll;
};

void *fun (void *input)
{
printf ("\n This is a thread function\n");
struct student *st = (struct student *)malloc(sizeof(struct student));
st->roll =10;
strcpy (st->name,input);
pthread_exit ((void*)st);
}

int main()
{
pthread_t th_id;
struct student *st;
char *name ="Hello";

int res = pthread_create (&th_id, NULL, fun, (void*)name);
if (res !=0)
{
printf ("\n Thread creation failed: %s\n", strerror(res));
exit(0);
}

printf ("\n Waiting for the thread to finish\n");
res = pthread_join (th_id,(void*)&st);
if (res !=0)
{
printf ("\n Thread join failed: %s\n", strerror(errno));
exit(0);
}

printf ("\n Thread Joined successfully\n");
printf("\n Main thread started executing again..\n");
printf ("\n %s roll number is %d\n", st->name, st->roll);
return 0;
}

Output:
[aprakash@wtl-lview-6 thread]$ gcc thread_exit.c -lpthred
[aprakash@wtl-lview-6 thread]$ ./a.out
Waiting for the thread to finish
This is a thread function
Thread Joined successfully
Main thread started executing again..
Hello has roll number is 10

Cancel a thread:pthread_cancel() API:

  • The pthread_cancel() function sends a cancellation request to a thread represented by id th_id.
  • For example, if multiple threads are concurrently searching through the database and one thread returns the result, other threads can be canceled.
  • This is one of the ways to terminate a thread along with pthread_exit() API.
  • The key difference between pthread_exit() and pthread_cancel() is that earlier one always succeeds that is guaranteed to terminate the thread while pthread_cancel() is just a request and it depends upon cancelability state and type and its return value is always PTHREAD_CANCELED.
int pthread_cancel (pthread_t th_id)
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);

state = PTHREAD_CANCEL_ENABLE or PTHREAD_CANCEL_DISABLE
Type = PTHREAD_CANCEL_DEFERRED or PTHREAD_CANCEL_ASYNCHRONOUS

  • The sole argument to this function is the thread id of the thread to be canceled.
  • This is just a cancellation request, whether and when the target threads respond to this cancellation request depends upon the two thread attribute that is cancelability state and cancelability type:
    • pthread_setcancelstate(): enable or disable
    • pthread_setcanceltype(): asynchronous or deferred
  • If the cancel state is disabled, then the cancellation request remains queued until the thread enables cancellation.
  • If the cancel type is asynchronous, a thread can be cancelled at any time(usually immediately, but the system does not guarantee)
  • If the cancel type is deferred, cancellation is delayed until the thread calls a function that is a cancellation point.
  • One important use case of disabling the cancel state is during the execution of a critical section of code which involves a file-save operation(such as an indexed database), otherwise, the files may be left in an inconsistent state.  To avoid this, the thread should disable cancelability during the file save operations.

Difficulties with cancellation request:

  • If the thread is canceled while in the midst of updating data it is sharing with others threads. It leaves the system in inconsistent state. T
  • Many times, the operating system will reclaim system resources from a cancelled thread but will not reclaim all resources. So, canceling a thread asynchronously may not free a necessary system-wide resource.
  • If the cancel type is deferred, cancellation occurs only after the target thread has checked a flag to determine whether or not it should be canceled. In this case cancellation only happens once all the resources are released and the system is in safe state.

Return Value:

  • It returns 0 on success and a non-zero error number on failure.
  • The return status of pthread_cancel() merely informs the caller whether the cancellation request was successfully queued.
  • After a canceled thread has terminated, a join with that thread using pthread_join() obtains PTHREAD_CANCELED as the thread’s exit status that is pthread_exit() function is called implicitly with PTHREAD_CANCELED as exit status which can be made available to calling thread by using pthread_join() function.

Source Code:
Main thread cancels the child thread and compares the exit status with PTHREAD_CANCELED. by default cancel state is enabled and type is asynchronous.

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

void *fun(void *input)
{
printf("\n Child thread executing ...\n");
/* sleep of 10 sec is given so that it can be cancelled before it finishes */
sleep (10);
/* This print should not come as it was cancelled */
printf("\n Child thread execution completed\n");

}

int main()
{
pthread_t th_id;
void *exit_status;
int res = pthread_create (&th_id, NULL, fun, NULL);
if (res !=0)
{
printf ("\n Thread creation failed: %s\n", strerror(res));
exit(0);
}
/* sleep is given so that child thread can start */
sleep (3);
printf("\n Cancelling the child thread\n");
res = pthread_cancel (th_id);
if (res !=0)
{
printf ("\n Thread cancellation request failed to queue: %s\n", strerror(res));
exit(0);
}

res= pthread_join (th_id, &exit_status);
if (res !=0)
{
printf ("\n Thread join failed: %s\n", strerror(errno));
exit(0);
}

if (exit_status == PTHREAD_CANCELED)
printf ("\n Thread cancelled successfully\n");
else
printf("\n Thread was not cancelled\n");

return 0;
}
 Output:
[aprakash@wtl-lview-6 thread]$ gcc thread_cancel.c -lpthred
[aprakash@wtl-lview-6 thread]$ ./a.out

Child thread executing ... Cancelling the child thread
Thread cancelled successfully

Deferred Cancellation:

  • In the case of deferred cancellation, the request to cancel is not handled immediately, here cancellation occurs only when a thread reaches a cancellation point.
  • To reach a cancellation point is to invoke the pthread_testcancel() function which creates a cancellation point within the calling thread and if any cancellation request is pending in the queue, any resources a thread has acquired is released before the thread is terminated.
while (1){
/* do some work for a while */
/* ... */
/* check if there is a cancellation request */
pthread testcancel();
}

Setting the cancellation state and type:

This can be set in the thread start function and again be restored back

void *fun (void * input)
{
int old_state;
int old_type;

/* Disable the cancelability and make type deferred*/
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_type);

/* Do some update operation */
...

/* Restore the old state and type */
pthread_setcancelstate (old_state, &old_state);
pthread_setcanceltype (old_type, &old_type);
}


Detach a thread:pthread_detach() API:

The pthread_detach () functions detaches the thread identified by th_id or detaches the calling thread by passing pthread_self() as the argument.

int pthread_detach (pthread_t th_id);
int pthread_detach (pthread_self());
  • The thread to be detached is the sole argument for this function.
  • When a detached terminates, its resources are automatically released without the need for another thread to join with the terminated thread.
  • The thread once detached cannot be joined later.
  • If the thread has not terminated, pthread_detached() does not cause it to terminates.
  • Either pthread_join() or pthread_detach() should be called for each thread that an application creates, so that system resources for the thread can be released.

Return Value:
It returns 0 on success and error number on failure.

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

void *fun (void *input)
{
   printf ("\n This is a thread function\n");
   /* DO some work */
}  

int main()
{
  pthread_t th_id;
  
  int res = pthread_create (&th_id, NULL, fun, (void*) NULL);
  if (res !=0)
  {
    printf ("\n Thread creation failed: %s\n", strerror(res));
    exit(0);
  } 
  
  res = pthread_detach (th_id);
  if (res !=0)
  {
     printf ("\n Thread detach failed: %s\n", strerror(res));
     exit(0);
  } 

  printf ("\n Thread detached successfully\n");
  return 0;
}
Output:
[aprakash@wtl-lview-6 thread]$ gcc thread_detach.c -lpthred
[aprakash@wtl-lview-6 thread]$ ./a.out
                                                                                                                                 
Thread detached successfully

Relevant Post:



Categories: Operating system (OS)

3 replies

Trackbacks

  1. Join a thread: pthread_join() API - Tech Access
  2. Thread create attributes: - Tech Access
  3. Index of Operating System - Tech Access

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: