qn abt pthreads and casting int to void*

here is a partial code fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 6

void *thread_function(void *arg);

int main() {
    int res;
    pthread_t a_thread[NUM_THREADS];
    void *thread_result;
    int lots_of_threads;
    for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
        res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, (void *)&lots_of_threads);


Notice that the last argument to pthread_create is (void *)&lots_of_threads which causes a race condition.
Amending it to:
 
    res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, (void*)lots_of_threads);


makes it work fine but why does it not cause a race condition and when I cast an int to a void* what actually happens?
You get to pass an integral value to the thread. You have to decide on both sides (passing in the arg and using it within the thread function) what you're going to pass.

If you want to pass you number as an integer, then you're reinterpreting the arg to be an int.

If you want to pass the address of the integer, then you're reinterpreting the arg to be an int*.

I guess your thread function is expecting the former and you were passing the latter. This is the sort of thing that can happen when you bypass the language's type system.
Homework question ? Anyway...

The race condition happens when you pass the address of the lots_of_threads variable, because it can happen that the new thread reads the data at this address after it was already incremented in the next iteration of the for loop at line 14.

In the second case, you pass the value of lots_of_threads to the function, i.e. a copy is made before the creation of the thread, thus no race condition.

Regarding your question about the cast to (void*):
When you cast, nothing happens. The cast just tells the compiler that the value of lots_of_threads is a pointer, i.e. an address, instead of an integer. But the compiled program won't try to access this address (if it did, it would very likely crash, since the value of lots_of_threads does not represent a valid address).
The reason why this cast is done, is because you want to pass an int to the new thread, but pthread_create() wants a void*. So to avoid a "type mismatch" compilation error, you cast your int to a void*, and cast it back to int in thread_function().
Last edited on
what if I wanted to pass by value a datatype whose size is much larger than the sizeof void *, will it still work?
void* is not a type like that...it is a pointer to the data.
lloydchristmas759 is correct about the use of the parameter. The value threads see is indeterminate.

The reason why void* is used to pass the parameter is that you can pass anything one thing to a thread function. The idea is that you pack all your stuff into a record (struct), and pass the address of that. You're not constrained to passing just the address of an int, but the address of anything.
but what if I wanted to pass by value a datatype whose size is much larger than the sizeof void * as in the second part of the above code, will it still work?
but what if I wanted to pass by value a datatype whose size is much larger than the sizeof void * as in the second part of the above code, will it still work?


Nope. In this case you will have to pass a pointer and make sure there is no race condition. You can do it like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct big_struct {
    size_t thread_id;
    int a;
    int b;
    int c;
} my_struct;

...

for(my_struct.thread_id = 0; my_struct.thread_id< NUM_THREADS; my_struct.thread_id++) {
    struct big_struct* copy = malloc(sizeof(*copy));
    *copy = my_struct;
    res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, (void *)copy);
}


Note that the new threads will be responsible for freeing the malloc()ated memory.
Last edited on
Is there a way that I can pass an array of function pointers to the thread function and have the thread use them?
1
2
3
4
5
6
7
8
9
10
typedef void FuncToPass(int, long);  // ... or whatever your parameters are
typedef FuncToPass* FuncToPassPtr;

//...

FuncToPassPtr flist[] = { Function1, Function2, Function3 };

for(my_struct.thread_id = 0; my_struct.thread_id< NUM_THREADS; my_struct.thread_id++) {
    res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, flist);
}

... or something like that.
Last edited on
but how do I use/cast it in the thread function? when I tried to call 1 of the functions in the thread function, it gave me an error:
test.cpp:165: warning: pointer of type ‘void *’ used in arithmetic
test.cpp:165: error: ‘void*’ is not a pointer-to-object type
1
2
3
4
5
6
7
8
9
10
11
void * threadfunc(void *param)
{
    if (FuncToPassPtr* flist = reinterpret_cast<FuncToPassPtr*>(param)
    {
        for (size_t i = 0; i < 3; ++i)
            flist[i](i, i * i);

 // ...
    }
    return 0;
}

I've not compiled it. I will say that I'd never do it like that myself. I'd pass the context in a struct at the very least, but would more likely pass the context in a thread class.
Last edited on
Does programs which use pthreads run faster and in parallel on
1) multicore processors
2) processors with hyperthreading technology
as opposed to programs which are not multithreaded?
Last edited on
It depends on what the program is doing. It may even run faster as a single threaded app. There's no general comment.
Topic archived. No new replies allowed.