LinuxQuestions.org
Review your favorite Linux distribution.
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 06-26-2020, 09:16 AM   #1
SoftSprocket
Member
 
Registered: Nov 2014
Posts: 305

Rep: Reputation: Disabled
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

Last edited by SoftSprocket; 06-26-2020 at 09:43 AM. Reason: fix quote tag
 
Old 06-27-2020, 04:23 AM   #2
GazL
LQ Guru
 
Registered: May 2008
Posts: 5,497
Blog Entries: 14

Rep: Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328Reputation: 3328
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.
 
1 members found this post helpful.
Old 06-27-2020, 09:17 AM   #3
SoftSprocket
Member
 
Registered: Nov 2014
Posts: 305

Original Poster
Rep: Reputation: Disabled
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;
}

Last edited by SoftSprocket; 06-27-2020 at 09:21 AM. Reason: fix indent
 
1 members found this post helpful.
Old 06-27-2020, 09:27 AM   #4
SoftSprocket
Member
 
Registered: Nov 2014
Posts: 305

Original Poster
Rep: Reputation: Disabled
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;
}

Last edited by SoftSprocket; 06-27-2020 at 09:34 AM. Reason: change thread header to pthread
 
  


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
Replacement for <unistd.h>, <sys/wait.h> and <signal.h> in Windows ? Kunsheng Programming 2 06-19-2009 07:18 AM
where to include unistd.h Ashok_mittal Linux - General 1 02-25-2008 07:52 AM
What is the packet for unistd.h sys/types.h ? spank Linux - Newbie 3 03-03-2007 11:32 AM
__syscall_return macro in unistd.h havokdrums Linux - Kernel 0 11-11-2006 05:19 AM
RH & HP4050N PCL - page, pause, page, pause, page andguent Linux - Hardware 0 11-10-2003 08:35 AM

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

All times are GMT -5. The time now is 05:45 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
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration