How to return a value from pthread threads in C?
Asked Answered
C

11

76

I'am new to C and would like to play with threads a bit. I would like to return some value from a thread using pthread_exit()

My code is as follows:

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

void *myThread()
{
   int ret = 42;
   pthread_exit(&ret);
}

int main()
{
   pthread_t tid;
   void *status;
 
   pthread_create(&tid, NULL, myThread, NULL);
   pthread_join(tid, &status);

   printf("%d\n",*(int*)status);   
 
   return 0;
}

I would expect the program output "42\n" but it outputs a random number. How can I print the returned value?

It seems to be a problem that I am returning a pointer to a local variable. What is the best practice of returning/storing variables of multiple threads? A global hash table?

Caliche answered 12/2, 2010 at 11:33 Comment(2)
In response to the edit: I'd tend to use an array if I needed to provided multiple threads each with a place to write their results. If you don't know in advance an upper limit on how many threads you're going to create, then I'd normally consider that a problem. But in general any structure is fine so long as whoever starts the thread can ensure there's a place for the thread to store its result; the thread is told where to store it; and whoever joins the thread can recover the result and if necessary free it. If the thread exits with the same value it was passed as parameter, that can help.Whelk
For me this works fine. Somethings have changed in last 12 years.Syringe
G
72

You are returning the address of a local variable, which no longer exists when the thread function exits. In any case, why call pthread_exit? why not simply return a value from the thread function?

void *myThread()
{
   return (void *) 42;
}

and then in main:

printf("%d\n", (int)status);   

If you need to return a complicated value such a structure, it's probably easiest to allocate it dynamically via malloc() and return a pointer. Of course, the code that initiated the thread will then be responsible for freeing the memory.

Geostatics answered 12/2, 2010 at 11:39 Comment(9)
If you actually mean "why not", then the reason why not is that casting 42 to a pointer type is undefined behaviour. Of course if it does anything bad then it's one of those "it's a C implementation, Jim, but not as we know it" moments.Whelk
@Steve There are times when I will risk a little UB - this is one of them - the alternatives are so messy.Geostatics
Me too, but I wouldn't put the "portable anywhere" sticker on the box.Whelk
This works but I dont understand how. What does the cast (void*) mean? I am making a pointer from the number 42 and then casting it back to int? Does it have any drawbacks? Finally what is the pthread_exit() good for when I can use just return?Caliche
@Petr Those could be separate questions, but.. the cast says "store this in a void * and don't worry about it" - as Steve pointed out it is nationally a bad thing, but is a pragmatic solution and works with all C compilers in existence (tempting fate here). As for pthread_exit, I would personally never use it & would always prefer a return from the thread function - but that's just me.Geostatics
pthread_exit is like exit. It allows your thread to bail out early in the same way that a program can bail out early, and it can be called from any code in the thread, whereas to return you have to make your way back up to the thread entry point. The difference, of course, is that in a thread you'll most likely leak resources all over the place if you try to exit other than back out through the entry point.Whelk
Casting 42 to void * is not UB. It's implementation-defined. And real-world implementations define it as expected.Woodworking
The result could be a trap representation, though. I'm being cautious about traps in two ways. First, I think it's fairly reasonable just to avoid writing code that could read them, even in ways that it might be argued the standard allows because the relevant text mentions only lvalues. Second, I ignore the fact that in real life no implementation defines its types to have any, except maybe floating-point types. On the sliding scale from relying only on the standard, through common implementation practice, to false assumptions that bite, it's a pretty safe bet to be less cautious in both ways.Whelk
Be careful with casting ints to void * on your return value though. I know the Xenomai implementation of pthreads has a (void *)-2 as the PTHREAD_CANCELED return value.Spicate
A
87

Here is a correct solution. In this case tdata is allocated in the main thread, and there is a space for the thread to place its result.

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

typedef struct thread_data {
   int a;
   int b;
   int result;

} thread_data;

void *myThread(void *arg)
{
   thread_data *tdata=(thread_data *)arg;

   int a=tdata->a;
   int b=tdata->b;
   int result=a+b;

   tdata->result=result;
   pthread_exit(NULL);
}

int main()
{
   pthread_t tid;
   thread_data tdata;

   tdata.a=10;
   tdata.b=32;

   pthread_create(&tid, NULL, myThread, (void *)&tdata);
   pthread_join(tid, NULL);

   printf("%d + %d = %d\n", tdata.a, tdata.b, tdata.result);   

   return 0;
}
Addieaddiego answered 17/11, 2013 at 13:31 Comment(2)
This is the cleanest example here - I am surprised it's not more highly upvoted. In particular it shows that managing the storage at the calling level is the right thing to do - and also that the pointer can be used to pass more complex structures - both in and out.Dupe
what about free() and threads waiting in a for loop?Cohla
G
72

