tbh1
tbh1

Reputation: 671

Pthreads and signals

I'm having a little trouble with pthreads. Basically, I want to catch a SIGINT and have all threads cleanup and exit. What I have (skeleton code):

main.c:

sig_atomic_t running;

void handler(int signal_number)
{
    running = 0;
}

int main(void)
{
    queue job_queue = new_job_queue();

    running = 1;

    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = &handler;
    sigaction(SIGINT, &sa, NULL);

    /* create a bunch of threads */
    init_threads(&job_queue);

    while(running) {
        /* do stuff */
    }

    cleanup();

    return (0);
}

threads.c

extern sig_atomic_t running;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t queue_count;

void init_threads(queue *q)
{
    int numthreads = 12; /* say */

    sem_init (&queue_count, 0, 0);

    pthread_t worker_threads[numthreads];

    int i;
    for(i=0;i<numthreads;i++)
        pthread_create(&worker_threads[i], NULL, &thread_function, q);
}

void * thread_function(void *args)
{
    pthread_detatch(pthread_self());

    queue *q = (queue *)args;

    while(running) {

        job *j = NULL;

        sem_wait(&queue_count);

        pthread_mutex_lock(&queue_mutex);
        j = first_job_in_queue(q);
        pthread_mutex_unlock(&queue_mutex);

        if(j) {
            /*do something*/
        }

    }

    return (NULL);
}

I am having little luck with this. Since you're not guarenteed which thread gets the signal I thought this was a good way to go. But I am having a problem where sem_wait() in threads.c is hanging, which is expected but not desired. The while(running) loop in threads.c seems redundant. Should I maybe do a pthread_kill() to all the threads from main? Any obvious problems with the above skeleton code? Is there a better/easier way to go about doing this?

Thanks.

Upvotes: 0

Views: 2352

Answers (4)

Jingsong Yang
Jingsong Yang

Reputation: 11

You can catch SIGINT in one thread, and use pthread_sigmask() to block SIGINT in all other threads, if SIGINT generated by some way, the signal will be delivered to the specified thread, that thread can call pthread_cancel() to cancel all other threads.

Upvotes: 1

Dave Rager
Dave Rager

Reputation: 8160

You shouldn't do a pthread_kill() if you don't have to. I'm not to familiar with pthread_detatch() but if you are wanting your main() function to wait for the threads to finish, it would probably be better if your cleanup() function did a pthread_join() on each thread id returned from pthread_create() to wait for each thread to exit normally.

Also, as far as I can tell, sem_wait() is hanging because your semaphore value is initialized to 0. If you want say at most 5 threads to access the shared resource at a time, initialize the semaphore to 5, i.e. sem_init(&queue_count, 0, 5).

Upvotes: 0

Eugene
Eugene

Reputation: 2918

What you can do is to call sem_post() from the handler until all threads are unlocked. In the thread function, immediately after sem_wait() you should check the value of the running variable and if it's zero break breom the while.

The code in the handler could be something like the following:

int sval;    
sem_getvalue(&queue_count, &sval);
while (sval < 0) {
    sem_post(&queue_count);
    sem_getvalue(&queue_count, &sval);
}

Of course return values should be verified for errors

Upvotes: 2

Jmoney38
Jmoney38

Reputation: 3314

You may want to consider calling pthread_join after each call to pthread_create. This will allow for your main thread to wait until all threads are done executing.

But maybe I'm misunderstanding slightly... Do you want to wait for all threads to finish, or simply wait for one to finish, and then stop all others immediately?

Upvotes: 0

Related Questions