0

I am trying to find the cause of a segmentation fault error which is happening when I try to generate Signal (^C) when System function is involved.

Also I am trying to understand why I see only one print Thread1 cancelled and not 3 prints for all three threads? [Corrected the code for this point]

Below is the code for the testapp.c. This is a kind of stress test app I have made to test my utility program. The utility program is not the actual program which I am using but to demonstrate the use of semaphore used in it. It is executing my utility program using system() function.

Source code and output is listed below,

/*******  testapp.c  *****************/
/* gcc testapp.c -o testapp -pthread */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

#define SLEEP1  250
#define SLEEP2  250
#define SLEEP3  250
pthread_t   thread1;
pthread_t   thread2;
pthread_t   thread3;
static volatile sig_atomic_t isRunning = 1;

void signal_handler(int signal)
{
    int rc = 0;
    switch (signal)
    {
        case SIGINT:
        case SIGTERM:
        case SIGQUIT:
            printf("Signal generated, cancelling threads...");
            // Graceful shutdown
            //isRunning = 0; //commented to see the segmentation fault issue
            rc = pthread_cancel(thread1);
            if(rc != 0){
                printf("signal_handler:pthread_cancel-1 failed - %d (%m)\n", errno);
            }
            printf("Thread1 cancelled\n");
            rc = pthread_cancel(thread2);
            if(rc != 0){
                printf("signal_handler:pthread_cancel-2 failed - %d (%m)\n", errno);
            }
            printf("Thread2 cancelled\n");
            rc = pthread_cancel(thread3);
            if(rc != 0){
                printf("signal_handler:pthread_cancel-3 failed - %d (%m)\n", errno);
            }
            printf("Thread3 cancelled\n");
            break;
        default:
            break;
    }
}

void* thread1_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;
    do
    {
        printf("Requesting cmd1\n");
        ret = system("util -c:cmd1 -v 2>> /tmp/stderr.log");

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP1 * 1000;  //milliseconds
        ret = select(0, NULL, NULL, NULL, &timeout);
    }while(isRunning);

    printf("Exiting thread1...");
    pthread_exit((void*)0);
}

void* thread2_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;
    do
    {
        printf("Requesting cmd2\n");
        ret = system("util -c:cmd2 -v 2>> /tmp/stderr.log");

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP2 * 1000;  //milliseconds
        int ret = select(0, NULL, NULL, NULL, &timeout);
    }while(isRunning);

    printf("Exiting thread2...");
    pthread_exit((void*)0);
}

void* thread3_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;
    do
    {
        printf("Requesting cmd3\n");
        ret = system("util -c:cmd3 -v 2>> /tmp/stderr.log");

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP3 * 1000;  //milliseconds
        int ret = select(0, NULL, NULL, NULL, &timeout);
    }while(isRunning);

    printf("Exiting thread3...");
    pthread_exit((void*)0);
}

int main(int argc, char **argv){
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);

    printf("Starting threads...\n");
    pthread_create(&thread1, NULL, thread1_worker, (void*) NULL);
    pthread_create(&thread2, NULL, thread2_worker, (void*) NULL);
    pthread_create(&thread3, NULL, thread3_worker, (void*) NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
    return 0;
}

Output is below, The prints in the square brackets is the actual output of the program and not the util program above,

root@mydevice:~$ testapp 
Starting threads...
Requesting cmd1
Requesting cmd2
Requesting cmd3
cmd2:[05 05]
cmd1:[0]
cmd3:[00 00 00 00 00 ]
Requesting cmd2
Requesting cmd1
cmd2:[05 05]
Requesting cmd3
cmd1:[1]
cmd3:[00 00 00 00 00 ]
Requesting cmd2
Requesting cmd1
cmd2:[05 05]
Requesting cmd3
cmd1:[2]
cmd3:[00 00 00 00 00 ]
Requesting cmd2
Requesting cmd1
cmd2:[05 05]
Requesting cmd3
cmd1:[3]
cmd3:[00 00 00 00 00 ]
Requesting cmd2
Requesting cmd1
cmd2:[05 05]
Requesting cmd3
^Ccmd1:[4]
sh: line 1:   964 Segmentation fault      util -c:cmd3 -v 2>> /tmp/stderr.log
Requesting cmd2
cmd2:[05 05]
Requesting cmd1
Requesting cmd3
cmd1:[5]
cmd3:[00 00 00 00 00 ]
Requesting cmd2
cmd2:[05 05]
Requesting cmd1
Requesting cmd3
cmd3:[00 00 00 00 00 ]
Requesting cmd2
cmd1:[84213760]
Requesting cmd3
^Ccmd2:[00 00]  -----------------------------------------------> Signal
cmd3:[00 00 00 00 00 ]
Requesting cmd1
cmd1:[7]
Requesting cmd2
Requesting cmd3
cmd2:[05 05]
cmd3:[00 00 00 00 00 ]
Requesting cmd1
cmd1:[8]
^CSignal generated, cancelling threads...Thread1 cancelled ---->Signal
Segmentation fault
root@mydevice:~$ 
Community
  • 1
  • 1
