Lancophone
Lancophone

Reputation: 310

Pthread runtime errors

I'm having trouble debugging the following program I wrote. The idea is to have two seperate threads; one thread executes a 5 second countdown while the other waits for key input from the user. Whichever thread completes first should cancel the sibling thread and exit the program. However, the following code just hangs.

Any help would be appreciated, but I would be most grateful for an explanation as to the problem.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep()

#define NUM_THREADS 2

// The stuct to be passed as an argument to the countdown routine
typedef struct countdown_struct {
pthread_t *thread;
signed int num_secs;
} CountdownStruct;

// Struct for passing to the input wait routine
typedef struct wait_struct {
pthread_t *thread;
int *key;
} WaitStruct;

// Countdown routine; simply acts as a timer counting down
void * countdown(void *args)
{
CountdownStruct *cd_str = (CountdownStruct *)args;
signed int secs = cd_str->num_secs;
printf("Will use default setting in %d seconds...", secs);
while (secs >= 0)
{
    sleep(1);
    secs -= 1;
    printf("Will use default setting in %d seconds...", secs);
}

// Cancel the other struct
pthread_cancel(*(cd_str->thread));
return NULL;
}

// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
do {
    c = getchar();
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;

// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}

int main(int argc, char **argv)
{
pthread_t wait_thread;
pthread_t countdown_thread;
pthread_attr_t attr;
int key=0;
long numMillis=5000;
int rc=0;
int status=0;

// Create the structs to be passe as paramaters to both routines
CountdownStruct *cd_str = (CountdownStruct *) malloc(sizeof(CountdownStruct));
if (cd_str == NULL)
{
    printf("Couldn't create the countdown struct. Aborting...");
    return -1;
}
cd_str->thread = &wait_thread;
cd_str->num_secs = 5;

WaitStruct *wait_str = (WaitStruct *) malloc(sizeof(WaitStruct));
if (wait_str == NULL)
{
    printf("Couldn't create the iput wait struct. Aborting...");
    return -1;
}
wait_str->thread = &countdown_thread;
wait_str->key = &key;

// Create the joinable attribute
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

// Create both threads
rc = pthread_create(&countdown_thread, &attr, countdown, (void *) cd_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
rc = pthread_create(&wait_thread, &attr, wait_for_input, (void *) wait_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }

// Destroy the pthread_attribute
pthread_attr_destroy(&attr);

// now join on the threads and wait for main
pthread_join(wait_thread, NULL);
pthread_join(countdown_thread, NULL);

// Call pthread_exit
pthread_exit(NULL);

// Free the function structs
free(cd_str);
free(wait_str);
}

Upvotes: 1

Views: 326

Answers (1)

dennis
dennis

Reputation: 231

Getchar is not required to be a cancellation point. Select and pselect are. Even if you want to continue to use a countdown thread you could still provide a cancellation point in the opposing thread by use of select.

I had reasonable behavior with the following modified wait_for_input()

    // Waits for the user to pass input through the tty
    void * wait_for_input(void *args)
    {
      WaitStruct *wait_str = (WaitStruct *) args;
      int c = 0;
      fd_set  readFds;
      int numFds=0;
      FD_ZERO(&readFds);

      do {
        struct timeval timeout={.tv_sec=8,.tv_usec=0};
        /* select here is primarily to serve as a cancellation
         * point.  Though there is a possible race condition
         * still between getchar() getting called right as the
         * the timeout thread calls cancel.(). 
         * Using the timeout option on select would at least 
         * cover that, but not done here while testing. 
         *******************************************************/  
        FD_ZERO(&readFds);
        FD_SET(STDOUT_FILENO,&readFds);
        numFds=select(STDOUT_FILENO+1,&readFds,NULL,NULL,&timeout);
        if(numFds==0 )  
        {
          /* must be timeout if no FD's selected */
          break;
        }
        if(FD_ISSET(STDOUT_FILENO,&readFds))
        {
          printf("Only get here if key pressed\n");
          c = getchar();
        }  
      } while (!(c == '1' || c == '2'));
      *(wait_str->key) = c;
      // Cancel the other thread
      pthread_cancel(*(wait_str->thread));
      return NULL;
    }

Upvotes: 2

Related Questions