m0hithreddy
m0hithreddy

Reputation: 1839

What is the best way of terminating POSIX multi-threaded application which has critical section code?

The application I am developing happens to be multi-threaded, and each thread has a critical section code. When user interrupts the application, I need to terminate the threads and save the execution state before terminating the application. To achieve this I coded some checks at random places in the thread function. Below is the minimal code that helps to understand the execution flow.

#include<pthread.h>
#include<signal.h>
#include<stdlib.h>

struct thread_data
{
    int quit;
    /* other data variables */
};

void* thread_func(void* data)
{

    for ( ; ; )
    {

        /* Non critical section code start */

        if (((struct thread_data*) data)->quit)   // checks at random places
            pthread_exit(NULL);

        /* end */

        if (((struct thread_data*) data)->quit)
            pthread_exit(NULL);

        /* Critical section code start */

            // Use data{} structure.

        /* end */

        if (((struct thread_data*) data)->quit)
                pthread_exit(NULL);
    }
}

int main()
{
    sigset_t sigmask;
    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGINT);

    pthread_sigmask(SIG_BLOCK, &sigmask, NULL);  // SIGINT is blocked by all the threads.


    struct thread_data* data = calloc(5, sizeof(struct thread_data));
    pthread_t tids[5];

    for (int i = 0; i < 5; i++)     // initialize the threads.
        pthread_create(tids + i, NULL, thread_func, (void*) (data + i));

    int signo;
    sigwait(&sigmask, &signo);  // wait for user interrupt.

    for (int i = 0; i < 5; i++)     // terminate threads.
    {
        data[i].quit = 1;
        pthread_join(tids[i], NULL);
    }

    /* Save the execution state */

        // Use data{} structure variable

    return 0;
}

But this method does not seem to be proficient, When the thread_func scales up, putting these checks at multiple places becomes tiresome. And a point to mention, I cant rely on signal disposition and calling pthread_exit() from signal handler as it is not async-signal-safe function. Is there a better way to achieve this ?

Upvotes: 5

Views: 384

Answers (1)

klutt
klutt

Reputation: 31409

Possibly not what you're looking for and not really something groundbreaking. But to remove some text (because I agree that it looks a bit messy) at least declare a pointer instead of casting all the time.

void* thread_func(void* data)
{
    struct thread_data *d = (struct thread_data*) data;

    if(d->quit) pthread_exit(NULL);

If you're doing these checks a lot, this would make it a lot cleaner. You could even make it even more clean with int *quit = &d->quit but maybe that's overkill.

Or use a function or a macro:

void maybe_quit(int x) 
{
    if(x) pthread_exit(NULL);
}

#define MAYBE_QUIT do {  if (((struct thread_data*) data)->quit) \
                             pthread_exit(NULL); }               \
                   while(0)

Not really an innovative approach, but it would certainly make the code look cleaner.

When I have to do loads of error checking and I find the probability of ever having to debug or profile the exit check function to be very low, then I choose a macro. I think this is much easier to read:

void* thread_func(void* data)
{
    for ( ; ; )
    {
        /* Non critical section code start */

        MAYBE_QUIT;

        /* end */

        MAYBE_QUIT;

        /* Critical section code start */

            // Use data{} structure.

        /* end */

        MAYBE_QUIT;
    }
}

The difference is actually quite big when you read the code over and over again. The brain can quite fast learn to just ignore those capital letters.

Upvotes: 2

Related Questions