Reputation: 1839
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
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