0

Consider this simple code:

void* threadFunction(void* arg) {

    int argument=(int)arg;

    printf("%d recieved\n", argument);

    return NULL;
}


int main(int argv, char* argc[]) {

    int error;
    int i=0;
    pthread_t thread;

    int argument_to_thread=0;

    if ((error=pthread_create(&thread, NULL, threadFunction, (void*)argument_to_thread))!=0) {
        printf("Can't create thread: [%s]\n", strerror(error));
        return 1;
    }

    pthread_join(thread, NULL);


    return 0;
}

This works, but two things bother me here.
First, I want to send more than one argument to threadFunction().
Of course, I can pass a pointer to an array, but what if I want to pass two arguments of different types? (say an int and char*) How can it be done?

The second thing that bothers me here, is the warnings I get when compiling the above...

test2.c: In function ‘threadFunction’:
test2.c:8:15: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  int argument=(int)arg;
               ^
test2.c: In function ‘main’:
test2.c:24:59: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
  if ((error=pthread_create(&thread, NULL, threadFunction, (void*)argument_to_thread))!=0) {
                                                           ^

Now, I can make this go away by doing this:

if ((error=pthread_create(&thread, NULL, threadFunction, (void*)&argument_to_thread))!=0) {
    printf("Can't create thread: [%s]\n", strerror(error));
    return 1;
}

But let's say I don't want to pass it by reference... Is there way to pass, say... an int, by value as an argument without the compiler warning me?

so.very.tired
  • 2,958
  • 4
  • 41
  • 69

3 Answers3

2

There are two ways to do this:

  1. Use malloc to obtain storage for the arguments structure, and have the new thread be responsible for calling free once it's done with the arguments. (Note: if pthread_create fails, you need to free this storage in the failure path.)

  2. Use a semaphore (or other synchronization, but a semaphore is by far the lightest).

Here's an example of the latter:

struct args {
    int a;
    char *b;
    sem_t sem;
};

void *thread_func(void *p)
{
    struct args *args = p;
    int a = args->a;
    char *b = args->b;
    sem_post(args->sem);
    ...
}

    /* in caller */
    struct args args;
    args.a = ...;
    args.b = ...;
    sem_init(&args.sem, 0, 0);
    pthread_create(&tid, 0, thread_func, &args);
    sem_wait(&args.sem);
    ...
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Not sure why this is downvoted, this is the right answer. – Crowman Jun 06 '14 at 21:00
  • 1
    Why do I need to protect the arg struct? or more precisely, **what** do I need to protect the arg struct from? who else is going to try and access the arg struct? the newly created thread will be the only one accessing it, won't it? – so.very.tired Jun 06 '14 at 21:07
  • This is unnecessarily complicated, given OP's example. – this Jun 06 '14 at 21:15
  • @so.very.tired: Unless the same function that called `pthread_create` also calls `pthread_join` before it returns (in which case, creating a thread to do the work was probably rather pointless), the new thread's access to the pointed-to argument is not synchronized with the end of the pointed-to argument's lifetime. In particular the function can return first, in which case the new thread will access junk. – R.. GitHub STOP HELPING ICE Jun 06 '14 at 21:20
  • @R.. So it actually protects me from accessing junk, rather than the danger of two threads accessing the same memory segment...? – so.very.tired Jun 06 '14 at 21:26
  • @so.very.tired: Without the synchronization, the code simply invokes undefined behavior by accessing an object after the end of its lifetime. In practice, what will happen is that you read a value from the parent thread's stack where the argument used to be located. This *might* still happen to contain the right value, or it might contain something new put there by whatever function the parent is now executing. But you cannot rely on it, and formally, since it's UB, anything could happen. – R.. GitHub STOP HELPING ICE Jun 06 '14 at 21:28
  • I see... Although, I'd probably won't need it in my case, since the pthread-creating function will not return until the thread is finished. You might say it is a bad practice, but for my (small) purposes, it is fine. I did learn something new about safety though... :) Thanks for the help. – so.very.tired Jun 06 '14 at 21:34
2

If you want to pass by value but make the warning go away, use this cast:

(void *)(uintptr_t)argument_to_thread

The intermediate cast through uintptr_t avoids the size mismatch. Similarly, when casting back, use:

(int)(uintptr_t)voidptr_arg

As noted in the comments, the above approach is not necessarily recommended because it relies on implementation-defined behavior. A fully portable approach, assuming you don't need the full range of int but only a small range, is:

static const char int_to_ptr_trick[MAXVAL];
void *ptr = int_to_ptr_trick + int_val;
int int_val = (char *)ptr - int_to_ptr_trick;
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Your code invokes undefined behaviour. http://stackoverflow.com/questions/9492798/using-intptr-t-instead-of-void – this Jun 06 '14 at 21:12
  • No it does not. The behavior of such conversions is implementation-defined, not undefined. – R.. GitHub STOP HELPING ICE Jun 06 '14 at 21:17
  • There is no guarantee that intptr_t is wider that int. I wouldn't use this code anywhere. – this Jun 06 '14 at 21:18
  • I don't necessarily recommend this approach unless you know that (1) the range of values you need to represent fits in `uintptr_t`, and (2) you know that the implementation-defined conversion from `uintptr_t` to `void *` is such that it has the property of preserving all values in the range you care about under round trips. These properties are true on every single POSIX or POSIX-like system I'm aware of, but I agree it's preferable not to rely on them. There's a similar approach however that is 100% portable. – R.. GitHub STOP HELPING ICE Jun 06 '14 at 21:22
1

Pass the address of the integer.

int argument = 123 ;
pthread_create(&thread, NULL, threadFunction, &argument)) ;

and in the thread function

 int argument= *(int*)arg ;

If you wish to pass more than one argument you can create a struct holding your values, using the same approach as with the above example.

You cannot pass an integer by value if the parameter for the functions is void*. You will have to provide the address of the variable or struct.

this
  • 5,229
  • 1
  • 22
  • 51