0

Hello i am having a hard time understanding how a value is returned from a thread in c. I have this working example:

#define NTHREADS 4
void *neg (void * param) {
    int *l;
    l=(int *) param;
    int *r=(int *)malloc(sizeof(int));
    *r=-*l;
    return ((void *) r);
}

int main (int argc, char *argv[]) {
    pthread_t threads[NTHREADS];
    int arg[NTHREADS];
    int err;
    for(long i=0;i<NTHREADS;i++) {
    arg[i]=i;
    err=pthread_create(&(threads[i]),NULL,&neg,(void *) &(arg[i]));
    if(err!=0)
        error(err,"pthread_create");
    }
    for(int i=0;i<NTHREADS;i++) {
        int *r;
        err=pthread_join(threads[i],(void **)&r);
            printf("Resultat[%d]=%d\n",i,*r);
        free(r);
        if(err!=0)
            error(err,"pthread_join");
    }
    return(EXIT_SUCCESS);
}

What i find hard to understand is the following:

the function neg returns a pointer * r casted in (void *) pointing to a value in the heap. Thus returning an adress to the heap basically. Then in pthread_join we get that return value by doing &r (which by itself seems illogical? grab the adress of an adress?) then casting to a pointer of a pointer? Why do we do that?

Thanks for the help!

Adrien Pecher
  • 335
  • 1
  • 7
  • 14
  • "neg returns a pointer * r": The pointer value returned is the value of the variable `r`, not `*r`. – alk May 24 '16 at 12:42
  • "*grab the adress of an adress?*" no, you pass the address of the pointer variable `r`, this tells `pthread_join()` were to write the threads result to. – alk May 24 '16 at 12:45
  • `int *r = malloc(sizeof(int));` - no need to cast result of `malloc`. In `main()`: `void *r; int result; err=pthread_join(threads[i], &r); result = *(int*)r;` – user3078414 May 24 '16 at 13:31

2 Answers2

2

Consider this code, which is perfectly valid and prints "5 5 5 5".

int x = 5;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
printf("%d %d %d %d\n", x, *p, **pp, ***ppp);
return 0;

C allows you to have any depth of pointers, initialized with a corresponding number of address-of operators. In your example, you allocated an int * and the function must return a void **, so you have to dereference the result twice and cast it, which you accomplished by casting the int *.

So,"grab the adress of an adress?" Yup! Well, the address of a pointer, which may hold the address of an int, or another pointer.

jamieguinan
  • 1,640
  • 1
  • 10
  • 14
  • Side notes, your `error(...)` call looks incorrect, it should have 2 `int` parameters then a `char *`. Also, putting the loop variable inside the `for()` requires >= c99. – jamieguinan May 24 '16 at 12:46
  • 3
    An address does not have an address. On the other hand a pointer variable holding an address has. – alk May 24 '16 at 12:54
  • I see, so basically because pthread_join expects a void ** we reference it (thats the word right? to add a pointer on top) in the neg fct then dereference it in the main afterwards. Next question is then why does pthread_join require void ** ? EDIT:Seen this happen to other situations aswell like passing on a pointer to a struct to a function is also passed on as double pointer ** – Adrien Pecher May 24 '16 at 14:50
  • + actually the whole pthread_join syntax is weird, in almost all other pthread functions you would usually pass a ptr of type pthread_t * but in join you use a pthread_t for some reason – Adrien Pecher May 24 '16 at 14:59
  • Yeah, if you want to set a pointer by means of a function call, you pass the address of that pointer, and the function takes a `**` parameter. That's usually as weird as it gets, although I've seen code that uses `***`. Anyway `pthread_join` returns an int error code, but also wants the `void *` return value of the thread function, so it makes sense there. – jamieguinan May 24 '16 at 15:36
  • Thanks i get it all now, just one last question. Why does pthread_join take a pthread_t structure and not a pointer to one as argument like most of the other pthreads functions? (Possible exam question on my upcomming exam :p) – Adrien Pecher May 24 '16 at 15:53
  • I suppose it was a design decision. `pthread_t` is an opaque type, so you're not supposed to depend on it being an int/struct/pointer, but it is typically an unsigned long int. Except for `pthread_create`, the other pthreads functions pass the `pthread_t` parameter by value not pointer. Good luck on your exam. :) – jamieguinan May 24 '16 at 16:07
0

Then in pthread_join we get that return value by doing &r (which by itself seems illogical? grab the adress of an adress?) then casting to a pointer of a pointer? Why do we do that?

First thing you need to understand is that the thread function(s) do not return the value directly; they send the value to the pthreads library using pthread_exit() or returning a value (as in your example) and it's retrieved with a call to pthread_join() (by using the thread identifier). Since the return value is a pointer, you have to pass a pointer to pointer to retrieve it from pthread_join() function.

For the understanding purposes, consider this pseudo code:

/* thread */
void *neg(void *arg)
{
    return ptr; // let's say "ptr" is stored in the library as "retval_thread_id"
}

int pthread_joing(pthread_t thread_id, void **retval)
{
    *retval = retval_thread_id;
}

int main(void)
{
    rc = pthread_join(threads[i],(void **)&r);
}

So, how can you retrieve the pointer retval_thread_id from pthread_join() without using a pointer to pointer? This is nothing different than passing a ptr-to-ptr to a function and the function stores at the pointee of the passed pointer:

void func(int **p) 
{
    *p = malloc(10 * sizeof *p);
}

int main(void)
{
    int *p;
    func(&p);
}

By the way , all the casts you do (except the one in the call to pthread_join() -- You wouldn't this either if you declared r as void *r;.) are unnecessary. A void pointer can be assigned to any other data pointer in C without a cast.

P.P
  • 117,907
  • 20
  • 175
  • 238