LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   unistd.h pause() not returning on sigint (https://www.linuxquestions.org/questions/programming-9/unistd-h-pause-not-returning-on-sigint-4175677671/)

SoftSprocket 06-26-2020 09:16 AM

unistd.h pause() not returning on sigint
 
The manpage for pause says:
Quote:

pause() causes the calling process (or thread) to sleep until a signal
is delivered that either terminates the process or causes the invoca‐
tion of a signal-catching function.
portaudio.h indicates that for every Pa_Initialize there must be a Pa_Terminate or else, so I thought wrap it so a destructor calls terminate and then use pause to call exit on sigint. The destructor works fine under normal conditions but the idea here is that if I've put the code into a endless loop SIGINT should trigger an exit and allow for cleanup.

The issue with the code below is that pause does not return and it seems to me it should. Does anyone have some insight into this?

Code:

#include <portaudio.h>
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <atomic>
#include <cstring>
#include <thread>

class PortAudio {
        bool m_initialized;
        PaError m_error;
public:
        static std::atomic<bool> interrupted;   

        static void interrupt_signaled (int) {
            PortAudio::interrupted.store (true);
        }

        PortAudio () : m_initialized (false) {
                m_error = Pa_Initialize ();

                if(m_error == paNoError) {
                        m_initialized = true;
                }

        }

        virtual ~PortAudio () {
                if (m_initialized) {
                        m_error = Pa_Terminate ();
                        if (m_error != paNoError) {
                                std::cerr << "Failed to teminate portaudio: " << Pa_GetErrorText (m_error) << std::endl;
                        }
                }
               
                std::cout << "Portaudio terminated" << std::endl;
               
        }

        bool error () {
                return m_error != paNoError;
        }

        PaError errorCode () {
                return m_error;
        }

        void printLastError () {
                std::cerr << Pa_GetErrorText (m_error) << std::endl;
        }

};

std::atomic<bool> PortAudio::interrupted (false);

void wait_for_signal () {
        pause ();

        exit (EXIT_SUCCESS);
}

int main () {
        std::cout << Pa_GetVersionText () << "\n";

        struct sigaction sa;
    memset (&sa, 0, sizeof (sa));
    sa.sa_handler = PortAudio::interrupt_signaled;
    sigfillset (&sa.sa_mask);
    sigaction (SIGINT, &sa, NULL);

        PortAudio portAudio;

        if(portAudio.error()) {
                portAudio.printLastError();
                exit (EXIT_FAILURE);
        }

        std::cout << "Portaudio initialized\n";

        std::thread wait_thread (wait_for_signal);


        wait_thread.join ();

        return EXIT_SUCCESS;
}

g++ -std=c++14 audio_devices.cpp -o audio_devices -lportaudio -pthread

GazL 06-27-2020 04:23 AM

I'm not familiar with signal handling and std::thread under C++, but in general, a signal such as sigint could be sent to any thread in the foreground process that has not blocked the signal. My guess is the thread running wait_thread.join() is receiving the signal instead of the one running wait_for_signal().

I think you may need to use something like sigprocmask() to make sure the signal can only go to the desired thread. But, I'm not a C++ guy, only C, so there may be a more C++ish way of doing this sort of thing.

SoftSprocket 06-27-2020 09:17 AM

Thanks GazL. That was the answer.

My solution:
Code:

#include <portaudio.h>
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <atomic>
#include <cstring>
#include <thread>

class PortAudio {
        bool m_initialized;
        PaError m_error;
        static sigset_t  signal_mask;
        static bool signal_initialized;
        static pthread_t thr;
        static int thread_error;
       
        static void* wait_for_signal (void* arg) {
                if (thread_error == 0) {
                        int sig_caught;
                        thread_error = sigwait (&signal_mask, &sig_caught);
                        if (thread_error != 0) {
                                perror ("sigwait");
                        }               
                }
        }
public:
        static std::atomic<bool> interrupted;   

        static void interrupt_signaled (int) {
            PortAudio::interrupted.store (true);
        }

        PortAudio () : m_initialized (false) {
                m_error = Pa_Initialize ();

                if(m_error == paNoError) {
                        m_initialized = true;

                        if (!signal_initialized) {
                                sigemptyset (&signal_mask);
                                sigaddset (&signal_mask, SIGINT);
                                sigaddset (&signal_mask, SIGTERM);
                                thread_error = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
                                if (thread_error == 0) {
                                        thread_error = pthread_create (&thr, NULL, wait_for_signal, NULL);
                                        if (thread_error) {
                                                perror ("pthread_create");
                                        }
                                        thread_initialized = true;
                                } else {
                                        perror ("pthread_sigmask");
                                }
                        }

                }

               

        }

        virtual ~PortAudio () {
                if (m_initialized) {
                        m_error = Pa_Terminate ();
                        if (m_error != paNoError) {
                                std::cerr << "Failed to teminate portaudio: " << Pa_GetErrorText (m_error) << std::endl;
                        }
                }
               
                std::cout << "Portaudio terminated" << std::endl;
               
        }

        static void wait () {
                if (thread_error == 0) {
                        pthread_join (thr, NULL);
                }

        }

        bool error () {
                return m_error != paNoError;
        }

        PaError errorCode () {
                return m_error;
        }

        void printLastError () {
                std::cerr << Pa_GetErrorText (m_error) << std::endl;
        }


};

std::atomic<bool> PortAudio::interrupted (false);
bool PortAudio::signal_initialized = false;
sigset_t PortAudio::signal_mask;
pthread_t PortAudio::thr = 0;
int PortAudio::thread_error = 0;;



int main () {
        std::cout << Pa_GetVersionText () << "\n";

        struct sigaction sa;
        memset (&sa, 0, sizeof (sa));
        sa.sa_handler = PortAudio::interrupt_signaled;
        sigfillset (&sa.sa_mask);
        sigaction (SIGINT, &sa, NULL);

        PortAudio portAudio;

        if(portAudio.error()) {
                portAudio.printLastError();
                exit (EXIT_FAILURE);
        }

        std::cout << "Portaudio initialized\n";

        PortAudio::wait();

        return EXIT_SUCCESS;
}


SoftSprocket 06-27-2020 09:27 AM

While I was at it I thought I might as well encapsulate all the signal handling:
Code:

#include <portaudio.h>
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <atomic>
#include <cstring>
#include <pthread.h>

class PortAudio {
        bool m_initialized;
        PaError m_error;

        static struct sigaction sa;
       
        static sigset_t  signal_mask;
        static bool signal_initialized;
        static pthread_t thr;
        static int thread_error;
       
        static void* wait_for_signal (void* arg) {
                if (thread_error == 0) {
                        int sig_caught;
                        thread_error = sigwait (&signal_mask, &sig_caught);
                        if (thread_error != 0) {
                                perror ("sigwait");
                        }               
                }
        }
public:
        static std::atomic<bool> interrupted;   

        static void interrupt_signaled (int) {
            PortAudio::interrupted.store (true);
        }

        PortAudio () : m_initialized (false) {
                m_error = Pa_Initialize ();

                if(m_error == paNoError) {
                        m_initialized = true;

                        if (!signal_initialized) {
                                sa.sa_handler = PortAudio::interrupt_signaled;
                                sigfillset (&sa.sa_mask);
                                sigaction (SIGINT, &sa, NULL);
                               
                                sigemptyset (&signal_mask);
                                sigaddset (&signal_mask, SIGINT);
                                sigaddset (&signal_mask, SIGTERM);
                                thread_error = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
                                if (thread_error == 0) {
                                        thread_error = pthread_create (&thr, NULL, wait_for_signal, NULL);
                                        if (thread_error) {
                                                perror ("pthread_create");
                                        }

                                        signal_initialized = true;
                                } else {
                                        perror ("pthread_sigmask");
                                }
                        }

                }

               

        }

        virtual ~PortAudio () {
                if (m_initialized) {
                        m_error = Pa_Terminate ();
                        if (m_error != paNoError) {
                                std::cerr << "Failed to teminate portaudio: " << Pa_GetErrorText (m_error) << std::endl;
                        }
                }
               
                std::cout << "Portaudio terminated" << std::endl;
               
        }

        static void wait () {
                if (thread_error == 0) {
                        pthread_join (thr, NULL);
                }

        }

        bool error () {
                return m_error != paNoError;
        }

        PaError errorCode () {
                return m_error;
        }

        void printLastError () {
                std::cerr << Pa_GetErrorText (m_error) << std::endl;
        }


};

struct sigaction PortAudio::sa = { 0 };
std::atomic<bool> PortAudio::interrupted (false);
bool PortAudio::signal_initialized = false;
sigset_t PortAudio::signal_mask;
pthread_t PortAudio::thr = 0;
int PortAudio::thread_error = 0;;



int main () {
        std::cout << Pa_GetVersionText () << "\n";

        PortAudio portAudio;

        if(portAudio.error()) {
                portAudio.printLastError();
                exit (EXIT_FAILURE);
        }

        std::cout << "Portaudio initialized\n";

        PortAudio::wait();

        return EXIT_SUCCESS;
}



All times are GMT -5. The time now is 06:19 AM.