You are returning the address of a local variable, which no longer exists when the thread function exits. In any case, why call pthread_exit? why not simply return a value from the thread function?

void *myThread()
{
   return (void *) 42;
}

and then in main:

printf("%d\n", (int)status);   

If you need to return a complicated value such a structure, it's probably easiest to allocate it dynamically via malloc() and return a pointer. Of course, the code that initiated the thread will then be responsible for freeing the memory.

Geostatics answered 12/2, 2010 at 11:39 Comment(9)
If you actually mean "why not", then the reason why not is that casting 42 to a pointer type is undefined behaviour. Of course if it does anything bad then it's one of those "it's a C implementation, Jim, but not as we know it" moments.Whelk
@Steve There are times when I will risk a little UB - this is one of them - the alternatives are so messy.Geostatics
Me too, but I wouldn't put the "portable anywhere" sticker on the box.Whelk
This works but I dont understand how. What does the cast (void*) mean? I am making a pointer from the number 42 and then casting it back to int? Does it have any drawbacks? Finally what is the pthread_exit() good for when I can use just return?Caliche
@Petr Those could be separate questions, but.. the cast says "store this in a void * and don't worry about it" - as Steve pointed out it is nationally a bad thing, but is a pragmatic solution and works with all C compilers in existence (tempting fate here). As for pthread_exit, I would personally never use it & would always prefer a return from the thread function - but that's just me.Geostatics
pthread_exit is like exit. It allows your thread to bail out early in the same way that a program can bail out early, and it can be called from any code in the thread, whereas to return you have to make your way back up to the thread entry point. The difference, of course, is that in a thread you'll most likely leak resources all over the place if you try to exit other than back out through the entry point.Whelk
Casting 42 to void * is not UB. It's implementation-defined. And real-world implementations define it as expected.Woodworking
The result could be a trap representation, though. I'm being cautious about traps in two ways. First, I think it's fairly reasonable just to avoid writing code that could read them, even in ways that it might be argued the standard allows because the relevant text mentions only lvalues. Second, I ignore the fact that in real life no implementation defines its types to have any, except maybe floating-point types. On the sliding scale from relying only on the standard, through common implementation practice, to false assumptions that bite, it's a pretty safe bet to be less cautious in both ways.Whelk
Be careful with casting ints to void * on your return value though. I know the Xenomai implementation of pthreads has a (void *)-2 as the PTHREAD_CANCELED return value.Spicate
W
28

You've returned a pointer to a local variable. That's bad even if threads aren't involved.

The usual way to do this, when the thread that starts is the same thread that joins, would be to pass a pointer to an int, in a location managed by the caller, as the 4th parameter of pthread_create. This then becomes the (only) parameter to the thread's entry-point. You can (if you like) use the thread exit value to indicate success:

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

int something_worked(void) {
    /* thread operation might fail, so here's a silly example */
    void *p = malloc(10);
    free(p);
    return p ? 1 : 0;
}

void *myThread(void *result)
{
   if (something_worked()) {
       *((int*)result) = 42;
       pthread_exit(result);
   } else {
       pthread_exit(0);
   }
}

int main()
{
   pthread_t tid;
   void *status = 0;
   int result;

   pthread_create(&tid, NULL, myThread, &result);
   pthread_join(tid, &status);

   if (status != 0) {
       printf("%d\n",result);
   } else {
       printf("thread failed\n");
   }

   return 0;
}

If you absolutely have to use the thread exit value for a structure, then you'll have to dynamically allocate it (and make sure that whoever joins the thread frees it). That's not ideal, though.

Whelk answered 12/2, 2010 at 11:38 Comment(5)
I just found this out but according to pubs.opengroup.org/onlinepubs/9699919799/functions/… you dont need to use pthread_exit in your function call for the thread. "The normal mechanism by which a thread terminates is to return from the routine that was specified in the pthread_create() call that started it. The pthread_exit() function provides the capability for a thread to terminate without requiring a return from the start routine of that thread, thereby providing a function analogous to exit()." pthread_exit should be used for early termination.Solano
printf ("thread failed\n"); - I would instead print the error to stderr using fprintf.Vacate
comparing void * status to int zero will result in "warning: comparison between pointer and integer"Tremulant
@Steve Jessop I don't think pthread_join(tid, &status); is correct since the second parameter of pthread_join should be a double pointer as int pthread_join(pthread_t tid, void **thread_return);Impulsive
@amjoad: status has type void*, so &status has type void** as required.Whelk
B
10

I think you have to store the number on heap. The int ret variable was on stack and was destructed at the end of execution of function myThread.

