Fawkes API  Fawkes Development Version
main_thread.cpp
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