Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * main_thread.cpp - Fawkes main thread 00004 * 00005 * Created: Thu Nov 2 16:47:50 2006 00006 * Copyright 2006-2011 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <baseapp/main_thread.h> 00025 00026 #include <core/threading/interruptible_barrier.h> 00027 #include <core/threading/mutex_locker.h> 00028 #include <core/exceptions/system.h> 00029 #include <core/version.h> 00030 #include <config/sqlite.h> 00031 #include <utils/time/clock.h> 00032 #include <utils/time/wait.h> 00033 #include <netcomm/fawkes/network_manager.h> 00034 #include <blackboard/local.h> 00035 00036 #include <aspect/manager.h> 00037 #include <plugin/manager.h> 00038 #include <plugin/loader.h> 00039 #include <plugin/net/handler.h> 00040 00041 #include <cstdio> 00042 #include <cstring> 00043 #include <cerrno> 00044 #include <cstdlib> 00045 #include <unistd.h> 00046 00047 #include <core/macros.h> 00048 00049 namespace fawkes { 00050 #if 0 /* just to make Emacs auto-indent happy */ 00051 } 00052 #endif 00053 00054 /** @class FawkesMainThread <baseapp/main_thread.h> 00055 * Fawkes default main thread. 00056 * This thread initializes all important stuff like the BlackBoard, 00057 * handles plugins and wakes up threads at defined hooks. 00058 * 00059 * @author Tim Niemueller 00060 */ 00061 00062 00063 /** Constructor. 00064 * @param config configuration to use 00065 * @param multi_logger basic multi logger to use, a network logger will be 00066 * added in the ctor. 00067 * @param thread_manager thread manager used to wakeup threads 00068 * @param plugin_manager plugin manager to load the desired plugins 00069 * @param load_plugins string with comma-separated list of names of plugins 00070 * to load on startup. 00071 * @param default_plugin additional default plugin name 00072 */ 00073 FawkesMainThread::FawkesMainThread(SQLiteConfiguration *config, 00074 MultiLogger *multi_logger, 00075 ThreadManager *thread_manager, 00076 PluginManager *plugin_manager, 00077 const char *load_plugins, 00078 const char *default_plugin) 00079 : Thread("FawkesMainThread") 00080 { 00081 __plugin_manager = plugin_manager; 00082 __thread_manager = thread_manager; 00083 __multi_logger = multi_logger; 00084 __sqlite_conf = config; 00085 __config = config; 00086 00087 __mainloop_thread = NULL; 00088 __mainloop_mutex = new Mutex(); 00089 __mainloop_barrier = new InterruptibleBarrier(__mainloop_mutex, 2); 00090 00091 __load_plugins = NULL; 00092 if (load_plugins) { 00093 __load_plugins = strdup(load_plugins); 00094 } 00095 00096 __default_plugin = NULL; 00097 if (default_plugin) { 00098 __default_plugin = strdup(default_plugin); 00099 } 00100 00101 /* Clock */ 00102 __clock = Clock::instance(); 00103 00104 __loop_start = new Time(__clock); 00105 __loop_end = new Time(__clock); 00106 try { 00107 __max_thread_time_usec = __config->get_uint("/fawkes/mainapp/max_thread_time"); 00108 } catch (Exception &e) { 00109 __max_thread_time_usec = 30000; 00110 __multi_logger->log_info("FawkesMainApp", 00111 "Maximum thread time not set, assuming 30ms."); 00112 } 00113 __max_thread_time_nanosec = __max_thread_time_usec * 1000; 00114 00115 __time_wait = NULL; 00116 try { 00117 __desired_loop_time_usec = 00118 __config->get_uint("/fawkes/mainapp/desired_loop_time"); 00119 if ( __desired_loop_time_usec > 0 ) { 00120 __time_wait = new TimeWait(__clock, __desired_loop_time_usec); 00121 } 00122 } catch (Exception &e) { 00123 __desired_loop_time_usec = 0; 00124 __multi_logger->log_info("FawkesMainApp", 00125 "Desired loop time not set, assuming 0"); 00126 } 00127 00128 __desired_loop_time_sec = (float)__desired_loop_time_usec / 1000000.f; 00129 00130 try { 00131 __enable_looptime_warnings = 00132 __config->get_bool("/fawkes/mainapp/enable_looptime_warnings"); 00133 if(!__enable_looptime_warnings) { 00134 __multi_logger->log_debug(name(), "loop time warnings are disabled"); 00135 } 00136 } catch(Exception &e) { 00137 __enable_looptime_warnings = true; 00138 } 00139 } 00140 00141 00142 /** Destructor. */ 00143 FawkesMainThread::~FawkesMainThread() 00144 { 00145 destruct(); 00146 } 00147 00148 00149 /** Destruct. 00150 * Mimics destructor, but may be called in ctor exceptions. 00151 */ 00152 void 00153 FawkesMainThread::destruct() 00154 { 00155 try { 00156 __sqlite_conf->try_dump(); 00157 } catch (CouldNotOpenFileException &e) { 00158 if (e.get_errno() == EACCES) { 00159 __multi_logger->log_warn("FawkesMainThread", "Cannot write to dump file, " 00160 "no write "); 00161 __multi_logger->log_warn("FawkesMainThread", "permission for file or " 00162 "directory. This"); 00163 __multi_logger->log_warn("FawkesMainThread", "usually happens if running " 00164 "with system-wide"); 00165 __multi_logger->log_warn("FawkesMainThread", "installed Fawkes as non-root " 00166 "user. Make"); 00167 __multi_logger->log_warn("FawkesMainThread", "configuration changes to the " 00168 "host-based"); 00169 __multi_logger->log_warn("FawkesMainThread", "database (set as non-default " 00170 "values)."); 00171 } else { 00172 __multi_logger->log_warn("FawkesMainThread", "Failed to dump default " 00173 "config (open), exception follows."); 00174 __multi_logger->log_warn("FawkesMainThread", e); 00175 } 00176 } catch (Exception &e) { 00177 __multi_logger->log_warn("FawkesMainThread", "Failed to dump default config, " 00178 "exception follows."); 00179 __multi_logger->log_warn("FawkesMainThread", e); 00180 } 00181 00182 if (__load_plugins) free(__load_plugins); 00183 if (__default_plugin) free(__default_plugin); 00184 00185 delete __time_wait; 00186 delete __loop_start; 00187 delete __loop_end; 00188 00189 delete __mainloop_barrier; 00190 delete __mainloop_mutex; 00191 } 00192 00193 /** Start the thread and wait until once() completes. 00194 * This is useful to assure that all plugins are loaded before assuming that 00195 * startup is complete. 00196 */ 00197 void 00198 FawkesMainThread::full_start() 00199 { 00200 __init_barrier = new Barrier(2); 00201 00202 start(false); 00203 00204 __init_barrier->wait(); 00205 delete(__init_barrier); 00206 __init_barrier = 0; 00207 } 00208 00209 void 00210 FawkesMainThread::once() 00211 { 00212 // if plugins passed on command line or in init options, load! 00213 if ( __load_plugins) { 00214 try { 00215 __plugin_manager->load(__load_plugins); 00216 } catch (Exception &e) { 00217 __multi_logger->log_error("FawkesMainThread", "Failed to load plugins %s, " 00218 "exception follows", __load_plugins); 00219 __multi_logger->log_error("FawkesMainThread", e); 00220 } 00221 } 00222 00223 // load extra default plugin given via init options 00224 try { 00225 if (__default_plugin && (strcmp("default", __default_plugin) != 0)) { 00226 __plugin_manager->load(__default_plugin); 00227 } 00228 } catch (PluginLoadException &e) { 00229 if (e.plugin_name() != __default_plugin) { 00230 // only print if name is not default, i.e. one of the plugins that 00231 // the default meta plugin 00232 __multi_logger->log_error("FawkesMainThread", "Failed to load default " 00233 "plugins, exception follows"); 00234 __multi_logger->log_error("FawkesMainThread", e); 00235 } 00236 } 00237 00238 // if no specific plugins were given to load, load the default plugin 00239 if (! __load_plugins) { 00240 try { 00241 __plugin_manager->load("default"); 00242 } catch (PluginLoadException &e) { 00243 if (e.plugin_name() != "default") { 00244 // only print if name is not default, i.e. one of the plugins that 00245 // the default meta plugin 00246 __multi_logger->log_error("FawkesMainThread", "Failed to load default " 00247 "plugins, exception follows"); 00248 __multi_logger->log_error("FawkesMainThread", e); 00249 } 00250 } catch (Exception &e) { 00251 __multi_logger->log_error("FawkesMainThread", "Failed to load default " 00252 "plugins, exception follows"); 00253 __multi_logger->log_error("FawkesMainThread", e); 00254 } 00255 } 00256 00257 if (__init_barrier) __init_barrier->wait(); 00258 } 00259 00260 void 00261 FawkesMainThread::set_mainloop_thread(Thread *mainloop_thread) 00262 { 00263 loopinterrupt_antistarve_mutex->lock(); 00264 __mainloop_mutex->lock(); 00265 __mainloop_barrier->interrupt(); 00266 __mainloop_thread = mainloop_thread; 00267 __mainloop_mutex->unlock(); 00268 loopinterrupt_antistarve_mutex->unlock(); 00269 } 00270 00271 00272 void 00273 FawkesMainThread::loop() 00274 { 00275 if ( ! __thread_manager->timed_threads_exist() ) { 00276 __multi_logger->log_debug("FawkesMainThread", "No timed threads exist, waiting"); 00277 try { 00278 __thread_manager->wait_for_timed_threads(); 00279 __multi_logger->log_debug("FawkesMainThread", "Timed threads have been added, " 00280 "running main loop now"); 00281 } catch (InterruptedException &e) { 00282 __multi_logger->log_debug("FawkesMainThread", "Waiting for timed threads interrupted"); 00283 return; 00284 } 00285 } 00286 00287 __plugin_manager->lock(); 00288 00289 try { 00290 if ( __time_wait ) { 00291 __time_wait->mark_start(); 00292 } 00293 __loop_start->stamp_systime(); 00294 00295 CancelState old_state; 00296 set_cancel_state(CANCEL_DISABLED, &old_state); 00297 00298 __mainloop_mutex->lock(); 00299 00300 if (unlikely(__mainloop_thread != NULL)) { 00301 try { 00302 if (likely(__mainloop_thread != NULL)) { 00303 __mainloop_thread->wakeup(__mainloop_barrier); 00304 __mainloop_barrier->wait(); 00305 } 00306 } catch (Exception &e) { 00307 __multi_logger->log_warn("FawkesMainThread", e); 00308 } 00309 } else { 00310 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_PRE_LOOP, __max_thread_time_usec); 00311 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_ACQUIRE, __max_thread_time_usec); 00312 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PREPARE, __max_thread_time_usec); 00313 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PROCESS, __max_thread_time_usec); 00314 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_WORLDSTATE, __max_thread_time_usec); 00315 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_THINK, __max_thread_time_usec); 00316 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SKILL, __max_thread_time_usec); 00317 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT, __max_thread_time_usec); 00318 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT_EXEC, __max_thread_time_usec); 00319 safe_wake(BlockedTimingAspect::WAKEUP_HOOK_POST_LOOP, __max_thread_time_usec); 00320 } 00321 __mainloop_mutex->unlock(); 00322 set_cancel_state(old_state); 00323 00324 test_cancel(); 00325 00326 __thread_manager->try_recover(__recovered_threads); 00327 if ( ! __recovered_threads.empty() ) { 00328 // threads have been recovered! 00329 //__multi_logger->log_error(name(), "Threads recovered %zu", __recovered_threads.size()); 00330 if(__enable_looptime_warnings) { 00331 if ( __recovered_threads.size() == 1 ) { 00332 __multi_logger->log_warn("FawkesMainThread", "The thread %s could be " 00333 "recovered and resumes normal operation", 00334 __recovered_threads.front().c_str()); 00335 } else { 00336 std::string s; 00337 for (std::list<std::string>::iterator i = __recovered_threads.begin(); 00338 i != __recovered_threads.end(); ++i) { 00339 s += *i + " "; 00340 } 00341 00342 __multi_logger->log_warn("FawkesMainThread", "The following threads could be " 00343 "recovered and resumed normal operation: %s", s.c_str()); 00344 } 00345 } 00346 __recovered_threads.clear(); 00347 } 00348 00349 if (__desired_loop_time_sec > 0) { 00350 __loop_end->stamp_systime(); 00351 float loop_time = *__loop_end - __loop_start; 00352 if(__enable_looptime_warnings) { 00353 // give some extra 10% to eliminate frequent false warnings due to regular 00354 // time jitter (TimeWait might not be all that precise) 00355 if (loop_time > 1.1 * __desired_loop_time_sec) { 00356 __multi_logger->log_warn("FawkesMainThread", "Loop time exceeded, " 00357 "desired: %f sec (%u usec), actual: %f sec", 00358 __desired_loop_time_sec, __desired_loop_time_usec, 00359 loop_time); 00360 } 00361 } 00362 } 00363 00364 __plugin_manager->unlock(); 00365 00366 if ( __time_wait ) { 00367 __time_wait->wait_systime(); 00368 } else { 00369 yield(); 00370 } 00371 } catch (Exception &e) { 00372 __multi_logger->log_warn("FawkesMainThread", 00373 "Exception caught while executing default main " 00374 "loop, ignoring."); 00375 __multi_logger->log_warn("FawkesMainThread", e); 00376 } catch (std::exception &e) { 00377 __multi_logger->log_warn("FawkesMainThread", 00378 "STL Exception caught while executing default main " 00379 "loop, ignoring. (what: %s)", e.what()); 00380 } 00381 // catch ... is not a good idea, would catch cancellation exception 00382 // at least needs to be rethrown. 00383 } 00384 00385 00386 /** Get logger. 00387 * @return logger 00388 */ 00389 MultiLogger * 00390 FawkesMainThread::logger() const 00391 { 00392 return __multi_logger; 00393 } 00394 00395 /** @class FawkesMainThread::Runner <baseapp/main_thread.h> 00396 * Utility class to run the main thread. 00397 * 00398 * @author Tim Niemueller 00399 */ 00400 00401 /** Constructor. 00402 * @param fmt Fawkes main thread to run 00403 * @param register_signals true to register default signal handlers 00404 * for SIGINT, SIGTERM, and SIGALRM. 00405 */ 00406 FawkesMainThread::Runner::Runner(FawkesMainThread *fmt, bool register_signals) 00407 { 00408 __init_mutex = new Mutex(); 00409 __init_running = true; 00410 __init_quit = false; 00411 __sigint_running = false; 00412 __register_signals = register_signals; 00413 00414 __fmt = fmt; 00415 00416 if (__register_signals) { 00417 SignalManager::register_handler(SIGINT, this); 00418 SignalManager::register_handler(SIGTERM, this); 00419 SignalManager::register_handler(SIGALRM, this); 00420 } 00421 } 00422 00423 00424 /** Destructor. */ 00425 FawkesMainThread::Runner::~Runner() 00426 { 00427 if (__register_signals) { 00428 SignalManager::unregister_handler(SIGINT); 00429 SignalManager::unregister_handler(SIGTERM); 00430 SignalManager::unregister_handler(SIGALRM); 00431 } 00432 delete __init_mutex; 00433 } 00434 00435 /** Run main thread. */ 00436 void 00437 FawkesMainThread::Runner::run() 00438 { 00439 __init_mutex->lock(); 00440 __init_running = false; 00441 if ( ! __init_quit ) { 00442 __fmt->full_start(); 00443 __fmt->logger()->log_info("FawkesMainThread", "Fawkes %s startup complete", 00444 FAWKES_VERSION_STRING); 00445 __init_mutex->unlock(); 00446 __fmt->join(); 00447 } else { 00448 __init_mutex->unlock(); 00449 } 00450 } 00451 00452 /** Handle signals. 00453 * @param signum signal number 00454 */ 00455 void 00456 FawkesMainThread::Runner::handle_signal(int signum) 00457 { 00458 if ((signum == SIGINT) && ! __sigint_running) { 00459 MutexLocker lock(__init_mutex); 00460 if (__init_running) { 00461 __init_quit = true; 00462 } else { 00463 __fmt->cancel(); 00464 } 00465 __sigint_running = true; 00466 alarm(3 /* sec */); 00467 } else if (signum == SIGALRM) { 00468 // we could use __fmt->logger()->log_info(), but we prefer direct printf 00469 // because we're mentioning Ctrl-C only useful on the console anyway 00470 printf("\nFawkes shutdown and finalization procedure still running.\n" 00471 "Hit Ctrl-C again to force immediate exit.\n\n"); 00472 00473 } else if ((signum == SIGTERM) || __sigint_running) { 00474 // we really need to quit 00475 ::exit(-2); 00476 } 00477 } 00478 00479 } // end namespace fawkes