LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
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


Reply
  Search this Thread
Old 03-24-2011, 08:35 AM   #1
lek.sys
LQ Newbie
 
Registered: Mar 2011
Posts: 2

Rep: Reputation: 0
Question 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.
 
Old 03-24-2011, 09:25 AM   #2
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by lek.sys View Post
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.)
 
Old 03-24-2011, 10:10 AM   #3
lek.sys
LQ Newbie
 
Registered: Mar 2011
Posts: 2

Original Poster
Rep: Reputation: 0
2Nominal 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.
 
Old 03-24-2011, 11:32 AM   #4
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by lek.sys View Post
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?
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
What happen if my thread in process crash?? Linux_Kid_ Linux - Software 3 08-20-2009 07:53 AM
Limiting resource use to prevent crashing nozzer Linux - General 5 03-23-2009 05:59 AM
Get Process size and Thread count for a particular running process haseit Linux - Newbie 2 01-22-2009 11:09 PM
How to prevent linux image crash yyang Linux - Software 4 01-17-2006 07:45 PM
How to prevent runaway processes from crashing system? agtlewis Linux - General 14 10-13-2005 09:41 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:25 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration