Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game. |
Notices |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
 |
03-24-2011, 08:35 AM
|
#1
|
LQ Newbie
Registered: Mar 2011
Posts: 2
Rep:
|
Prevent process crashing on thread crash
Hello, everybody.
I'm kinda new in linux thread programming, so please don't judge me sternly. I want to know, is there any way to prevent the multi-thread process from crashing if some errors (say, segmentation faults) occur in one of its child threads? I've found pthread_sigmask() function, but that does not seem to work:
Code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
typedef struct thread_argument
{
sigset_t set;
int other_arg;
} thread_argument_t;
// Thread function with error, that will normally cause all process to die
void* thread_function(void* arg)
{
// Get arguments
thread_argument_t ta = *(static_cast<thread_argument_t*> (arg));
// block some of the signals from this thread
pthread_sigmask(SIG_BLOCK, &ta.set, NULL);
puts("--> Hello from buggy thread! We will wait 1 second and then crash you!");
sleep(1);
char* pointer;
*pointer = 2;
puts("--> Har Har Har! You should never see it");
return NULL;
}
// Signal handler that will inform about errors in threads
void* sig_handler(void *arg)
{
// Get arguments
thread_argument_t ta = *(static_cast<thread_argument_t*> (arg));
sigset_t *set = &ta.set;
int s, sig;
puts("-> Hello from signal handler thread! We will catch all the signals!");
for (;;)
{
s = sigwait(set, &sig);
if (s != 0)
{
printf("Signal handling thread got signal %d\n", sig);
}
}
return NULL;
}
int main(int argc, char** argv)
{
pthread_t thread1;
pthread_t thread_handler;
// set signal handler
thread_argument_t arg;
sigfillset(&arg.set);
arg.other_arg = 1;
puts("Creating signal handler");
pthread_create(&thread_handler, NULL, &sig_handler, &arg);
usleep(10);
puts("creating buggy thread");
pthread_create(&thread1, NULL, &thread_function, &arg);
usleep(10);
puts("all threads created");
for (int i =0; i < 50; i++)
{
printf("%d second in main thread\n", i);
sleep(1);
}
}
I even dont get "Signal handling thread got signal" message...
Thanks in advance.
|
|
|
03-24-2011, 09:25 AM
|
#2
|
Senior Member
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
|
Quote:
Originally Posted by lek.sys
I even dont get "Signal handling thread got signal" message...
Thanks in advance.
|
stdio buffers output. You need to add fflush(stdout); whenever you need to make sure the output is actually written. With standard error, it's of course fflush(stderr);.
There are quite a lot of issues with your code. I'll point out the most important ones. - SIGSEGV and SIGFPE are thread-directed.
They are only sent to the specific thread, not the entire process, so you cannot have a separate thread handling these signals.
- You should not block signals indefinitely.
There are certain nasty side effects, like losing signals or even zombifying your program that will/may result from blocking signals for too long.
Instead, set appropriate signal handlers, and only block signals for short critical sections.
Note that signal handlers are process-wide: if you set a signal handler, it will be set that way for all current and future threads.
- Only a small subset of functions are safe to use in a signal handler.
They are listed in man 7 signal, under the heading Async-signal-safe functions.
Specifically, stdio functions are not safe. printf/fprintf/fwrite/puts et al. usually do work, but there are no guarantees, and they can just fail. If you want your program to be reliable, only use the safe functions.
Segmentation fault is a serious problem, and it is recommended that your program stops its execution immediately. After all, if you scribble to random memory areas, you may already have scribbled over critical memory (even memory belonging to other threads of the same process), so you really are screwed if that happens.
If there is a weird case when you know segmentation fault may occur due to an unavoidable race condition, or other similar problem, and you know you're not really scribbling to random memory, just unallowed memory, you can use a SIGSEGV signal handler which calls pthread_exit():
Code:
#include <signal.h>
#include <string.h>
#include <pthread.h>
volatile sigatomic_t segmentation_violation = 0;
void sigsegv_handler(int signum, siginfo_t *info, void *data)
{
segmentation_violation = 1;
pthread_exit(NULL);
}
int main(void)
{
/* Set SIGSEGV handler. */
{ struct sigaction handler;
sigemptyset(&handler.sa_mask);
handler.sa_sigaction = &sigsegv_handler;
handler.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &handler, NULL) == -1) {
fprintf(stderr, "Cannot set SIGSEGV handler: %s.\n", strerror(errno));
exit(1);
}
}
/*
* ... Spawn threads and other stuff ...
*/
/* Has there been a segmentation violation? */
if (segmentation_violation)
fprintf(stderr, "Uh-oh. A segmentation violation has occurred.\n");
return 0;
}
Note how I've used the volatile sigatomic_t variable segmentation_violation as a flag to indicate a signal has been caught. The valid range of sigatomic_t may be as low as 0 to 127, and we cannot e.g. increase it safely without using some sort of locking; it's best to use it only as a flag. (The volatile keyword tells the compiler that it may change value at any time, so the compiler must each time read it again, and never cache the value.)
|
|
|
03-24-2011, 10:10 AM
|
#3
|
LQ Newbie
Registered: Mar 2011
Posts: 2
Original Poster
Rep:
|
2 Nominal Animal: Thanks a lot.
Quote:
stdio buffers output. You need to add fflush(stdout); whenever you need to make sure the output is actually written. With standard error, it's of course fflush(stderr);.
|
Yep, stupid mistake. That was just a sample dirty code to demonstrate the issue, but still it's a shame for me..
Quote:
Segmentation fault is a serious problem, and it is recommended that your program stops its execution immediately. After all, if you scribble to random memory areas, you may already have scribbled over critical memory (even memory belonging to other threads of the same process), so you really are screwed if that happens.
|
In my case sometimes segfaults occur in plugins (dynamically loaded shared objects, plugin code runs in separate thread) that are provided by various people. I just need the main program thread to point the module with error and may be notifying some of the top-level applications before crashing.
Again, thanks a lot for the sample, i'll try it.
|
|
|
03-24-2011, 11:32 AM
|
#4
|
Senior Member
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
|
Quote:
Originally Posted by lek.sys
In my case sometimes segfaults occur in plugins (dynamically loaded shared objects, plugin code runs in separate thread) that are provided by various people. I just need the main program thread to point the module with error and may be notifying some of the top-level applications before crashing.
|
That is of course an excellent idea.
There is one way you can do that quite safely with a SIGSEGV handler: Set up a parent or child process as a supervisor/error-reporter, and communicate with it via a pipe. Because it is a separate process, the plugins cannot harm the supervisor/error-reporter. Low-level I/O functions in unistd.h ( open, read, write) are safe to use in a signal handler, too.
Since the supervisor/error-reporter can simply wait in a blocking read from the pipe (with signal handlers to catch control signals and child processes exiting), it will not consume any CPU time, or much of any other resources.
Of course, usually it is better to sandbox the plugins into separate processes, and communicate with them via pipes or shared memory. If a browser does this, then e.g. a media player won't crash the browser itself; only the small visible media bit is lost. Perhaps you could consider something similar?
|
|
|
All times are GMT -5. The time now is 09:25 PM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|