1 /*
2  * SObjectizer-5
3  */
4 
5 /*!
6  * \since
7  * v.5.4.0
8  *
9  * \file
10  * \brief Helpers for handling unhandled exceptions from agent's event handlers.
11  */
12 
13 #include <so_5/impl/process_unhandled_exception.hpp>
14 
15 #include <so_5/environment.hpp>
16 
17 #include <so_5/details/abort_on_fatal_error.hpp>
18 #include <so_5/details/suppress_exceptions.hpp>
19 
20 namespace so_5 {
21 
22 namespace impl {
23 
24 namespace {
25 
26 /*!
27  * \since
28  * v.5.4.0
29  *
30  * \brief Switch agent to special state and deregister its cooperation.
31  *
32  * Calls abort() if an exception is raised during work.
33  */
34 void
switch_agent_to_special_state_and_deregister_coop(so_5::agent_t & a_exception_producer)35 switch_agent_to_special_state_and_deregister_coop(
36 	//! Agent who is the producer of the exception.
37 	so_5::agent_t & a_exception_producer ) noexcept
38 	{
39 		const coop_handle_t coop = a_exception_producer.so_coop();
40 		try
41 		{
42 			a_exception_producer.so_switch_to_awaiting_deregistration_state();
43 			a_exception_producer.so_environment().deregister_coop(
44 					coop,
45 					so_5::dereg_reason::unhandled_exception );
46 		}
47 		catch( const std::exception & x )
48 		{
49 			so_5::details::abort_on_fatal_error( [&] {
50 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
51 				{
52 					log_stream << "An exception '" << x.what()
53 							<< "' during deregistring cooperation "
54 							<< coop << " on unhandled exception"
55 							"processing. Application will be aborted.";
56 				}
57 			} );
58 		}
59 	}
60 
61 /*!
62  * \since
63  * v.5.4.0
64  *
65  * \brief Switch agent to special state and initiate stopping
66  * of SObjectizer Environment.
67  *
68  * Calls abort() if an exception is raised during work.
69  */
70 void
switch_agent_to_special_state_and_shutdown_sobjectizer(agent_t & a_exception_producer)71 switch_agent_to_special_state_and_shutdown_sobjectizer(
72 	//! Agent who is the producer of the exception.
73 	agent_t & a_exception_producer ) noexcept
74 	{
75 		try
76 		{
77 			a_exception_producer.so_switch_to_awaiting_deregistration_state();
78 			a_exception_producer.so_environment().stop();
79 		}
80 		catch( const std::exception & x )
81 		{
82 			so_5::details::abort_on_fatal_error( [&] {
83 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
84 				{
85 					log_stream << "An exception '" << x.what()
86 							<< "' during shutting down SObjectizer on unhandled "
87 							"exception processing. Application will be aborted.";
88 				}
89 			} );
90 		}
91 	}
92 
93 /*!
94  * \since
95  * v.5.4.0
96  *
97  * \brief Log unhandled exception from cooperation.
98  *
99  * \note
100  * This function is noexcept since v.5.6.0.
101  */
102 void
log_unhandled_exception(const std::exception & ex_to_log,agent_t & a_exception_producer)103 log_unhandled_exception(
104 	//! Raised and caught exception.
105 	const std::exception & ex_to_log,
106 	//! Agent who is the producer of the exception.
107 	agent_t & a_exception_producer ) noexcept
108 	{
109 		a_exception_producer.so_environment().call_exception_logger(
110 				ex_to_log,
111 				a_exception_producer.so_coop() );
112 	}
113 
114 } /* namespace anonymous */
115 
116 //
117 // process_unhandled_exception
118 //
119 void
process_unhandled_exception(current_thread_id_t working_thread_id,const std::exception & ex,agent_t & a_exception_producer)120 process_unhandled_exception(
121 	current_thread_id_t working_thread_id,
122 	const std::exception & ex,
123 	agent_t & a_exception_producer ) noexcept
124 	{
125 		log_unhandled_exception( ex, a_exception_producer );
126 
127 		auto reaction = a_exception_producer.so_exception_reaction();
128 		if( working_thread_id == null_current_thread_id() &&
129 				ignore_exception != reaction &&
130 				abort_on_exception != reaction )
131 		{
132 			so_5::details::abort_on_fatal_error( [&] {
133 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
134 				{
135 					log_stream << "Illegal exception_reaction code "
136 							"for the multithreadded agent: "
137 							<< static_cast< int >(reaction) << ". "
138 							"The only allowed exception_reaction for "
139 							"such kind of agents are ignore_exception or "
140 							"abort_on_exception. "
141 							"Application will be aborted. "
142 							"Unhandled exception '" << ex.what()
143 							<< "' from cooperation "
144 							<< a_exception_producer.so_coop();
145 				}
146 			} );
147 		}
148 
149 		if( abort_on_exception == reaction )
150 		{
151 			so_5::details::abort_on_fatal_error( [&] {
152 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
153 				{
154 					log_stream << "Application will be aborted due to unhandled "
155 							"exception '" << ex.what() << "' from cooperation "
156 							<< a_exception_producer.so_coop();
157 				}
158 			} );
159 		}
160 		else if( shutdown_sobjectizer_on_exception == reaction )
161 		{
162 			// Since v.5.6.2 all logging-related exceptions are suppressed here.
163 			so_5::details::suppress_exceptions( [&] {
164 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
165 				{
166 					log_stream << "SObjectizer will be shutted down due to "
167 							"unhandled exception '" << ex.what()
168 							<< "' from cooperation "
169 							<< a_exception_producer.so_coop();
170 				}
171 			} );
172 
173 			switch_agent_to_special_state_and_shutdown_sobjectizer(
174 					a_exception_producer );
175 		}
176 		else if( deregister_coop_on_exception == reaction )
177 		{
178 			// Since v.5.6.2 all logging-related exceptions are suppressed here.
179 			so_5::details::suppress_exceptions( [&] {
180 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
181 				{
182 					log_stream << "Cooperation "
183 							<< a_exception_producer.so_coop()
184 							<< " will be deregistered due to unhandled exception '"
185 							<< ex.what() << "'";
186 				}
187 			} );
188 
189 			switch_agent_to_special_state_and_deregister_coop(
190 					a_exception_producer );
191 		}
192 		else if( ignore_exception == reaction )
193 		{
194 			// Since v.5.6.2 all logging-related exceptions are suppressed here.
195 			so_5::details::suppress_exceptions( [&] {
196 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
197 				{
198 					log_stream << "Ignore unhandled exception '"
199 							<< ex.what() << "' from cooperation "
200 							<< a_exception_producer.so_coop();
201 				}
202 			} );
203 		}
204 		else
205 		{
206 			so_5::details::abort_on_fatal_error( [&] {
207 				SO_5_LOG_ERROR( a_exception_producer.so_environment(), log_stream )
208 				{
209 					log_stream << "Unknown exception_reaction code: "
210 							<< static_cast< int >(reaction)
211 							<< ". Application will be aborted. Unhandled exception '"
212 							<< ex.what() << "' from cooperation "
213 							<< a_exception_producer.so_coop();
214 				}
215 			} );
216 		}
217 	}
218 
219 void
process_unhandled_unknown_exception(current_thread_id_t working_thread_id,agent_t & a_exception_producer)220 process_unhandled_unknown_exception(
221 	current_thread_id_t working_thread_id,
222 	agent_t & a_exception_producer ) noexcept
223 	{
224 		// Just call process_unhandled_exception with dummy exception object.
225 		exception_t dummy{
226 				"an exception of unknown type is caught",
227 				rc_unknown_exception_type
228 		};
229 
230 		process_unhandled_exception(
231 				working_thread_id,
232 				dummy,
233 				a_exception_producer );
234 	}
235 
236 } /* namespace impl */
237 
238 } /* namespace so_5 */
239 
240