Mercurial > hg > freeDiameter
comparison libfdcore/core.c @ 1111:84162710428e
Cleanup to avoid sending several terminate event
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Mon, 13 May 2013 11:37:12 +0800 |
parents | 96f2051215c8 |
children | d87cee14b051 |
comparison
equal
deleted
inserted
replaced
1110:a731051d2e83 | 1111:84162710428e |
---|---|
46 /* gcrypt functions to support posix threads */ | 46 /* gcrypt functions to support posix threads */ |
47 #ifndef GNUTLS_VERSION_210 | 47 #ifndef GNUTLS_VERSION_210 |
48 GCRY_THREAD_OPTION_PTHREAD_IMPL; | 48 GCRY_THREAD_OPTION_PTHREAD_IMPL; |
49 #endif /* GNUTLS_VERSION_210 */ | 49 #endif /* GNUTLS_VERSION_210 */ |
50 | 50 |
51 /* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */ | |
52 static int is_ready = 0; | |
53 static pthread_mutex_t is_ready_mtx = PTHREAD_MUTEX_INITIALIZER; | |
54 static pthread_cond_t is_ready_cnd = PTHREAD_COND_INITIALIZER; | |
55 | |
56 static int signal_framework_ready(void) | |
57 { | |
58 TRACE_ENTRY(""); | |
59 CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) ); | |
60 is_ready = 1; | |
61 CHECK_POSIX( pthread_cond_broadcast( &is_ready_cnd ) ); | |
62 CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) ); | |
63 return 0; | |
64 } | |
65 | |
66 /* Thread that process incoming events on the main queue -- and terminates the framework when requested */ | 51 /* Thread that process incoming events on the main queue -- and terminates the framework when requested */ |
67 static pthread_t core_runner = (pthread_t)NULL; | 52 static pthread_t core_runner = (pthread_t)NULL; |
68 | 53 |
69 /* How the thread is terminated */ | 54 /* Signal extensions when the framework is completely initialized (they are waiting in fd_core_waitstartcomplete()) */ |
70 enum core_mode { | 55 static enum core_state { |
71 CORE_MODE_EVENTS, | 56 CORE_NOT_INIT, /* initial state */ |
72 CORE_MODE_IMMEDIATE | 57 CORE_LIBS_INIT, /* fd_core_initialize was called */ |
73 }; | 58 CORE_CONF_READY,/* Configuration was parsed, extensions are loaded */ |
59 CORE_RUNNING, /* Servers and clients are started, core_runner thread is running */ | |
60 CORE_SHUTDOWN, /* The framework is terminating all objects */ | |
61 CORE_TERM /* Shutdown complete. */ | |
62 } core_state = CORE_NOT_INIT; | |
63 static pthread_mutex_t core_mtx = PTHREAD_MUTEX_INITIALIZER; | |
64 static pthread_cond_t core_cnd = PTHREAD_COND_INITIALIZER; | |
65 | |
66 static enum core_state core_state_get(void) | |
67 { | |
68 enum core_state cur_state; | |
69 CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), ); | |
70 cur_state = core_state; | |
71 CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), ); | |
72 return cur_state; | |
73 } | |
74 | |
75 static void core_state_set(enum core_state newstate) | |
76 { | |
77 CHECK_POSIX_DO( pthread_mutex_lock( &core_mtx ), ); | |
78 LOG_D("Core state: %d -> %d", core_state, newstate); | |
79 core_state = newstate; | |
80 CHECK_POSIX_DO( pthread_cond_broadcast( &core_cnd ), ); | |
81 CHECK_POSIX_DO( pthread_mutex_unlock( &core_mtx ), ); | |
82 } | |
83 | |
84 static int core_state_wait(enum core_state waitstate) | |
85 { | |
86 int ret; | |
87 CHECK_POSIX( pthread_mutex_lock( &core_mtx )); | |
88 pthread_cleanup_push( fd_cleanup_mutex, &core_mtx ); | |
89 do { | |
90 CHECK_POSIX_DO(ret = pthread_cond_wait(&core_cnd, &core_mtx), break); | |
91 } while (waitstate > core_state); | |
92 pthread_cleanup_pop( 0 ); | |
93 CHECK_POSIX( pthread_mutex_unlock( &core_mtx )); | |
94 return ret; | |
95 } | |
96 | |
97 static void core_shutdown(void) | |
98 { | |
99 LOG_N( FD_PROJECT_BINARY " framework is stopping..."); | |
100 fd_log_threadname("fD Core Shutdown"); | |
101 | |
102 /* cleanups */ | |
103 CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ ); | |
104 CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ ); | |
105 CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ ); | |
106 CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ ); | |
107 | |
108 CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ ); | |
109 CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ ); | |
110 | |
111 GNUTLS_TRACE( gnutls_global_deinit() ); | |
112 | |
113 CHECK_FCT_DO( fd_conf_deinit(), ); | |
114 | |
115 CHECK_FCT_DO( fd_event_trig_fini(), ); | |
116 | |
117 fd_log_debug(FD_PROJECT_BINARY " framework is terminated."); | |
118 | |
119 fd_libproto_fini(); | |
120 | |
121 } | |
122 | |
74 | 123 |
75 static void * core_runner_thread(void * arg) | 124 static void * core_runner_thread(void * arg) |
76 { | 125 { |
77 if (arg && (*(int *)arg == CORE_MODE_IMMEDIATE)) | |
78 goto end; | |
79 | |
80 fd_log_threadname("fD Core Runner"); | 126 fd_log_threadname("fD Core Runner"); |
127 | |
128 core_state_wait(CORE_RUNNING); | |
81 | 129 |
82 /* Handle events incoming on the main event queue */ | 130 /* Handle events incoming on the main event queue */ |
83 while (1) { | 131 while (1) { |
84 int code; size_t sz; void * data; | 132 int code; size_t sz; void * data; |
85 CHECK_FCT_DO( fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data), break ); | 133 CHECK_FCT_DO( fd_event_get(fd_g_config->cnf_main_ev, &code, &sz, &data), break ); |
107 TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code); | 155 TRACE_DEBUG(INFO, "Unexpected event in the main event queue (%d), ignored.", code); |
108 } | 156 } |
109 } | 157 } |
110 | 158 |
111 end: | 159 end: |
112 TRACE_DEBUG(INFO, FD_PROJECT_BINARY " framework is stopping..."); | 160 core_shutdown(); |
113 fd_log_threadname("fD Core Shutdown"); | |
114 | |
115 /* cleanups */ | |
116 CHECK_FCT_DO( fd_servers_stop(), /* Stop accepting new connections */ ); | |
117 CHECK_FCT_DO( fd_rtdisp_cleanstop(), /* Stop dispatch thread(s) after a clean loop if possible */ ); | |
118 CHECK_FCT_DO( fd_peer_fini(), /* Stop all connections */ ); | |
119 CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ ); | |
120 | |
121 CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ ); | |
122 CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ ); | |
123 | |
124 GNUTLS_TRACE( gnutls_global_deinit() ); | |
125 | |
126 CHECK_FCT_DO( fd_conf_deinit(), ); | |
127 | |
128 CHECK_FCT_DO( fd_event_trig_fini(), ); | |
129 | |
130 fd_log_debug(FD_PROJECT_BINARY " framework is terminated."); | |
131 | |
132 fd_libproto_fini(); | |
133 | 161 |
134 return NULL; | 162 return NULL; |
135 } | 163 } |
136 | 164 |
137 /*********************************/ | 165 /*********************************/ |
145 | 173 |
146 /* Initialize the libfdcore internals. This also initializes libfdproto */ | 174 /* Initialize the libfdcore internals. This also initializes libfdproto */ |
147 int fd_core_initialize(void) | 175 int fd_core_initialize(void) |
148 { | 176 { |
149 int ret; | 177 int ret; |
178 | |
179 if (core_state_get() != CORE_NOT_INIT) { | |
180 fprintf(stderr, "fd_core_initialize() called more than once!\n"); | |
181 return EINVAL; | |
182 } | |
150 | 183 |
151 /* Initialize the library -- must come first since it initializes the debug facility */ | 184 /* Initialize the library -- must come first since it initializes the debug facility */ |
152 ret = fd_libproto_init(); | 185 ret = fd_libproto_init(); |
153 if (ret != 0) { | 186 if (ret != 0) { |
154 fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret)); | 187 fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret)); |
190 CHECK_FCT( fd_queues_init() ); | 223 CHECK_FCT( fd_queues_init() ); |
191 CHECK_FCT( fd_msg_init() ); | 224 CHECK_FCT( fd_msg_init() ); |
192 CHECK_FCT( fd_sess_start() ); | 225 CHECK_FCT( fd_sess_start() ); |
193 CHECK_FCT( fd_p_expi_init() ); | 226 CHECK_FCT( fd_p_expi_init() ); |
194 | 227 |
228 core_state_set(CORE_LIBS_INIT); | |
229 | |
195 /* Next thing is to parse the config, leave this for a different function */ | 230 /* Next thing is to parse the config, leave this for a different function */ |
196 return 0; | 231 return 0; |
197 } | 232 } |
198 | 233 |
199 /* Parse the freeDiameter.conf configuration file, load the extensions */ | 234 /* Parse the freeDiameter.conf configuration file, load the extensions */ |
227 LOG_N("%s", b ?: "Error during triggers dump..."); | 262 LOG_N("%s", b ?: "Error during triggers dump..."); |
228 } | 263 } |
229 | 264 |
230 free(buf); | 265 free(buf); |
231 | 266 |
267 core_state_set(CORE_CONF_READY); | |
268 | |
232 return 0; | 269 return 0; |
233 } | 270 } |
234 | 271 |
235 /* For threads that would need to wait complete start of the framework (ex: in extensions) */ | 272 /* For threads that would need to wait complete start of the framework (ex: in extensions) */ |
236 int fd_core_waitstartcomplete(void) | 273 int fd_core_waitstartcomplete(void) |
237 { | 274 { |
238 int ret = 0; | |
239 | |
240 TRACE_ENTRY(""); | 275 TRACE_ENTRY(""); |
241 | 276 |
242 CHECK_POSIX( pthread_mutex_lock( &is_ready_mtx ) ); | 277 return core_state_wait(CORE_RUNNING); |
243 pthread_cleanup_push( fd_cleanup_mutex, &is_ready_mtx ); | |
244 | |
245 while (!ret && !is_ready) { | |
246 CHECK_POSIX_DO( ret = pthread_cond_wait( &is_ready_cnd, &is_ready_mtx ), ); | |
247 } | |
248 | |
249 pthread_cleanup_pop( 0 ); | |
250 CHECK_POSIX( pthread_mutex_unlock( &is_ready_mtx ) ); | |
251 | |
252 return ret; | |
253 } | 278 } |
254 | 279 |
255 /* Start the server & client threads */ | 280 /* Start the server & client threads */ |
256 int fd_core_start(void) | 281 int fd_core_start(void) |
257 { | 282 { |
261 /* Start the peer state machines */ | 286 /* Start the peer state machines */ |
262 CHECK_FCT( fd_psm_start() ); | 287 CHECK_FCT( fd_psm_start() ); |
263 | 288 |
264 /* Start the core runner thread that handles main events (until shutdown) */ | 289 /* Start the core runner thread that handles main events (until shutdown) */ |
265 CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) ); | 290 CHECK_POSIX( pthread_create(&core_runner, NULL, core_runner_thread, NULL) ); |
266 | 291 |
267 /* Unlock threads waiting into fd_core_waitstartcomplete */ | 292 /* Unlock threads waiting into fd_core_waitstartcomplete */ |
268 CHECK_FCT( signal_framework_ready() ); | 293 core_state_set(CORE_RUNNING); |
269 | 294 |
270 /* Ok, everything is running now... */ | 295 /* Ok, everything is running now... */ |
271 return 0; | 296 return 0; |
272 } | 297 } |
273 | 298 |
274 | 299 |
275 /* Initialize shutdown of the framework. This is not blocking. */ | 300 /* Initialize shutdown of the framework. This is not blocking. */ |
276 int fd_core_shutdown(void) | 301 int fd_core_shutdown(void) |
277 { | 302 { |
278 if (core_runner != (pthread_t)NULL) { | 303 enum core_state cur_state = core_state_get(); |
279 /* Signal the framework to terminate */ | 304 |
305 if (cur_state < CORE_RUNNING) { | |
306 core_shutdown(); | |
307 } else if (cur_state == CORE_RUNNING) { | |
308 core_state_set(CORE_SHUTDOWN); | |
280 CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL) ); | 309 CHECK_FCT( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL) ); |
281 } else { | 310 } |
282 /* The framework was maybe not fully initialized (ex: tests) */ | 311 |
283 enum core_mode arg = CORE_MODE_IMMEDIATE; | 312 /* Other case, the framework is already shutting down */ |
284 (void) core_runner_thread(&arg); | 313 |
285 } | 314 return 0; |
286 | 315 } |
287 return 0; | 316 |
288 } | 317 |
289 | 318 /* Wait for the shutdown to be complete -- this must be called after fd_core_shutdown to reclaim some resources. */ |
290 | |
291 /* Wait for the shutdown to be complete -- this must always be called after fd_core_shutdown to reclaim some resources. */ | |
292 int fd_core_wait_shutdown_complete(void) | 319 int fd_core_wait_shutdown_complete(void) |
293 { | 320 { |
294 int ret; | 321 int ret; |
322 enum core_state cur_state = core_state_get(); | |
295 void * th_ret = NULL; | 323 void * th_ret = NULL; |
296 | 324 |
297 if (core_runner != (pthread_t)NULL) { | 325 if (cur_state == CORE_TERM) |
298 /* Just wait for core_runner_thread to complete and return gracefully */ | 326 return 0; |
299 ret = pthread_join(core_runner, &th_ret); | 327 |
300 if (ret != 0) { | 328 CHECK_FCT(core_state_wait(CORE_SHUTDOWN)); |
301 TRACE_ERROR( "Unable to wait for main framework thread termination: %s", strerror(ret)); | 329 |
302 return ret; | 330 /* Just wait for core_runner_thread to complete and return gracefully */ |
303 } | 331 CHECK_POSIX(pthread_join(core_runner, &th_ret)); |
304 } | 332 |
305 | 333 core_state_set(CORE_TERM); |
306 return 0; | 334 |
307 } | 335 return 0; |
308 | 336 } |
309 | 337 |
310 | 338 |
311 | 339 |
340 |