3

I have a C pthread program that creates N threads in main that update a global variable. Main also calls pthread_join on all of these update threads to wait for them to finish. I also have 2 watcher threads that use pthread condition variables to check to see if the global variable goes above or below certain numbers, and if so, it kills all update threads and the other watcher thread. However, I'm having trouble with this last part..killing the other threads. My program does what it is supposed to do but never completes... it just gets stuck. Calling exit(0) at the end of each watcher thread works but I feel like that's too lazy of a solution, I'd really like to learn how to kill other threads from a separate thread and return to main.

Here's my code:

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

void *update(void *i);
void *watchIncrease();
void *watchDecrease();

//init globals
double marketValue;
int numThreads;
double *stocks;
double ceiling;
double floor_;
int flag;

pthread_t *threads;
pthread_t watchIncreaseThread;
pthread_t watchDecreaseThread;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t threshold_ceiling;
pthread_cond_t threshold_floor_;

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

    numThreads = atoi(argv[1]);
    int level = atoi(argv[2]);

    marketValue = 100 * numThreads;
    //initialize personal stocks for threads
    stocks = (double *) malloc(sizeof(double) * numThreads);
    int i;
    for(i = 0; i < numThreads; i++) stocks[i] = 100;
    //initialize floor/ceiling
    double percent = (double) level / 100;

    double cap = marketValue * percent;
    ceiling = marketValue + cap;
    floor_ = marketValue - cap;

    //seed rand()
    srand(time(NULL));
    //create threads
    pthread_cond_init(&threshold_ceiling,NULL);
    pthread_cond_init(&threshold_floor_,NULL);

    int rc = pthread_create(&watchIncreaseThread,NULL,watchIncrease,NULL);
    assert(rc == 0);
    rc = pthread_create(&watchDecreaseThread,NULL,watchDecrease,NULL);
    assert(rc == 0);

    threads = (pthread_t *)malloc(sizeof(pthread_t) * numThreads);
    assert(threads != NULL);
    for(i = 0; i < numThreads; i++){
        int rc = pthread_create(&threads[i],NULL,update,(void *)i);
        assert(rc == 0);
    }

    int j;
    for(j = 0; j < numThreads; j++){
        pthread_join(threads[j],NULL);
    }

    return 0;
}

void *update(void *i){

    int index = (int)i;
    double max = 2;
    double val;

    while(1){
        int rc = pthread_mutex_lock (&lock);
        assert(rc == 0);
        val = max * ((double)rand()/(double)RAND_MAX - 0.5);
        stocks[index] += val;

        marketValue += val;
        pthread_cond_signal (&threshold_ceiling);
        pthread_cond_signal (&threshold_floor_);
        pthread_mutex_unlock(&lock);

    }

}

void *watchIncrease(){

    int rc = pthread_mutex_lock(&lock);
    assert(rc == 0);
    while(marketValue < ceiling){
        pthread_cond_wait(&threshold_ceiling, &lock);
    }
    printf("Market Up to %.2f\n",marketValue);
    int i;
    double sum = 0;
    for(i = 0; i < numThreads; i++){
        sum += stocks[i];
    }
    printf("Total Market Price of %d stocks: %.2f\n",numThreads,sum);
    for(i = 0; i < numThreads; i++){
        rc = pthread_cancel(threads[i]);
        assert(rc == 0);
    }
    pthread_cancel(watchDecreaseThread);
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);

    //exit(0);
}

void *watchDecrease(){

    int rc = pthread_mutex_lock(&lock);
    assert(rc == 0);
    while(marketValue > floor_){
        pthread_cond_wait(&threshold_floor_, &lock);
    }
    printf("Market Down to %.2f\n",marketValue);
    int i;
    double sum = 0;
    for(i = 0; i < numThreads; i++){
        sum += stocks[i];
    }
    printf("Total Market Price of %d stocks: %.2f\n",numThreads,sum);
    for(i = 0; i < numThreads; i++){
        rc = pthread_cancel(threads[i]);
        assert(rc == 0);
    }
    pthread_cancel(watchIncreaseThread);
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);
    //exit(0);


}
Dylan
  • 89
  • 4
  • 1
    First of, this is a bad idea. Second, the `while`-loop in `update()` contains no functions specified as cancellation-points for deferred cancellation by `pthread_cancel()`. You could decide to enable asynchronous canellation, but the sane(r) solution would be to add a `pthread_testcancel()` after the `pthread_mutex_unlock()`. – EOF Apr 14 '16 at 19:39
  • Just to add, asynchronous cancellation is definitely a bad idea in this case, as neither `pthread_mutex_lock()`, `pthread_mutex_unlock()` or `pthread_cond_signal()` are async-cancel safe ( https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html ). As said, a `pthread_testcancel()` at the beginning or end of the `while`-loop (where the mutex is not locked) would be the better way to go. – sonicwave Apr 19 '16 at 15:55

1 Answers1

0

pthread_cancel is discouraged, instead the proper way of doing this is replacing while(1){ by while(!killed[index]){. Then, to kill a thread you set killed[index]. If you have C11 compiler, use http://en.cppreference.com/w/c/atomic/atomic_flag, otherwise you may need a pthread_mutex to protect the killed array.

Example: https://gustedt.wordpress.com/2012/01/22/simple-c11-atomics-atomic_flag/

Giovanni Funchal
  • 8,934
  • 13
  • 61
  • 110