user2669989
  • 308
  • 1
  • 2
  • 15
  • OT: `(void*)0` is expected to be the same as `NULL`. – alk Jul 06 '16 at 19:00
  • @alk: Thanks for your input, Its already there but I wanted to rule out any un-expected problem like segmentation fault. This may be in my util program also. – user2669989 Jul 06 '16 at 19:01
  • 2
    OT^2: Thou shalt not printf from signal handlers. – alk Jul 06 '16 at 19:02
  • @OP Do a [man 7 signal](http://man7.org/linux/man-pages/man7/signal.7.html) and look for the list of signal safe functions. – evaitl Jul 06 '16 at 21:03
  • the posted code does NOT cleanly compile. So your first step is to correct all the messages output by the compiler. As a hint: in the `main()` function, the `argc` and `argv` parameters are not used, so the signature for `main()` should be: `int main( void )`. for other functions: if a parameter is not used, then the first statement in the body of the function should be something similar to: `(void)data;` There are unused variables. There are variables set but not used like `ret`. – user3629249 Jul 07 '16 at 02:38
  • when calling system functions, like `pthread_create()`, always check the returned value to assure the function was successful – user3629249 Jul 07 '16 at 02:40
  • DO NOT call `printf()` from inside a signal handler However, `write()` is acceptable – user3629249 Jul 07 '16 at 03:38

2 Answers2

3

This was just a "typo" bug.

Change:

    pthread_create(&thread1, NULL, thread1_worker, (void*) NULL);
    pthread_create(&thread1, NULL, thread2_worker, (void*) NULL);
    pthread_create(&thread1, NULL, thread3_worker, (void*) NULL);

Into:

    pthread_create(&thread1, NULL, thread1_worker, (void*) NULL);
    pthread_create(&thread2, NULL, thread2_worker, (void*) NULL);
    pthread_create(&thread3, NULL, thread3_worker, (void*) NULL);
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • Ohhh. Nice catch. I was overlooking it and concentrating on signal handler. You resolved my one of two concern `Thread1 cancelled` raising this question. Thanks. – user2669989 Jul 06 '16 at 19:03
2

correcting the code, per the comments results in:

/*******  testapp.c  *****************/
/* gcc testapp.c -o testapp -pthread */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

#define SLEEP1  250
#define SLEEP2  250
#define SLEEP3  250

pthread_t   thread1;
pthread_t   thread2;
pthread_t   thread3;

static volatile sig_atomic_t isRunning = 1;

void signal_handler(int signal)
{
    int rc = 0;
    char buffer[1024];

    switch (signal)
    {
        case SIGINT:
        case SIGTERM:
        case SIGQUIT:
            write( 1, "Signal generated, cancelling threads...\n");
            // Graceful shutdown
            //isRunning = 0; //commented to see the segmentation fault issue
            rc = pthread_cancel(thread1);
            if( rc )
            {
                write(1, "signal_handler:pthread_cancel-1 failed\n" ):
            }

            write( 1, "Thread1 cancelled\n");


            rc = pthread_cancel(thread2);
            if( rc )
            {
                write( 1, "signal_handler:pthread_cancel-2 failed\n");
            }

            write( 1, "Thread2 cancelled\n");


            rc = pthread_cancel(thread3);
            if( rc )
            {
                write( 1, "signal_handler:pthread_cancel-3 failed\n");
            }

            "Thread3 cancelled\n");
            break;

        default:
            break;
    } // end switch
} // end function: signal_handler


void* thread1_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;

    (void)data;

    do
    {
        printf("Requesting cmd1\n");
        ret = system("util -c:cmd1 -v 2>> /tmp/stderr.log");
        if( ret )
        {
            perror( "system-util result" );
        }

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP1 * 1000;  //milliseconds
        select(0, NULL, NULL, NULL, &timeout);
    } while(isRunning);

    printf("Exiting thread1...\n");
    pthread_exit( NULL );
} // end function: thread1_worker


void* thread2_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;

    (void)data;

    do
    {
        printf("Requesting cmd2\n");
        ret = system("util -c:cmd2 -v 2>> /tmp/stderr.log");
        if( ret )
        {
            perror( "system-util result" );
        }

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP2 * 1000;  //milliseconds
        select(0, NULL, NULL, NULL, &timeout);
    }while(isRunning);

    printf("Exiting thread2...\n");
    pthread_exit((void*)0);
} // end function: thread2_worker


void* thread3_worker(void* data)
{
    struct timeval timeout;
    int ret = 0;

    (void)data;

    do
    {
        printf("Requesting cmd3\n");
        ret = system("util -c:cmd3 -v 2>> /tmp/stderr.log");
        if( ret )
        {
            perror( "system-util result" );
        }

        timeout.tv_sec  = 0;
        timeout.tv_usec = SLEEP3 * 1000;  //milliseconds
        select(0, NULL, NULL, NULL, &timeout);
    }while(isRunning);

    printf("Exiting thread3...\n");
    pthread_exit((void*)0);
} // end function: thread3_worker


int main( void )
{
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);

    printf("Starting threads...\n");
    pthread_create(&thread1, NULL, thread1_worker, (void*) NULL);
    pthread_create(&thread2, NULL, thread2_worker, (void*) NULL);
    pthread_create(&thread3, NULL, thread3_worker, (void*) NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
    return 0;
} // end function: main

when the code is run the following output is typical: (which continues to repeat with slight variations on the order of the statements:

system-util result: Success
system-util result: Success
system-util result: Success
Requesting cmd3
Requesting cmd2
Requesting cmd1

Then, since I have no util function the /tmp/stderr.log contains repeating statements of the following:

sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found
sh: 1: util: not found

and, unless the util function is marked executable AND is in the same directory at this program, it will fail.

changing util to ./util will work, if in the same directory without having to mark it as executable

The seg fault event is most likely from the util function rather than from the posted code.

further consideration should be given to calling the function: signal(), which per the man page is unreliable. The man page strongly suggests using: sigaction() instead.

user3629249
  • 16,402
  • 1
  • 16
  • 17
  • Thanks for your response. Actually `util` is stored on device `/usr/bin` so in the reference code above its like that. One point I want to mention here as you suspect the `util` might have the actual problem, `util` is also handling same number of signals as the `testapp`. I have shared the link to the `util` program in my original question. – user2669989 Jul 07 '16 at 16:42