Why does one of my ways of passing retval (void *) to pthread_exit() give unexpected results?

Issue

Today is my first day working with threads. I am struggling to understand why I am unable to pass the retval (type: void *) in both of the following ways below (i.e., only one way will give expected results as I illustrate/describe below).

I will provide a simplified version of my code:

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

static void *threadFunction(void *arg)
{
  printf("thread1: hello from thread...\n");
  char array_of_chars[] = "goodbye...";
  char *ptr_to_first_element_in_array_of_chars = array_of_chars;
  
  void *exitStatus_ptr;
  exitStatus_ptr = ptr_to_first_element_in_array_of_chars;

  printf("thread1: thread about to die...\n");
  //pthread_exit( exitStatus_ptr ); /** denote as Line 17 **/
  pthread_exit((void *) "goodbye..."); /** denote as Line 18 **/
}
int main(int argc, int *argv[]) //main thread
{

  pthread_t thread1;

  void *targetThread_exitStatus;
  int s; 

  s = pthread_create(&thread1, NULL, threadFunction, NULL); 

  if (s != 0) 
  {
    perror("pthread_create() error...");
  }
  
  s = pthread_join(thread1, &targetThread_exitStatus);
  
  if (s != 0)
  {
    perror("pthread_join() error...");
  }

  printf("main thread: retval from thread is: %s\n", (char*) targetThread_exitStatus);
  printf("main thread: thread about to die...\n");

  pthread_exit(NULL);

} 

When I compile the above source code on my Linux terminal:

gcc code.c -lpthread -o code

And then execute:

./code

I get the following output:

thread1: hello from thread...
thread1: thread about to die...
main thread: retval from thread is: goodbye...
main thread: thread about to die...

However, if I add a single-line comment to Line 18 above, and I remove the single-line comment from Line 17 above, the output is as follows:

thread1: hello from thread...
thread1: thread about to die...
main thread: retval from thread is: 
main thread: thread about to die...

As you can see, the retval (i.e., goodbye…) is not displayed.

Given that a string is stored as an array of characters in C, and arrays "decay" to pointers (i.e., a pointer to the first element in the array), why does the above code only produce expected results for one way of passing the retval and not the other (i.e., it does not produce expected results when the single-line comment is added to Line 18 and removed from Line 17)?

Solution

This code:

pthread_exit( exitStatus_ptr ); /** denote as Line 17 **/

passes a pointer to a local variable to pthread_exit. When you want to retreive that value in main(), that pointer is invalid as it points to something on the stack in threadFunction – which has been destroyed when pthread_join() returns. So you get undefined behavior.

The man page for pthread_exit mentions this explicitly:

The value pointed to by retval should not be located on the calling
thread’s stack, since the contents of that stack are undefined after
the thread terminates.

This on the other hand:

pthread_exit((void *) "goodbye..."); 

gives a pointer to a string literal to pthread_exit(). String literals live for the duration of the process, so there’s no issue using that pointer in main() after pthread_join

Answered By – nos

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published