renamed some signal handling functions in the App class
This commit is contained in:
		| @ -102,6 +102,7 @@ struct qse_cli_data_t | |||||||
| 	const qse_char_t** optsta; | 	const qse_char_t** optsta; | ||||||
| 	const qse_char_t*  optasn; | 	const qse_char_t*  optasn; | ||||||
| 	qse_cli_opt_t*     opts; | 	qse_cli_opt_t*     opts; | ||||||
|  | 	void*              ctx; | ||||||
|  |  | ||||||
| }; | }; | ||||||
| typedef struct qse_cli_data_t qse_cli_data_t; | typedef struct qse_cli_data_t qse_cli_data_t; | ||||||
|  | |||||||
| @ -43,12 +43,11 @@ public: | |||||||
|  |  | ||||||
| 	enum SignalState | 	enum SignalState | ||||||
| 	{ | 	{ | ||||||
| 		SIGNAL_UNHANDLED, | 		SIGNAL_NEGLECTED, // signal is unhandled at the system level. | ||||||
| 		SIGNAL_ACCEPTED, | 		SIGNAL_ACCEPTED,  // on_signal callback is triggered | ||||||
| 		SIGNAL_IGNORED // handled but ignored | 		SIGNAL_DISCARDED  // handled but doesn't trigger the on_signal callback | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  |  | ||||||
| 	App (Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT; | 	App (Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT; | ||||||
| 	virtual ~App () QSE_CPP_NOEXCEPT; | 	virtual ~App () QSE_CPP_NOEXCEPT; | ||||||
|  |  | ||||||
| @ -65,15 +64,21 @@ public: | |||||||
| 	virtual void on_signal (int sig) { } | 	virtual void on_signal (int sig) { } | ||||||
|  |  | ||||||
| 	SignalState getSignalSubscription (int sig) const; | 	SignalState getSignalSubscription (int sig) const; | ||||||
| 	int setSignalSubscription (int sig, SignalState ss); | 	int setSignalSubscription (int sig, SignalState ss, bool ignore_if_unhandled = false); | ||||||
|  |  | ||||||
| 	int subscribeToSignal (int sig, bool accept) | 	int acceptSignal (int sig) | ||||||
| 	{ | 	{ | ||||||
| 		return this->setSignalSubscription (sig, (accept? SIGNAL_ACCEPTED: SIGNAL_IGNORED)); | 		return this->setSignalSubscription(sig, SIGNAL_ACCEPTED); | ||||||
| 	} | 	} | ||||||
| 	int unsubscribeFromSignal (int sig) |  | ||||||
|  | 	int discardSignal (int sig) | ||||||
| 	{ | 	{ | ||||||
| 		return this->setSignalSubscription (sig, SIGNAL_UNHANDLED); | 		return this->setSignalSubscription(sig, SIGNAL_DISCARDED); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	int neglectSignal (int sig) | ||||||
|  | 	{ | ||||||
|  | 		return this->setSignalSubscription(sig, SIGNAL_NEGLECTED); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	typedef void (*SignalHandler) (int sig); | 	typedef void (*SignalHandler) (int sig); | ||||||
| @ -96,7 +101,7 @@ private: | |||||||
|  |  | ||||||
| 	struct _SigLink | 	struct _SigLink | ||||||
| 	{ | 	{ | ||||||
| 		_SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(SIGNAL_UNHANDLED) {} | 		_SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(SIGNAL_NEGLECTED) {} | ||||||
| 		App* _prev; | 		App* _prev; | ||||||
| 		App* _next; | 		App* _next; | ||||||
| 		SignalState _state; | 		SignalState _state; | ||||||
| @ -105,15 +110,12 @@ private: | |||||||
| 	_SigLink _sig[QSE_NSIGS];  | 	_SigLink _sig[QSE_NSIGS];  | ||||||
| 	long int _guarded_child_pid; | 	long int _guarded_child_pid; | ||||||
|  |  | ||||||
| protected: |  | ||||||
| 	static int set_signal_handler_no_mutex (int sig, SignalHandler sighr); | 	static int set_signal_handler_no_mutex (int sig, SignalHandler sighr); | ||||||
| 	static int unset_signal_handler_no_mutex (int sig, int ignore); | 	static int unset_signal_handler_no_mutex (int sig, int ignore); | ||||||
|  | 	int set_signal_subscription_no_mutex (int sig, SignalState reqstate, bool ignore_if_unhandled); | ||||||
| 	int set_signal_subscription_no_mutex (int sig, SignalState reqstate); |  | ||||||
|  |  | ||||||
| 	void on_guard_signal (int sig); | 	void on_guard_signal (int sig); | ||||||
| 	static void handle_signal (int sig); | 	static void handle_signal (int sig); | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| ///////////////////////////////// | ///////////////////////////////// | ||||||
|  | |||||||
| @ -85,8 +85,19 @@ App::~App () QSE_CPP_NOEXCEPT | |||||||
| 	SigScopedMutexLocker sml(g_app_mutex); | 	SigScopedMutexLocker sml(g_app_mutex); | ||||||
| 	for (int i = 0; i < QSE_NSIGS; i++) | 	for (int i = 0; i < QSE_NSIGS; i++) | ||||||
| 	{ | 	{ | ||||||
| 		this->set_signal_subscription_no_mutex (i, SIGNAL_UNHANDLED); | 		// if the signal handler has not been removed before this application | ||||||
|  | 		// is destroyed and this is the last application subscribing to  | ||||||
|  | 		// a signal, i arrange to set the signal handler to SIG_IGN as indicated by | ||||||
|  | 		// the third argument 'true' to set_signal_subscription_no_mutex(). | ||||||
|  | 		// if the signal is not ignored, the signal received after destruction | ||||||
|  | 		// of this application object may lead to program crash as the handler | ||||||
|  | 		// is still associated with the destructed object. | ||||||
|  |  | ||||||
|  | 		// if the signal handler has been removed, the following function | ||||||
|  | 		// actual does nothing. | ||||||
|  | 		this->set_signal_subscription_no_mutex (i, SIGNAL_NEGLECTED, true); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (this->_next_app) this->_next_app->_prev_app = this->_prev_app; | 	if (this->_next_app) this->_next_app->_prev_app = this->_prev_app; | ||||||
| 	if (this->_prev_app) this->_prev_app->_next_app = this->_next_app; | 	if (this->_prev_app) this->_prev_app->_next_app = this->_next_app; | ||||||
| 	if (this == g_app_top)  | 	if (this == g_app_top)  | ||||||
| @ -321,10 +332,10 @@ int App::unset_signal_handler_no_mutex(int sig, int ignore) | |||||||
| 		if (sl._state == App::SIGNAL_ACCEPTED)  | 		if (sl._state == App::SIGNAL_ACCEPTED)  | ||||||
| 		{ | 		{ | ||||||
| 			// the actual signal handler is called with the mutex locked. | 			// the actual signal handler is called with the mutex locked. | ||||||
| 			// it must not call subscribeToSingal() or unsubscribeFromSingal() | 			// it must not call acceptSingal()/discardSignal()neglectSingal() | ||||||
| 			// from within the handler. | 			// from within the handler. | ||||||
| 			if (app->_guarded_child_pid >= 0) app->on_guard_signal (sig); | 			if (app->_guarded_child_pid >= 0) app->on_guard_signal (sig); | ||||||
| 			else app->on_signal (sig); | 			app->on_signal (sig); | ||||||
| 		} | 		} | ||||||
| 		app = next; | 		app = next; | ||||||
| 	} | 	} | ||||||
| @ -335,28 +346,28 @@ void App::on_guard_signal (int sig) | |||||||
| 	::kill (this->_guarded_child_pid, sig); | 	::kill (this->_guarded_child_pid, sig); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| App::SignalState App::getSignalSubscription (int sig) const | App::SignalState App::getSignalSubscription (int sig) const | ||||||
| { | { | ||||||
| 	QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); | 	QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); | ||||||
| 	return this->_sig[sig]._state; | 	return this->_sig[sig]._state; | ||||||
| } | } | ||||||
|  |  | ||||||
| int App::setSignalSubscription (int sig, SignalState ss) | int App::setSignalSubscription (int sig, SignalState ss, bool ignore_if_unhandled) | ||||||
| { | { | ||||||
| 	QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); | 	QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); | ||||||
| 	SigScopedMutexLocker sml(g_app_mutex); | 	SigScopedMutexLocker sml(g_app_mutex); | ||||||
| 	return this->set_signal_subscription_no_mutex(sig, ss); | 	return this->set_signal_subscription_no_mutex(sig, ss, ignore_if_unhandled); | ||||||
| } | } | ||||||
|  |  | ||||||
| int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) | int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate, bool ignore_if_unhandled) | ||||||
| { | { | ||||||
| 	_SigLink& sl = this->_sig[sig]; | 	_SigLink& sl = this->_sig[sig]; | ||||||
|  |  | ||||||
| 	if (QSE_UNLIKELY(sl._state == reqstate)) return 0; // no change | 	if (QSE_UNLIKELY(sl._state == reqstate)) return 0; // no change | ||||||
|  |  | ||||||
| 	if (reqstate == SIGNAL_UNHANDLED) | 	if (reqstate == SIGNAL_NEGLECTED) | ||||||
| 	{ | 	{ | ||||||
| 		// accepted/ignored -> unhandled | 		// accepted/discarded -> neglected(unhandled) | ||||||
| 		QSE_ASSERT (g_app_sig[sig] != QSE_NULL); | 		QSE_ASSERT (g_app_sig[sig] != QSE_NULL); | ||||||
|  |  | ||||||
| 		if (g_app_sig[sig] == this)  | 		if (g_app_sig[sig] == this)  | ||||||
| @ -364,7 +375,9 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) | |||||||
| 			QSE_ASSERT (sl._prev == QSE_NULL); | 			QSE_ASSERT (sl._prev == QSE_NULL); | ||||||
| 			if (!sl._next)  | 			if (!sl._next)  | ||||||
| 			{ | 			{ | ||||||
| 				if (App::unset_signal_handler_no_mutex(sig, true) <= -1) return -1; | 				// this is the last application subscribing to the signal. | ||||||
|  | 				// let's get rid of the signal handler | ||||||
|  | 				if (App::unset_signal_handler_no_mutex(sig, ignore_if_unhandled) <= -1) return -1; | ||||||
| 			} | 			} | ||||||
| 			g_app_sig[sig] = sl._next; | 			g_app_sig[sig] = sl._next; | ||||||
| 		} | 		} | ||||||
| @ -375,9 +388,9 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) | |||||||
| 		sl._next = QSE_NULL; | 		sl._next = QSE_NULL; | ||||||
| 		sl._state = reqstate; | 		sl._state = reqstate; | ||||||
| 	} | 	} | ||||||
| 	else if (QSE_LIKELY(sl._state == SIGNAL_UNHANDLED)) | 	else if (QSE_LIKELY(sl._state == SIGNAL_NEGLECTED)) | ||||||
| 	{ | 	{ | ||||||
| 		// unhandled -> accepted/ignored | 		// neglected(unhandled) -> accepted/discarded | ||||||
| 		QSE_ASSERT (sl._prev == QSE_NULL && sl._next == QSE_NULL); | 		QSE_ASSERT (sl._prev == QSE_NULL && sl._next == QSE_NULL); | ||||||
|  |  | ||||||
| 		App* xapp = g_app_sig[sig]; | 		App* xapp = g_app_sig[sig]; | ||||||
| @ -400,7 +413,7 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) | |||||||
| 				// roll back  | 				// roll back  | ||||||
| 				g_app_sig[sig] = xapp; | 				g_app_sig[sig] = xapp; | ||||||
| 				if (xapp) xapp->_sig[sig]._prev = xapp_xprev; | 				if (xapp) xapp->_sig[sig]._prev = xapp_xprev; | ||||||
| 				sl._state = SIGNAL_UNHANDLED; | 				sl._state = SIGNAL_NEGLECTED; | ||||||
| 				sl._next = QSE_NULL; | 				sl._next = QSE_NULL; | ||||||
| 				QSE_ASSERT (sl._prev == QSE_NULL); | 				QSE_ASSERT (sl._prev == QSE_NULL); | ||||||
| 				return -1; | 				return -1; | ||||||
| @ -423,16 +436,6 @@ int App::guardProcess (const SignalSet& signals, const qse_mchar_t* proc_name) | |||||||
| { | { | ||||||
| 	SignalState old_ss[QSE_NSIGS]; | 	SignalState old_ss[QSE_NSIGS]; | ||||||
|  |  | ||||||
| 	while (1) |  | ||||||
| 	{ |  | ||||||
| 		pid_t pid = ::fork(); |  | ||||||
| 		if (pid == -1) return -1; |  | ||||||
| 		if (pid == 0)  |  | ||||||
| 		{ |  | ||||||
| 			::setpgid (0, 0); // change the process group id.  |  | ||||||
| 			break; // child |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 	for (int i = 0; i < QSE_NSIGS; i++) | 	for (int i = 0; i < QSE_NSIGS; i++) | ||||||
| 	{ | 	{ | ||||||
| 		if (signals.isSet(i))  | 		if (signals.isSet(i))  | ||||||
| @ -442,6 +445,27 @@ int App::guardProcess (const SignalSet& signals, const qse_mchar_t* proc_name) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	while (1) | ||||||
|  | 	{ | ||||||
|  | 		pid_t pid = ::fork(); | ||||||
|  | 		if (pid == -1) return -1; | ||||||
|  | 		if (pid == 0)  | ||||||
|  | 		{ | ||||||
|  | 			// child process | ||||||
|  | 			this->_guarded_child_pid = -1; | ||||||
|  |  | ||||||
|  | 			for (int i = 0; i < QSE_NSIGS; i++) | ||||||
|  | 			{ | ||||||
|  | 				if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			::setpgid (0, 0); // change the process group id.  | ||||||
|  | 			break;  | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// =============================================== | ||||||
|  | 		// the guardian(parent) process | ||||||
|  | 		// =============================================== | ||||||
| 		this->_guarded_child_pid = pid; | 		this->_guarded_child_pid = pid; | ||||||
|  |  | ||||||
| 		int status; | 		int status; | ||||||
| @ -455,17 +479,15 @@ int App::guardProcess (const SignalSet& signals, const qse_mchar_t* proc_name) | |||||||
| 			// ------------------------------------------------------ | 			// ------------------------------------------------------ | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		this->_guarded_child_pid = -1; |  | ||||||
| 		for (int i = 0; i < QSE_NSIGS; i++) |  | ||||||
| 		{ |  | ||||||
| 			if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (WIFEXITED(status)) | 		if (WIFEXITED(status)) | ||||||
| 		{ | 		{ | ||||||
| 			if (WEXITSTATUS(status) == 0)  | 			if (WEXITSTATUS(status) == 0)  | ||||||
| 			{ | 			{ | ||||||
| 				// the child has terminated normally and successfully. | 				// the child has terminated normally and successfully. | ||||||
|  | 				for (int i = 0; i < QSE_NSIGS; i++) | ||||||
|  | 				{ | ||||||
|  | 					if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); | ||||||
|  | 				} | ||||||
| 				return 0;  | 				return 0;  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -67,6 +67,8 @@ public: | |||||||
| 			case SIGINT: | 			case SIGINT: | ||||||
| 			case SIGTERM: | 			case SIGTERM: | ||||||
| 			case SIGHUP: | 			case SIGHUP: | ||||||
|  | 				// TODO: don't call stop() if this processs is a guardian | ||||||
|  | 				//       though it's no harm to call stop(). | ||||||
| 				qse_printf (QSE_T("requesting to stop server...app %p server %p - pid %d\n"), this, &this->server, (int)getpid()); | 				qse_printf (QSE_T("requesting to stop server...app %p server %p - pid %d\n"), this, &this->server, (int)getpid()); | ||||||
| 				this->server.stop(); | 				this->server.stop(); | ||||||
| 				break; | 				break; | ||||||
| @ -103,20 +105,20 @@ MyApp app2 (&heap_mmgr); | |||||||
| MyApp app3 (&heap_mmgr); | MyApp app3 (&heap_mmgr); | ||||||
| MyApp app4 (&heap_mmgr); | MyApp app4 (&heap_mmgr); | ||||||
|  |  | ||||||
| 	app.subscribeToSignal (SIGINT, true); | 	app.acceptSignal (SIGINT); | ||||||
| 	app.subscribeToSignal (SIGTERM, true); | 	app.acceptSignal (SIGTERM); | ||||||
|  |  | ||||||
| app4.subscribeToSignal (SIGINT, true);  | app4.acceptSignal (SIGINT); | ||||||
| app3.subscribeToSignal (SIGINT, true); | app3.acceptSignal (SIGINT); | ||||||
| app2.subscribeToSignal (SIGINT, true); | app2.acceptSignal (SIGINT); | ||||||
|  |  | ||||||
| 	int n = app.run(); | 	int n = app.run(); | ||||||
| 	app.subscribeToSignal (SIGTERM, false); | 	app.discardSignal (SIGTERM); | ||||||
| 	app.subscribeToSignal (SIGINT, false); | 	app.discardSignal (SIGINT); | ||||||
|  |  | ||||||
| app4.unsubscribeFromSignal (SIGINT);  | app4.neglectSignal (SIGINT);  | ||||||
| app3.unsubscribeFromSignal (SIGINT); | app3.neglectSignal (SIGINT); | ||||||
| app2.unsubscribeFromSignal (SIGINT); | app2.neglectSignal (SIGINT); | ||||||
| qse_printf (QSE_T("END OF %d\n"), (int)getpid()); | qse_printf (QSE_T("END OF %d\n"), (int)getpid()); | ||||||
|  |  | ||||||
| 	return n; | 	return n; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user