void *myThread()
{
   int *ret = malloc(sizeof(int));
   if (ret == NULL) {
       // ...
   }
   *ret = 42;
   pthread_exit(ret);
}

Don't forget to free it when you don't need it

Another solution is to return the number as value of the pointer, like Neil Butterworth suggests.

Blondellblondelle answered 12/2, 2010 at 11:37 Comment(4)
It's stupid that pthread_exit accepts only pointer as parameter then :(Caliche
It's not "stupid", it's a limitation of the C type system. In order to return a parameter of arbitrary type back though the pthreads system you either need a Python-style object system, where variables don't have types, only values do, or else you need a lot of C++ template technology. In exactly the same way, you can only return void* from the thread entry point, you can't return (e.g.) a double.Whelk
Also don't forget that to check that malloc worked :-)Against
So in summary: the memory management has to be done manually with the child thread calling malloc to allocate a value on the heap and then returning a pointer to it, while on the other hand the parent thread is using pthread_join to retrieve that address to access the very same value and later on calling free up on it as a clean up?Wisecrack
M
4
#include<stdio.h>
#include<pthread.h>

void* myprint(void *x)
{
    int k = *((int *)x);
    printf("\n Thread created.. value of k [%d]\n", k);
    //k =11;
    pthread_exit((void *)k);
}

int main()
{
    pthread_t th1;
    int x =5;
    int *y;
    pthread_create(&th1, NULL, myprint, (void*)&x);
    pthread_join(th1, (void*)&y);
    printf("\n Exit value is [%d]\n", y);
}  
Manage answered 18/11, 2014 at 8:9 Comment(1)
Returning value of a local variable from thread function. without using global variable.Manage
O
2

You are returning a reference to ret which is a variable on the stack.

Operable answered 12/2, 2010 at 11:38 Comment(0)
A
2

Question : What is the best practice of returning/storing variables of multiple threads? A global hash table?

This totally depends on what you want to return and how you would use it? If you want to return only status of the thread (say whether the thread completed what it intended to do) then just use pthread_exit or use a return statement to return the value from the thread function.

But, if you want some more information which will be used for further processing then you can use global data structure. But, in that case you need to handle concurrency issues by using appropriate synchronization primitives. Or you can allocate some dynamic memory (preferrably for the structure in which you want to store the data) and send it via pthread_exit and once the thread joins, you update it in another global structure. In this way only the one main thread will update the global structure and concurrency issues are resolved. But, you need to make sure to free all the memory allocated by different threads.

Atmosphere answered 12/2, 2010 at 11:57 Comment(0)
P
2
#include <pthread.h>
#include <stdio.h>
#include <stdint.h>

void *myThread(void *args)
{
    return (void *)(intptr_t)42;
}

int main(void)
{
    pthread_t tid;
    void *status;
    int ret;

    pthread_create(&tid, NULL, myThread, NULL);
    ret = pthread_join(tid, &status);

    if (ret) {
        fprintf(stderr, "pthread_join() failed\n");
        return -1;
    }

    /* pthread_join() copies the exit status (namely 42) of the target thread
     * into the location pointed to by retval (namely &status), &status points
     * to void *, so we need to cast void * to int.
     */
    printf("%ld\n", (intptr_t)status);

    return 0;
}
Poirer answered 15/3, 2022 at 18:17 Comment(1)
Does one need to manually free the memory to which the status pointer is referring to prevent a memory leak, or how is the memory release of the return value taken care of otherwise?Wisecrack
S
1

if you're uncomfortable with returning addresses and have just a single variable eg. an integer value to return, you can even typecast it into (void *) before passing it, and then when you collect it in the main, again typecast it into (int). You have the value without throwing up ugly warnings.

Secondrate answered 8/10, 2011 at 16:14 Comment(1)
casting void* to int is implementation-dependent behavior and not defined by the standardResa
C
0

I did this:

void *myThread()
{
   int ret = 42;
   return (void*)(__intptr_t)ret;
}

int main()
{
   pthread_t tid;
   int status = 1;
 
   pthread_create(&tid, NULL, myThread, NULL);
   pthread_join(tid, (void*)(__intptr_t)&status);

   printf("%d\n",status);   
 
   return 0;
}
Calvin answered 24/9, 2023 at 6:36 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Therapeutic
J
0
#include<stdio.h>
#include<pthread.h>

void *fun(void *arg)
{
    int *val=(int*)arg;
    pthread_exit((void*)val);
}

int main()
{
    pthread_t id;
    int a=3;
    pthread_create(&id, NULL, fun,(void*)&a);
    void* ret;
    pthread_join(id, &ret);
    int exit_status = *((int *)ret);
    printf("%d\n",exit_status);
}
Josphinejoss answered 6/12, 2023 at 5:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.