Fawkes API  Fawkes Development Version
main_thread.cpp
1 
2 /***************************************************************************
3  * main_thread.cpp - Fawkes main thread
4  *
5  * Created: Thu Nov 2 16:47:50 2006
6  * Copyright 2006-2011 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <baseapp/main_thread.h>
25 
26 #include <core/threading/interruptible_barrier.h>
27 #include <core/threading/mutex_locker.h>
28 #include <core/exceptions/system.h>
29 #include <core/version.h>
30 #include <config/sqlite.h>
31 #include <utils/time/clock.h>
32 #include <utils/time/wait.h>
33 #include <netcomm/fawkes/network_manager.h>
34 #include <blackboard/local.h>
35 
36 #include <aspect/manager.h>
37 #include <plugin/manager.h>
38 #include <plugin/loader.h>
39 #include <plugin/net/handler.h>
40 
41 #include <cstdio>
42 #include <cstring>
43 #include <cerrno>
44 #include <cstdlib>
45 #include <unistd.h>
46 
47 #include <core/macros.h>
48 
49 namespace fawkes {
50 #if 0 /* just to make Emacs auto-indent happy */
51 }
52 #endif
53 
54 /** @class FawkesMainThread <baseapp/main_thread.h>
55  * Fawkes default main thread.
56  * This thread initializes all important stuff like the BlackBoard,
57  * handles plugins and wakes up threads at defined hooks.
58  *
59  * @author Tim Niemueller
60  */
61 
62 
63 /** Constructor.
64  * @param config configuration to use
65  * @param multi_logger basic multi logger to use, a network logger will be
66  * added in the ctor.
67  * @param thread_manager thread manager used to wakeup threads
68  * @param plugin_manager plugin manager to load the desired plugins
69  * @param load_plugins string with comma-separated list of names of plugins
70  * to load on startup.
71  * @param default_plugin additional default plugin name
72  */
74  MultiLogger *multi_logger,
75  ThreadManager *thread_manager,
76  PluginManager *plugin_manager,
77  const char *load_plugins,
78  const char *default_plugin)
79  : Thread("FawkesMainThread")
80 {
81  __plugin_manager = plugin_manager;
82  __thread_manager = thread_manager;
83  __multi_logger = multi_logger;
84  __sqlite_conf = config;
85  __config = config;
86 
87  __mainloop_thread = NULL;
88  __mainloop_mutex = new Mutex();
89  __mainloop_barrier = new InterruptibleBarrier(__mainloop_mutex, 2);
90 
91  __load_plugins = NULL;
92  if (load_plugins) {
93  __load_plugins = strdup(load_plugins);
94  }
95 
96  __default_plugin = NULL;
97  if (default_plugin) {
98  __default_plugin = strdup(default_plugin);
99  }
100 
101  /* Clock */
102  __clock = Clock::instance();
103 
104  __loop_start = new Time(__clock);
105  __loop_end = new Time(__clock);
106  try {
107  __max_thread_time_usec = __config->get_uint("/fawkes/mainapp/max_thread_time");
108  } catch (Exception &e) {
109  __max_thread_time_usec = 30000;
110  __multi_logger->log_info("FawkesMainApp",
111  "Maximum thread time not set, assuming 30ms.");
112  }
113  __max_thread_time_nanosec = __max_thread_time_usec * 1000;
114 
115  __time_wait = NULL;
116  try {
117  __desired_loop_time_usec =
118  __config->get_uint("/fawkes/mainapp/desired_loop_time");
119  if ( __desired_loop_time_usec > 0 ) {
120  __time_wait = new TimeWait(__clock, __desired_loop_time_usec);
121  }
122  } catch (Exception &e) {
123  __desired_loop_time_usec = 0;
124  __multi_logger->log_info("FawkesMainApp",
125  "Desired loop time not set, assuming 0");
126  }
127 
128  __desired_loop_time_sec = (float)__desired_loop_time_usec / 1000000.f;
129 
130  try {
131  __enable_looptime_warnings =
132  __config->get_bool("/fawkes/mainapp/enable_looptime_warnings");
133  if(!__enable_looptime_warnings) {
134  __multi_logger->log_debug(name(), "loop time warnings are disabled");
135  }
136  } catch(Exception &e) {
137  __enable_looptime_warnings = true;
138  }
139 }
140 
141 
142 /** Destructor. */
144 {
145  destruct();
146 }
147 
148 
149 /** Destruct.
150  * Mimics destructor, but may be called in ctor exceptions.
151  */
152 void
153 FawkesMainThread::destruct()
154 {
155  try {
156  __sqlite_conf->try_dump();
157  } catch (CouldNotOpenFileException &e) {
158  if (e.get_errno() == EACCES) {
159  __multi_logger->log_warn("FawkesMainThread", "Cannot write to dump file, "
160  "no write ");
161  __multi_logger->log_warn("FawkesMainThread", "permission for file or "
162  "directory. This");
163  __multi_logger->log_warn("FawkesMainThread", "usually happens if running "
164  "with system-wide");
165  __multi_logger->log_warn("FawkesMainThread", "installed Fawkes as non-root "
166  "user. Make");
167  __multi_logger->log_warn("FawkesMainThread", "configuration changes to the "
168  "host-based");
169  __multi_logger->log_warn("FawkesMainThread", "database (set as non-default "
170  "values).");
171  } else {
172  __multi_logger->log_warn("FawkesMainThread", "Failed to dump default "
173  "config (open), exception follows.");
174  __multi_logger->log_warn("FawkesMainThread", e);
175  }
176  } catch (Exception &e) {
177  __multi_logger->log_warn("FawkesMainThread", "Failed to dump default config, "
178  "exception follows.");
179  __multi_logger->log_warn("FawkesMainThread", e);
180  }
181 
182  if (__load_plugins) free(__load_plugins);
183  if (__default_plugin) free(__default_plugin);
184 
185  delete __time_wait;
186  delete __loop_start;
187  delete __loop_end;
188 
189  delete __mainloop_barrier;
190  delete __mainloop_mutex;
191 }
192 
193 /** Start the thread and wait until once() completes.
194  * This is useful to assure that all plugins are loaded before assuming that
195  * startup is complete.
196  */
197 void
199 {
200  __init_barrier = new Barrier(2);
201 
202  start(false);
203 
204  __init_barrier->wait();
205  delete(__init_barrier);
206  __init_barrier = 0;
207 }
208 
209 void
211 {
212  // if plugins passed on command line or in init options, load!
213  if ( __load_plugins) {
214  try {
215  __plugin_manager->load(__load_plugins);
216  } catch (Exception &e) {
217  __multi_logger->log_error("FawkesMainThread", "Failed to load plugins %s, "
218  "exception follows", __load_plugins);
219  __multi_logger->log_error("FawkesMainThread", e);
220  }
221  }
222 
223  // load extra default plugin given via init options
224  try {
225  if (__default_plugin && (strcmp("default", __default_plugin) != 0)) {
226  __plugin_manager->load(__default_plugin);
227  }
228  } catch (PluginLoadException &e) {
229  if (e.plugin_name() != __default_plugin) {
230  // only print if name is not default, i.e. one of the plugins that
231  // the default meta plugin
232  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
233  "plugins, exception follows");
234  __multi_logger->log_error("FawkesMainThread", e);
235  }
236  }
237 
238  // if no specific plugins were given to load, load the default plugin
239  if (! __load_plugins) {
240  try {
241  __plugin_manager->load("default");
242  } catch (PluginLoadException &e) {
243  if (e.plugin_name() != "default") {
244  // only print if name is not default, i.e. one of the plugins that
245  // the default meta plugin
246  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
247  "plugins, exception follows");
248  __multi_logger->log_error("FawkesMainThread", e);
249  }
250  } catch (Exception &e) {
251  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
252  "plugins, exception follows");
253  __multi_logger->log_error("FawkesMainThread", e);
254  }
255  }
256 
257  if (__init_barrier) __init_barrier->wait();
258 }
259 
260 void
262 {
264  __mainloop_mutex->lock();
265  __mainloop_barrier->interrupt();
266  __mainloop_thread = mainloop_thread;
267  __mainloop_mutex->unlock();
269 }
270 
271 
272 void
274 {
275  if ( ! __thread_manager->timed_threads_exist() ) {
276  __multi_logger->log_debug("FawkesMainThread", "No timed threads exist, waiting");
277  try {
278  __thread_manager->wait_for_timed_threads();
279  __multi_logger->log_debug("FawkesMainThread", "Timed threads have been added, "
280  "running main loop now");
281  } catch (InterruptedException &e) {
282  __multi_logger->log_debug("FawkesMainThread", "Waiting for timed threads interrupted");
283  return;
284  }
285  }
286 
287  __plugin_manager->lock();
288 
289  try {
290  if ( __time_wait ) {
291  __time_wait->mark_start();
292  }
293  __loop_start->stamp_systime();
294 
295  CancelState old_state;
296  set_cancel_state(CANCEL_DISABLED, &old_state);
297 
298  __mainloop_mutex->lock();
299 
300  if (unlikely(__mainloop_thread != NULL)) {
301  try {
302  if (likely(__mainloop_thread != NULL)) {
303  __mainloop_thread->wakeup(__mainloop_barrier);
304  __mainloop_barrier->wait();
305  }
306  } catch (Exception &e) {
307  __multi_logger->log_warn("FawkesMainThread", e);
308  }
309  } else {
310  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_PRE_LOOP, __max_thread_time_usec);
311  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_ACQUIRE, __max_thread_time_usec);
312  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PREPARE, __max_thread_time_usec);
313  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PROCESS, __max_thread_time_usec);
314  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_WORLDSTATE, __max_thread_time_usec);
315  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_THINK, __max_thread_time_usec);
316  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SKILL, __max_thread_time_usec);
317  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT, __max_thread_time_usec);
318  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT_EXEC, __max_thread_time_usec);
319  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_POST_LOOP, __max_thread_time_usec);
320  }
321  __mainloop_mutex->unlock();
322  set_cancel_state(old_state);
323 
324  test_cancel();
325 
326  __thread_manager->try_recover(__recovered_threads);
327  if ( ! __recovered_threads.empty() ) {
328  // threads have been recovered!
329  //__multi_logger->log_error(name(), "Threads recovered %zu", __recovered_threads.size());
330  if(__enable_looptime_warnings) {
331  if ( __recovered_threads.size() == 1 ) {
332  __multi_logger->log_warn("FawkesMainThread", "The thread %s could be "
333  "recovered and resumes normal operation",
334  __recovered_threads.front().c_str());
335  } else {
336  std::string s;
337  for (std::list<std::string>::iterator i = __recovered_threads.begin();
338  i != __recovered_threads.end(); ++i) {
339  s += *i + " ";
340  }
341 
342  __multi_logger->log_warn("FawkesMainThread", "The following threads could be "
343  "recovered and resumed normal operation: %s", s.c_str());
344  }
345  }
346  __recovered_threads.clear();
347  }
348 
349  if (__desired_loop_time_sec > 0) {
350  __loop_end->stamp_systime();
351  float loop_time = *__loop_end - __loop_start;
352  if(__enable_looptime_warnings) {
353  // give some extra 10% to eliminate frequent false warnings due to regular
354  // time jitter (TimeWait might not be all that precise)
355  if (loop_time > 1.1 * __desired_loop_time_sec) {
356  __multi_logger->log_warn("FawkesMainThread", "Loop time exceeded, "
357  "desired: %f sec (%u usec), actual: %f sec",
358  __desired_loop_time_sec, __desired_loop_time_usec,
359  loop_time);
360  }
361  }
362  }
363 
364  __plugin_manager->unlock();
365 
366  if ( __time_wait ) {
367  __time_wait->wait_systime();
368  } else {
369  yield();
370  }
371  } catch (Exception &e) {
372  __multi_logger->log_warn("FawkesMainThread",
373  "Exception caught while executing default main "
374  "loop, ignoring.");
375  __multi_logger->log_warn("FawkesMainThread", e);
376  } catch (std::exception &e) {
377  __multi_logger->log_warn("FawkesMainThread",
378  "STL Exception caught while executing default main "
379  "loop, ignoring. (what: %s)", e.what());
380  }
381  // catch ... is not a good idea, would catch cancellation exception
382  // at least needs to be rethrown.
383 }
384 
385 
386 /** Get logger.
387  * @return logger
388  */
389 MultiLogger *
391 {
392  return __multi_logger;
393 }
394 
395 /** @class FawkesMainThread::Runner <baseapp/main_thread.h>
396  * Utility class to run the main thread.
397  *
398  * @author Tim Niemueller
399  */
400 
401 /** Constructor.
402  * @param fmt Fawkes main thread to run
403  * @param register_signals true to register default signal handlers
404  * for SIGINT, SIGTERM, and SIGALRM.
405  */
407 {
408  __init_mutex = new Mutex();
409  __init_running = true;
410  __init_quit = false;
411  __sigint_running = false;
412  __register_signals = register_signals;
413 
414  __fmt = fmt;
415 
416  if (__register_signals) {
417  SignalManager::register_handler(SIGINT, this);
418  SignalManager::register_handler(SIGTERM, this);
419  SignalManager::register_handler(SIGALRM, this);
420  }
421 }
422 
423 
424 /** Destructor. */
426 {
427  if (__register_signals) {
431  }
432  delete __init_mutex;
433 }
434 
435 /** Run main thread. */
436 void
438 {
439  __init_mutex->lock();
440  __init_running = false;
441  if ( ! __init_quit ) {
442  __fmt->full_start();
443  __fmt->logger()->log_info("FawkesMainThread", "Fawkes %s startup complete",
444  FAWKES_VERSION_STRING);
445  __init_mutex->unlock();
446  __fmt->join();
447  } else {
448  __init_mutex->unlock();
449  }
450 }
451 
452 /** Handle signals.
453  * @param signum signal number
454  */
455 void
457 {
458  if ((signum == SIGINT) && ! __sigint_running) {
459  MutexLocker lock(__init_mutex);
460  if (__init_running) {
461  __init_quit = true;
462  } else {
463  __fmt->cancel();
464  }
465  __sigint_running = true;
466  alarm(3 /* sec */);
467  } else if (signum == SIGALRM) {
468  // we could use __fmt->logger()->log_info(), but we prefer direct printf
469  // because we're mentioning Ctrl-C only useful on the console anyway
470  printf("\nFawkes shutdown and finalization procedure still running.\n"
471  "Hit Ctrl-C again to force immediate exit.\n\n");
472 
473  } else if ((signum == SIGTERM) || __sigint_running) {
474  // we really need to quit
475  ::exit(-2);
476  }
477 }
478 
479 } // end namespace fawkes
void full_start()
Start the thread and wait until once() completes.
File could not be opened.
Definition: system.h:53
act thread (motor module etc.)
int get_errno()
Get errno.
Definition: exception.cpp:643
void interrupt()
Interrupt the barrier.
static Clock * instance()
Clock initializer.
Definition: clock.cpp:65
sensor data preparation thread, convert acquired data to usable format
virtual void log_error(const char *component, const char *format,...)
Log error message.
Definition: multi.cpp:247
Time & stamp_systime()
Set this time to the current system time.
Definition: time.cpp:800
Configuration storage using SQLite.
Definition: sqlite.h:39
Fawkes library namespace.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
void unlock()
Unlock the mutex.
Definition: mutex.cpp:135
virtual void wait()
Wait for other threads.
Definition: barrier.cpp:157
Mutex locking helper.
Definition: mutex_locker.h:33
This exception is thrown if the requested plugin could not be loaded.
Definition: loader.h:41
A class for handling time.
Definition: time.h:91
thread cannot be cancelled
Definition: thread.h:62
virtual bool timed_threads_exist()
Check if any timed threads exist.
Thread class encapsulation of pthreads.
Definition: thread.h:42
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Definition: thread.cpp:1327
virtual void loop()
Code to execute in the thread.
Fawkes Plugin Manager.
Definition: manager.h:53
A barrier is a synchronization tool which blocks until a given number of threads have reached the bar...
bool wait(unsigned int timeout_sec, unsigned int timeout_nanosec)
Wait for other threads.
void wait_systime()
Wait until minimum loop time has been reached in real time.
Definition: wait.cpp:100
void try_dump()
Try to dump default configuration.
Definition: sqlite.cpp:426
void lock()
Lock plugin manager.
Definition: manager.cpp:599
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:84
Log through multiple loggers.
Definition: multi.h:35
Mutex * loopinterrupt_antistarve_mutex
Mutex to avoid starvation when trying to lock loop_mutex.
Definition: thread.h:138
virtual void wait_for_timed_threads()
Wait for timed threads.
virtual ~FawkesMainThread()
Destructor.
void wakeup()
Wake up thread.
Definition: thread.cpp:979
Base application thread manager.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void load(const char *plugin_list)
Load plugin.
Definition: manager.cpp:297
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:225
Runner(FawkesMainThread *fmt, bool register_signals=true)
Constructor.
static SignalHandler * register_handler(int signum, SignalHandler *handler)
Register a SignalHandler for a signal.
Definition: signal.cpp:116
The current system call has been interrupted (for instance by a signal).
Definition: system.h:39
const char * name() const
Get name of thread.
Definition: thread.h:95
MultiLogger * logger() const
Get logger.
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:203
void mark_start()
Mark start of loop.
Definition: wait.cpp:70
void test_cancel()
Set cancellation point.
Definition: thread.cpp:868
static void unregister_handler(int signum)
Unregister a SignalHandler for a signal.
Definition: signal.cpp:140
void run()
Run main thread.
virtual void set_mainloop_thread(Thread *mainloop_thread)
Set a new main loop.
void yield()
Yield the processor to another thread or process.
Definition: thread.cpp:881
void handle_signal(int signum)
Handle signals.
virtual void try_recover(std::list< std::string > &recovered_threads)
Try to recover threads.
void lock()
Lock this mutex.
Definition: mutex.cpp:89
void unlock()
Unlock plugin manager.
Definition: manager.cpp:619
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
FawkesMainThread(SQLiteConfiguration *config, MultiLogger *multi_logger, ThreadManager *thread_manager, PluginManager *plugin_manager, const char *load_plugins, const char *default_plugin=0)
Constructor.
Definition: main_thread.cpp:73
Mutex mutual exclusion lock.
Definition: mutex.h:32
Fawkes default main thread.
Definition: main_thread.h:58
void exit()
Exit the thread.
Definition: thread.cpp:584
virtual void log_debug(const char *component, const char *format,...)
Log debug message.
Definition: multi.cpp:181
Time wait utility.
Definition: wait.h:32
A barrier is a synchronization tool which blocks until a given number of threads have reached the bar...
Definition: barrier.h:32
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:507
sensor acquisition thread, acquire data from sensor
CancelState
Cancel state.
Definition: thread.h:60
virtual void once()
Execute an action exactly once.