views:

86

answers:

1

The following code is a simple thread game, that switches between threads causing the timer to decrease.

It works fine for 3 threads, causes and Abort(core dumped) for 4 threads, and causes a seg fault for 5 or more threads.

Anyone have any idea why this might be happening?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>

int volatile num_of_threads;
int volatile time_per_round;
int volatile time_left;
int volatile turn_id;
int volatile thread_running;
int volatile can_check;
void *  player (void * id_in){
 int id= (int)id_in;
 while(1){
  if(can_check){
   if (time_left<=0){
    break;
   }
   can_check=0;
   if(thread_running){
    if(turn_id==id-1){
     turn_id=random()%num_of_threads;
     time_left--;
    }
   }
   can_check=1;
  }
 }
 pthread_exit(NULL);
}
int main(int argc, char *args[]){
 int i;
 int buffer;
 pthread_t * threads =(pthread_t *)malloc(num_of_threads*sizeof(pthread_t));
 thread_running=0;
 num_of_threads=atoi(args[1]);
 can_check=0;
 time_per_round = atoi(args[2]);
 time_left=time_per_round;
 srandom(time(NULL));
 //Create Threads
 for (i=0;i<num_of_threads;i++){
  do{
  buffer=pthread_create(&threads[i],NULL,player,(void *)(i+1));
  }while(buffer == EAGAIN);
 }
 can_check=1;

 time_left=time_per_round;
 turn_id=random()%num_of_threads;
 thread_running=1;

 for (i=0;i<num_of_threads;i++){
  assert(!pthread_join(threads[i], NULL));
 }
 return 0;
}
+2  A: 

See below on why you should not depend on volatile in pthreads. However, your specific problem is probably because you malloc your pthread array, based on the num_of_threads variable before you've actually set num_of_thread from argv[]:

pthread_t *threads = (pthread_t *)malloc (num_of_threads * sizeof (pthread_t));
thread_running = 0;
num_of_threads = atoi (args[1]);

So there's a very good chance you're writing beyond the end of the threads array. The num_of_threads variable will probably be zero on start-up which means you're not allocating what you think you are. Move the allocation to after the extraction of the arguments and that should fix it.


And now, for your viewing pleasure :-), my original rant on the unsafe use of volatile, which I still stand by.

Do not rely on volatile to protect your shared variables. The correct way to do this is with the pthread_mutex_blab_blah_blah calls.

Of particular note, examine this code segment:

if (can_check) {
   if (time_left <= 0) {
    break;
   }
   // URK!!
   can_check=0;

URK!! is the point where your current thread may be switched out and another run, leading to the possibility that two threads can be running a critical section of code.

My advice is to forget the can_check altogether and just protect all the shared variables with a mutex, something like (from memory):

void *player (void * id_in) {
    int id = (int)id_in;
    while (1) {
        pthread_mutex_lock (&mutex);
        if (time_left <= 0) {
            pthread_mutex_unlock (&mutex);
            break;
        }
        if (thread_running) {
            if (turn_id == id-1) {
                turn_id = random() % num_of_threads;
                time_left--;
            }
        }
        pthread_mutex_unlock (&mutex);
    }
    pthread_exit(NULL);
}

Then put at file-level:

pthread_mutexattr_t mutexattr;  // prob. not needed at file level.
pthread_mutex_t mutex;

and, in main, before starting any other threads:

pthread_mutexattr_init (&mutexattr);
// Change attributes if needed.
pthread_mutex_init (&mutex, &mutex_attr);

// Then run all you other stuff here, make sure you've joined with all threads.

pthread_mutex_destroy (&mutex);

Oh yeah, although I haven't done it, you should also check the return codes for all those mutex calls. I'm not going to add that since it'll clog up the answer with unnecessary detail, but it's good practice.

paxdiablo
Yes, I have read this. However, this is for a specific school assignment.I've carefully printf debugged the issue, and it is surrounding only the pthread_exit/join calls... So I don't think its a memory issue.
MJewkes
Protip: printf tells you NOTHING in a multithreaded context.
Platinum Azure
He very clearly states what your problem is in the first paragraph of the answer... read it again.
SoapBox
@Soapbox, in all fairness, I state that _now_. My original answer was the railing against volatile which, while excellent advice, was not the specific cause of the problem.
paxdiablo
Sorry, I responded to an earlier version of the post.Thanks. I just wasted my afternoon on this dumbness.
MJewkes
It's never a waste if you learn something :-)
paxdiablo