void *

The designers of pthread_create did not want to limit the type of arguments the thread routine can take. So, passing a void pointer to the thread routine makes it possible to pass any type of argument. A void pointer can point to any type of variable. One must be a bit careful when using this kind of pointer though.

It is common practice, but perhaps a bit sloppy to write:

  int i;
  ....
  if ( ret = pthread_create(&thread_id[i], NULL, dotprod, (void *) (long) i) )


We interpret i as an address (casting it as a pointer to void). We need long on the 64-bit system, since void * is stored using eight bytes as is a variable of type long. (Had we declared i as long, (long) would not have been necessary.) In dotprod we cast back, i_am = (int) (long) arg;, yes, both casts are necessary (unless i_am is of type long, in which case we can skip (int)).

This works on the student system, since pointers (addresses) are stored using eight bytes (the same as a variable of type long) and because the compiler, gcc, supports the conversions back and forth. If this is not the case, we may get problems or at least complaints.

In older C-versions this was no problem, but in modern C one has to be more careful. Here is a quote from the C-FAQ (Question 4.14):
Pointer-to-integer and integer-to-pointer conversions are
implementation-defined ..., and there is no longer any guarantee that
pointers can be converted to integers and back, without change.
Here are the details for our compiler (i.e. for gcc).

The following may seem like a safer alternative:

  long i_tmp;
  for(i = 0; i < N_THREADS; i++) {

    i_tmp = i;  // Not safe to use &i directly

    // the cast (void *) &i_tmp is not necessary
    if( ret = pthread_create( &thread_id[i], NULL, dotprod, &i_tmp ) ) {
      printf ("Error in thread create\n");
      exit(1);
    }
  }
...


In dotprod

 // One may not dereference a void pointer, so we convert to a pointer to int first.
  i_am = * (long *) arg;  // same as *((long *) arg)


This does not work (on our system), due to a race condition. There is a delay in starting the threads and the loop will have exited when the i_am-assignments are made. So all threads will likely get i_am = 3 (the last value). It works if we have a pause in each iteration of the loop.

The following version works and does not give any complaints when compiling 32- or 64-bit systems (dotprod as above).

  long  args[N_THREADS];
   ...
  for(i = 0; i < N_THREADS; i++) {
    args[i] = i;
    if( ret = pthread_create( &thread_id[i], NULL, dotprod, &args[i] ) ) {
...




Back