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/config.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  __config = config;
85 
86  __mainloop_thread = NULL;
87  __mainloop_mutex = new Mutex();
88  __mainloop_barrier = new InterruptibleBarrier(__mainloop_mutex, 2);
89 
90  __load_plugins = NULL;
91  if (load_plugins) {
92  __load_plugins = strdup(load_plugins);
93  }
94 
95  __default_plugin = NULL;
96  if (default_plugin) {
97  __default_plugin = strdup(default_plugin);
98  }
99 
100  /* Clock */
101  __clock = Clock::instance();
102 
103  __loop_start = new Time(__clock);
104  __loop_end = new Time(__clock);
105  try {
106  __max_thread_time_usec = __config->get_uint("/fawkes/mainapp/max_thread_time");
107  } catch (Exception &e) {
108  __max_thread_time_usec = 30000;
109  __multi_logger->log_info("FawkesMainApp",
110  "Maximum thread time not set, assuming 30ms.");
111  }
112  __max_thread_time_nanosec = __max_thread_time_usec * 1000;
113 
114  __time_wait = NULL;
115  try {
116  __desired_loop_time_usec =
117  __config->get_uint("/fawkes/mainapp/desired_loop_time");
118  if ( __desired_loop_time_usec > 0 ) {
119  __time_wait = new TimeWait(__clock, __desired_loop_time_usec);
120  }
121  } catch (Exception &e) {
122  __desired_loop_time_usec = 0;
123  __multi_logger->log_info("FawkesMainApp",
124  "Desired loop time not set, assuming 0");
125  }
126 
127  __desired_loop_time_sec = (float)__desired_loop_time_usec / 1000000.f;
128 
129  try {
130  __enable_looptime_warnings =
131  __config->get_bool("/fawkes/mainapp/enable_looptime_warnings");
132  if(!__enable_looptime_warnings) {
133  __multi_logger->log_debug(name(), "loop time warnings are disabled");
134  }
135  } catch(Exception &e) {
136  __enable_looptime_warnings = true;
137  }
138 }
139 
140 
141 /** Destructor. */
143 {
144  destruct();
145 }
146 
147 
148 /** Destruct.
149  * Mimics destructor, but may be called in ctor exceptions.
150  */
151 void
152 FawkesMainThread::destruct()
153 {
154  try {
155  __config->try_dump();
156  } catch (CouldNotOpenFileException &e) {
157  if (e.get_errno() == EACCES) {
158  __multi_logger->log_warn("FawkesMainThread", "Cannot write to dump file, "
159  "no write ");
160  __multi_logger->log_warn("FawkesMainThread", "permission for file or "
161  "directory. This");
162  __multi_logger->log_warn("FawkesMainThread", "usually happens if running "
163  "with system-wide");
164  __multi_logger->log_warn("FawkesMainThread", "installed Fawkes as non-root "
165  "user. Make");
166  __multi_logger->log_warn("FawkesMainThread", "configuration changes to the "
167  "host-based");
168  __multi_logger->log_warn("FawkesMainThread", "database (set as non-default "
169  "values).");
170  } else {
171  __multi_logger->log_warn("FawkesMainThread", "Failed to dump default "
172  "config (open), exception follows.");
173  __multi_logger->log_warn("FawkesMainThread", e);
174  }
175  } catch (Exception &e) {
176  __multi_logger->log_warn("FawkesMainThread", "Failed to dump default config, "
177  "exception follows.");
178  __multi_logger->log_warn("FawkesMainThread", e);
179  }
180 
181  if (__load_plugins) free(__load_plugins);
182  if (__default_plugin) free(__default_plugin);
183 
184  delete __time_wait;
185  delete __loop_start;
186  delete __loop_end;
187 
188  delete __mainloop_barrier;
189  delete __mainloop_mutex;
190 }
191 
192 /** Start the thread and wait until once() completes.
193  * This is useful to assure that all plugins are loaded before assuming that
194  * startup is complete.
195  */
196 void
198 {
199  __init_barrier = new Barrier(2);
200 
201  start(false);
202 
203  __init_barrier->wait();
204  delete(__init_barrier);
205  __init_barrier = 0;
206 }
207 
208 void
210 {
211  // if plugins passed on command line or in init options, load!
212  if ( __load_plugins) {
213  try {
214  __plugin_manager->load(__load_plugins);
215  } catch (Exception &e) {
216  __multi_logger->log_error("FawkesMainThread", "Failed to load plugins %s, "
217  "exception follows", __load_plugins);
218  __multi_logger->log_error("FawkesMainThread", e);
219  }
220  }
221 
222  // load extra default plugin given via init options
223  try {
224  if (__default_plugin && (strcmp("default", __default_plugin) != 0)) {
225  __plugin_manager->load(__default_plugin);
226  }
227  } catch (PluginLoadException &e) {
228  if (e.plugin_name() != __default_plugin) {
229  // only print if name is not default, i.e. one of the plugins that
230  // the default meta plugin
231  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
232  "plugins, exception follows");
233  __multi_logger->log_error("FawkesMainThread", e);
234  }
235  }
236 
237  // if no specific plugins were given to load, load the default plugin
238  if (! __load_plugins) {
239  try {
240  __plugin_manager->load("default");
241  } catch (PluginLoadException &e) {
242  if (e.plugin_name() != "default") {
243  // only print if name is not default, i.e. one of the plugins that
244  // the default meta plugin
245  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
246  "plugins, exception follows");
247  __multi_logger->log_error("FawkesMainThread", e);
248  }
249  } catch (Exception &e) {
250  __multi_logger->log_error("FawkesMainThread", "Failed to load default "
251  "plugins, exception follows");
252  __multi_logger->log_error("FawkesMainThread", e);
253  }
254  }
255 
256  if (__init_barrier) __init_barrier->wait();
257 }
258 
259 void
261 {
263  __mainloop_mutex->lock();
264  __mainloop_barrier->interrupt();
265  __mainloop_thread = mainloop_thread;
266  __mainloop_mutex->unlock();
268 }
269 
270 
271 void
273 {
274  if ( ! __thread_manager->timed_threads_exist() ) {
275  __multi_logger->log_debug("FawkesMainThread", "No timed threads exist, waiting");
276  try {
277  __thread_manager->wait_for_timed_threads();
278  __multi_logger->log_debug("FawkesMainThread", "Timed threads have been added, "
279  "running main loop now");
280  } catch (InterruptedException &e) {
281  __multi_logger->log_debug("FawkesMainThread", "Waiting for timed threads interrupted");
282  return;
283  }
284  }
285 
286  __plugin_manager->lock();
287 
288  try {
289  if ( __time_wait ) {
290  __time_wait->mark_start();
291  }
292  __loop_start->stamp_systime();
293 
294  CancelState old_state;
295  set_cancel_state(CANCEL_DISABLED, &old_state);
296 
297  __mainloop_mutex->lock();
298 
299  if (unlikely(__mainloop_thread != NULL)) {
300  try {
301  if (likely(__mainloop_thread != NULL)) {
302  __mainloop_thread->wakeup(__mainloop_barrier);
303  __mainloop_barrier->wait();
304  }
305  } catch (Exception &e) {
306  __multi_logger->log_warn("FawkesMainThread", e);
307  }
308  } else {
309  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_PRE_LOOP, __max_thread_time_usec);
310  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_ACQUIRE, __max_thread_time_usec);
311  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PREPARE, __max_thread_time_usec);
312  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PROCESS, __max_thread_time_usec);
313  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_WORLDSTATE, __max_thread_time_usec);
314  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_THINK, __max_thread_time_usec);
315  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_SKILL, __max_thread_time_usec);
316  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT, __max_thread_time_usec);
317  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_ACT_EXEC, __max_thread_time_usec);
318  safe_wake(BlockedTimingAspect::WAKEUP_HOOK_POST_LOOP, __max_thread_time_usec);
319  }
320  __mainloop_mutex->unlock();
321  set_cancel_state(old_state);
322 
323  test_cancel();
324 
325  __thread_manager->try_recover(__recovered_threads);
326  if ( ! __recovered_threads.empty() ) {
327  // threads have been recovered!
328  //__multi_logger->log_error(name(), "Threads recovered %zu", __recovered_threads.size());
329  if(__enable_looptime_warnings) {
330  if ( __recovered_threads.size() == 1 ) {
331  __multi_logger->log_warn("FawkesMainThread", "The thread %s could be "
332  "recovered and resumes normal operation",
333  __recovered_threads.front().c_str());
334  } else {
335  std::string s;
336  for (std::list<std::string>::iterator i = __recovered_threads.begin();
337  i != __recovered_threads.end(); ++i) {
338  s += *i + " ";
339  }
340 
341  __multi_logger->log_warn("FawkesMainThread", "The following threads could be "
342  "recovered and resumed normal operation: %s", s.c_str());
343  }
344  }
345  __recovered_threads.clear();
346  }
347 
348  if (__desired_loop_time_sec > 0) {
349  __loop_end->stamp_systime();
350  float loop_time = *__loop_end - __loop_start;
351  if(__enable_looptime_warnings) {
352  // give some extra 10% to eliminate frequent false warnings due to regular
353  // time jitter (TimeWait might not be all that precise)
354  if (loop_time > 1.1 * __desired_loop_time_sec) {
355  __multi_logger->log_warn("FawkesMainThread", "Loop time exceeded, "
356  "desired: %f sec (%u usec), actual: %f sec",
357  __desired_loop_time_sec, __desired_loop_time_usec,
358  loop_time);
359  }
360  }
361  }
362 
363  __plugin_manager->unlock();
364 
365  if ( __time_wait ) {
366  __time_wait->wait_systime();
367  } else {
368  yield();
369  }
370  } catch (Exception &e) {
371  __multi_logger->log_warn("FawkesMainThread",
372  "Exception caught while executing default main "
373  "loop, ignoring.");
374  __multi_logger->log_warn("FawkesMainThread", e);
375  } catch (std::exception &e) {
376  __multi_logger->log_warn("FawkesMainThread",
377  "STL Exception caught while executing default main "
378  "loop, ignoring. (what: %s)", e.what());
379  }
380  // catch ... is not a good idea, would catch cancellation exception
381  // at least needs to be rethrown.
382 }
383 
384 
385 /** Get logger.
386  * @return logger
387  */
388 MultiLogger *
390 {
391  return __multi_logger;
392 }
393 
394 /** @class FawkesMainThread::Runner <baseapp/main_thread.h>
395  * Utility class to run the main thread.
396  *
397  * @author Tim Niemueller
398  */
399 
400 /** Constructor.
401  * @param fmt Fawkes main thread to run
402  * @param register_signals true to register default signal handlers
403  * for SIGINT, SIGTERM, and SIGALRM.
404  */
406 {
407  __init_mutex = new Mutex();
408  __init_running = true;
409  __init_quit = false;
410  __sigint_running = false;
411  __register_signals = register_signals;
412 
413  __fmt = fmt;
414 
415  SignalManager::ignore(SIGPIPE);
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.
virtual void try_dump()=0
Try to dump configuration.
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:249
Time & stamp_systime()
Set this time to the current system time.
Definition: time.cpp:800
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:1350
virtual void loop()
Code to execute in the thread.
Fawkes Plugin Manager.
Definition: manager.h:51
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 lock()
Lock plugin manager.
Definition: manager.cpp:617
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:140
virtual void wait_for_timed_threads()
Wait for timed threads.
virtual ~FawkesMainThread()
Destructor.
void wakeup()
Wake up thread.
Definition: thread.cpp:1000
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:302
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:227
static void ignore(int signum)
Ignore a signal.
Definition: signal.cpp:182
const char * name() const
Get name of thread.
Definition: thread.h:95
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
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:205
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:93
void mark_start()
Mark start of loop.
Definition: wait.cpp:70
void test_cancel()
Set cancellation point.
Definition: thread.cpp:889
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:902
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:637
MultiLogger * logger() const
Get logger.
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
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:594
Interface for configuration handling.
Definition: config.h:67
FawkesMainThread(Configuration *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
virtual void log_debug(const char *component, const char *format,...)
Log debug message.
Definition: multi.cpp:183
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:511
sensor acquisition thread, acquire data from sensor
CancelState
Cancel state.
Definition: thread.h:60
virtual void once()
Execute an action exactly once.