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 <aspect/manager.h>
25#include <baseapp/main_thread.h>
26#include <config/config.h>
27#include <core/exceptions/system.h>
28#include <core/macros.h>
29#include <core/threading/interruptible_barrier.h>
30#include <core/threading/mutex_locker.h>
31#include <core/version.h>
32#include <plugin/loader.h>
33#include <plugin/manager.h>
34#include <utils/time/clock.h>
35#include <utils/time/wait.h>
36
37#include <cerrno>
38#include <cstdio>
39#include <cstdlib>
40#include <cstring>
41#include <unistd.h>
42
43namespace fawkes {
44
45/** @class FawkesMainThread <baseapp/main_thread.h>
46 * Fawkes default main thread.
47 * This thread initializes all important stuff like the BlackBoard,
48 * handles plugins and wakes up threads at defined hooks.
49 *
50 * @author Tim Niemueller
51 */
52
53/** Constructor.
54 * @param config configuration to use
55 * @param multi_logger basic multi logger to use, a network logger will be
56 * added in the ctor.
57 * @param thread_manager thread manager used to wakeup threads
58 * @param syncpoint_manager syncpoint manager used to manage syncpoints
59 * @param plugin_manager plugin manager to load the desired plugins
60 * @param load_plugins string with comma-separated list of names of plugins
61 * to load on startup.
62 * @param default_plugin additional default plugin name
63 */
65 MultiLogger * multi_logger,
66 ThreadManager * thread_manager,
67 SyncPointManager *syncpoint_manager,
68 PluginManager * plugin_manager,
69 const char * load_plugins,
70 const char * default_plugin)
71: Thread("FawkesMainThread")
72{
73 plugin_manager_ = plugin_manager;
74 thread_manager_ = thread_manager;
75 syncpoint_manager_ = syncpoint_manager;
76 multi_logger_ = multi_logger;
77 config_ = config;
78
79 mainloop_thread_ = NULL;
80 mainloop_mutex_ = new Mutex();
81 mainloop_barrier_ = new InterruptibleBarrier(mainloop_mutex_, 2);
82
83 load_plugins_ = NULL;
84 if (load_plugins) {
85 load_plugins_ = strdup(load_plugins);
86 }
87
88 default_plugin_ = NULL;
89 if (default_plugin) {
90 default_plugin_ = strdup(default_plugin);
91 }
92
93 /* Clock */
94 clock_ = Clock::instance();
95
96 loop_start_ = new Time(clock_);
97 loop_end_ = new Time(clock_);
98 try {
99 max_thread_time_usec_ = config_->get_uint("/fawkes/mainapp/max_thread_time");
100 } catch (Exception &e) {
101 max_thread_time_usec_ = 30000;
102 multi_logger_->log_info("FawkesMainApp", "Maximum thread time not set, assuming 30ms.");
103 }
104 max_thread_time_nanosec_ = max_thread_time_usec_ * 1000;
105
106 time_wait_ = NULL;
107 try {
108 desired_loop_time_usec_ = config_->get_uint("/fawkes/mainapp/desired_loop_time");
109 if (desired_loop_time_usec_ > 0) {
110 time_wait_ = new TimeWait(clock_, desired_loop_time_usec_);
111 }
112 } catch (Exception &e) {
113 desired_loop_time_usec_ = 0;
114 multi_logger_->log_info("FawkesMainApp", "Desired loop time not set, assuming 0");
115 }
116
117 desired_loop_time_sec_ = (float)desired_loop_time_usec_ / 1000000.f;
118
119 try {
120 enable_looptime_warnings_ = config_->get_bool("/fawkes/mainapp/enable_looptime_warnings");
121 if (!enable_looptime_warnings_) {
122 multi_logger_->log_debug(name(), "loop time warnings are disabled");
123 }
124 } catch (Exception &e) {
125 enable_looptime_warnings_ = true;
126 }
127}
128
129/** Destructor. */
131{
132 destruct();
133}
134
135/** Destruct.
136 * Mimics destructor, but may be called in ctor exceptions.
137 */
138void
139FawkesMainThread::destruct()
140{
141 try {
142 config_->try_dump();
143 } catch (CouldNotOpenFileException &e) {
144 if (e.get_errno() == EACCES) {
145 multi_logger_->log_warn("FawkesMainThread",
146 "Cannot write to dump file, "
147 "no write ");
148 multi_logger_->log_warn("FawkesMainThread",
149 "permission for file or "
150 "directory. This");
151 multi_logger_->log_warn("FawkesMainThread",
152 "usually happens if running "
153 "with system-wide");
154 multi_logger_->log_warn("FawkesMainThread",
155 "installed Fawkes as non-root "
156 "user. Make");
157 multi_logger_->log_warn("FawkesMainThread",
158 "configuration changes to the "
159 "host-based");
160 multi_logger_->log_warn("FawkesMainThread",
161 "database (set as non-default "
162 "values).");
163 } else {
164 multi_logger_->log_warn("FawkesMainThread",
165 "Failed to dump default "
166 "config (open), exception follows.");
167 multi_logger_->log_warn("FawkesMainThread", e);
168 }
169 } catch (Exception &e) {
170 multi_logger_->log_warn("FawkesMainThread",
171 "Failed to dump default config, "
172 "exception follows.");
173 multi_logger_->log_warn("FawkesMainThread", e);
174 }
175
176 if (load_plugins_)
177 free(load_plugins_);
178 if (default_plugin_)
179 free(default_plugin_);
180
181 delete time_wait_;
182 delete loop_start_;
183 delete loop_end_;
184
185 delete mainloop_barrier_;
186 delete mainloop_mutex_;
187}
188
189/** Start the thread and wait until once() completes.
190 * This is useful to assure that all plugins are loaded before assuming that
191 * startup is complete.
192 */
193void
195{
196 init_barrier_ = new Barrier(2);
197
198 start(false);
199
200 init_barrier_->wait();
201 delete (init_barrier_);
202 init_barrier_ = 0;
203}
204
205void
207{
208 // register to all syncpoints of the main loop
209 std::vector<BlockedTimingAspect::WakeupHook> hooks;
220
221 try {
222 for (std::vector<BlockedTimingAspect::WakeupHook>::const_iterator it = hooks.begin();
223 it != hooks.end();
224 it++) {
225 syncpoints_start_hook_.push_back(syncpoint_manager_->get_syncpoint(
227 syncpoints_start_hook_.back()->register_emitter("FawkesMainThread");
228 syncpoints_end_hook_.push_back(syncpoint_manager_->get_syncpoint(
230 }
231 } catch (Exception &e) {
232 multi_logger_->log_error("FawkesMainThread", "Failed to acquire mainloop syncpoint");
233 throw;
234 }
235
236 // if plugins passed on command line or in init options, load!
237 if (load_plugins_) {
238 try {
239 plugin_manager_->load(load_plugins_);
240 } catch (Exception &e) {
241 multi_logger_->log_error("FawkesMainThread",
242 "Failed to load plugins %s, "
243 "exception follows",
244 load_plugins_);
245 multi_logger_->log_error("FawkesMainThread", e);
246 }
247 }
248
249 // load extra default plugin given via init options
250 try {
251 if (default_plugin_ && (strcmp("default", default_plugin_) != 0)) {
252 plugin_manager_->load(default_plugin_);
253 }
254 } catch (PluginLoadException &e) {
255 if (e.plugin_name() != default_plugin_) {
256 // only print if name is not default, i.e. one of the plugins that
257 // the default meta plugin
258 multi_logger_->log_error("FawkesMainThread",
259 "Failed to load default "
260 "plugins, exception follows");
261 multi_logger_->log_error("FawkesMainThread", e);
262 }
263 }
264
265 // if no specific plugins were given to load, load the default plugin
266 if (!load_plugins_) {
267 try {
268 plugin_manager_->load("default");
269 } catch (PluginLoadException &e) {
270 if (e.plugin_name() != "default") {
271 // only print if name is not default, i.e. one of the plugins that
272 // the default meta plugin
273 multi_logger_->log_error("FawkesMainThread",
274 "Failed to load default "
275 "plugins, exception follows");
276 multi_logger_->log_error("FawkesMainThread", e);
277 }
278 } catch (Exception &e) {
279 multi_logger_->log_error("FawkesMainThread",
280 "Failed to load default "
281 "plugins, exception follows");
282 multi_logger_->log_error("FawkesMainThread", e);
283 }
284 }
285
286 if (init_barrier_)
287 init_barrier_->wait();
288}
289
290void
292{
294 mainloop_mutex_->lock();
295 mainloop_barrier_->interrupt();
296 mainloop_thread_ = mainloop_thread;
297 mainloop_mutex_->unlock();
299}
300
301void
303{
304 if (!thread_manager_->timed_threads_exist()) {
305 multi_logger_->log_debug("FawkesMainThread", "No timed threads exist, waiting");
306 try {
307 thread_manager_->wait_for_timed_threads();
308 multi_logger_->log_debug("FawkesMainThread",
309 "Timed threads have been added, "
310 "running main loop now");
311 } catch (InterruptedException &e) {
312 multi_logger_->log_debug("FawkesMainThread", "Waiting for timed threads interrupted");
313 return;
314 }
315 }
316
317 plugin_manager_->lock();
318
319 try {
320 if (time_wait_) {
321 time_wait_->mark_start();
322 }
323 loop_start_->stamp_systime();
324
325 CancelState old_state;
327
328 mainloop_mutex_->lock();
329
330 if (unlikely(mainloop_thread_ != NULL)) {
331 try {
332 if (likely(mainloop_thread_ != NULL)) {
333 mainloop_thread_->wakeup(mainloop_barrier_);
334 mainloop_barrier_->wait();
335 }
336 } catch (Exception &e) {
337 multi_logger_->log_warn("FawkesMainThread", e);
338 }
339 } else {
340 uint num_hooks = syncpoints_start_hook_.size();
341 if (syncpoints_end_hook_.size() != num_hooks) {
342 multi_logger_->log_error(
343 "FawkesMainThread",
344 "Hook syncpoints are not initialized properly, not waking up any threads!");
345 } else {
346 for (uint i = 0; i < num_hooks; i++) {
347 syncpoints_start_hook_[i]->emit("FawkesMainThread");
348 syncpoints_end_hook_[i]->reltime_wait_for_all("FawkesMainThread",
349 0,
350 max_thread_time_nanosec_);
351 }
352 }
353 }
354 mainloop_mutex_->unlock();
355 set_cancel_state(old_state);
356
357 test_cancel();
358
359 thread_manager_->try_recover(recovered_threads_);
360 if (!recovered_threads_.empty()) {
361 // threads have been recovered!
362 //multi_logger_->log_error(name(), "Threads recovered %zu", recovered_threads_.size());
363 if (enable_looptime_warnings_) {
364 if (recovered_threads_.size() == 1) {
365 multi_logger_->log_warn("FawkesMainThread",
366 "The thread %s could be "
367 "recovered and resumes normal operation",
368 recovered_threads_.front().c_str());
369 } else {
370 std::string s;
371 for (std::list<std::string>::iterator i = recovered_threads_.begin();
372 i != recovered_threads_.end();
373 ++i) {
374 s += *i + " ";
375 }
376
377 multi_logger_->log_warn("FawkesMainThread",
378 "The following threads could be "
379 "recovered and resumed normal operation: %s",
380 s.c_str());
381 }
382 }
383 recovered_threads_.clear();
384 }
385
386 if (desired_loop_time_sec_ > 0) {
387 loop_end_->stamp_systime();
388 float loop_time = *loop_end_ - loop_start_;
389 if (enable_looptime_warnings_) {
390 // give some extra 10% to eliminate frequent false warnings due to regular
391 // time jitter (TimeWait might not be all that precise)
392 if (loop_time > 1.1 * desired_loop_time_sec_) {
393 multi_logger_->log_warn("FawkesMainThread",
394 "Loop time exceeded, "
395 "desired: %f sec (%u usec), actual: %f sec",
396 desired_loop_time_sec_,
397 desired_loop_time_usec_,
398 loop_time);
399 }
400 }
401 }
402
403 plugin_manager_->unlock();
404
405 if (time_wait_) {
406 time_wait_->wait_systime();
407 } else {
408 yield();
409 }
410 } catch (Exception &e) {
411 multi_logger_->log_warn("FawkesMainThread",
412 "Exception caught while executing default main "
413 "loop, ignoring.");
414 multi_logger_->log_warn("FawkesMainThread", e);
415 } catch (std::exception &e) {
416 multi_logger_->log_warn("FawkesMainThread",
417 "STL Exception caught while executing default main "
418 "loop, ignoring. (what: %s)",
419 e.what());
420 }
421 // catch ... is not a good idea, would catch cancellation exception
422 // at least needs to be rethrown.
423}
424
425/** Get logger.
426 * @return logger
427 */
430{
431 return multi_logger_;
432}
433
434/** @class FawkesMainThread::Runner <baseapp/main_thread.h>
435 * Utility class to run the main thread.
436 *
437 * @author Tim Niemueller
438 */
439
440/** Constructor.
441 * @param fmt Fawkes main thread to run
442 * @param register_signals true to register default signal handlers
443 * for SIGINT, SIGTERM, and SIGALRM.
444 */
446{
447 init_mutex_ = new Mutex();
448 init_running_ = true;
449 init_quit_ = false;
450 sigint_running_ = false;
451 register_signals_ = register_signals;
452
453 fmt_ = fmt;
454
455 SignalManager::ignore(SIGPIPE);
456 if (register_signals_) {
458 SignalManager::register_handler(SIGTERM, this);
459 SignalManager::register_handler(SIGALRM, this);
460 }
461}
462
463/** Destructor. */
465{
466 if (register_signals_) {
470 }
471 delete init_mutex_;
472}
473
474/** Run main thread. */
475void
477{
478 init_mutex_->lock();
479 init_running_ = false;
480 if (!init_quit_) {
481 fmt_->full_start();
482 fmt_->logger()->log_info("FawkesMainThread",
483 "Fawkes %s startup complete",
484 FAWKES_VERSION_STRING);
485 init_mutex_->unlock();
486 fmt_->join();
487 } else {
488 init_mutex_->unlock();
489 }
490}
491
492/** Handle signals.
493 * @param signum signal number
494 */
495void
497{
498 if ((signum == SIGINT) && !sigint_running_) {
499 MutexLocker lock(init_mutex_);
500 if (init_running_) {
501 init_quit_ = true;
502 } else {
503 fmt_->cancel();
504 }
505 sigint_running_ = true;
506 alarm(3 /* sec */);
507 } else if (signum == SIGALRM) {
508 // we could use fmt_->logger()->log_info(), but we prefer direct printf
509 // because we're mentioning Ctrl-C only useful on the console anyway
510 printf("\nFawkes shutdown and finalization procedure still running.\n"
511 "Hit Ctrl-C again to force immediate exit.\n\n");
512
513 } else if ((signum == SIGTERM) || sigint_running_) {
514 // we really need to quit
515 ::exit(-2);
516 }
517}
518
519} // end namespace fawkes
A barrier is a synchronization tool which blocks until a given number of threads have reached the bar...
Definition: barrier.h:32
virtual void wait()
Wait for other threads.
Definition: barrier.cpp:153
@ WAKEUP_HOOK_SENSOR_ACQUIRE
sensor acquisition thread, acquire data from sensor
@ WAKEUP_HOOK_ACT
act thread (motor module etc.)
@ WAKEUP_HOOK_WORLDSTATE
world state thread
@ WAKEUP_HOOK_PRE_LOOP
before each loop
@ WAKEUP_HOOK_SENSOR_PREPARE
sensor data preparation thread, convert acquired data to usable format
@ WAKEUP_HOOK_THINK
think thread (agent)
@ WAKEUP_HOOK_POST_LOOP
run after loop
@ WAKEUP_HOOK_SKILL
skill thread (skill module)
@ WAKEUP_HOOK_SENSOR_PROCESS
sensor data processing thread
@ WAKEUP_HOOK_ACT_EXEC
act execution thread
static std::string blocked_timing_hook_to_end_syncpoint(WakeupHook hook)
Get the syncpoint identifier corresponding to the end of a wakeup hook.
static std::string blocked_timing_hook_to_start_syncpoint(WakeupHook hook)
Get the syncpoint identifier corresponding to the start of a wakeup hook.
static Clock * instance()
Clock initializer.
Definition: clock.cpp:63
Interface for configuration handling.
Definition: config.h:68
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual void try_dump()=0
Try to dump configuration.
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what() const noexcept
Get primary string.
Definition: exception.cpp:639
Runner(FawkesMainThread *fmt, bool register_signals=true)
Constructor.
void run()
Run main thread.
void handle_signal(int signum)
Handle signals.
Fawkes default main thread.
Definition: main_thread.h:59
virtual void set_mainloop_thread(Thread *mainloop_thread)
Set a new main loop.
virtual void once()
Execute an action exactly once.
void full_start()
Start the thread and wait until once() completes.
MultiLogger * logger() const
Get logger.
FawkesMainThread(Configuration *config, MultiLogger *multi_logger, ThreadManager *thread_manager, SyncPointManager *syncpoint_manager, PluginManager *plugin_manager, const char *load_plugins, const char *default_plugin=0)
Constructor.
Definition: main_thread.cpp:64
virtual void loop()
Code to execute in the thread.
virtual ~FawkesMainThread()
Destructor.
The current system call has been interrupted (for instance by a signal).
Definition: system.h:39
A barrier is a synchronization tool which blocks until a given number of threads have reached the bar...
void interrupt() noexcept
Interrupt the barrier.
bool wait(unsigned int timeout_sec, unsigned int timeout_nanosec)
Wait for other threads.
Log through multiple loggers.
Definition: multi.h:35
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:195
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:216
virtual void log_debug(const char *component, const char *format,...)
Log debug message.
Definition: multi.cpp:174
virtual void log_error(const char *component, const char *format,...)
Log error message.
Definition: multi.cpp:237
Mutex locking helper.
Definition: mutex_locker.h:34
Mutex mutual exclusion lock.
Definition: mutex.h:33
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
This exception is thrown if the requested plugin could not be loaded.
Definition: loader.h:39
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:87
Fawkes Plugin Manager.
Definition: manager.h:48
void load(const std::string &plugin_list)
Load plugin.
Definition: manager.cpp:325
void unlock()
Unlock plugin manager.
Definition: manager.cpp:680
void lock()
Lock plugin manager.
Definition: manager.cpp:661
static void ignore(int signum)
Ignore a signal.
Definition: signal.cpp:174
static void unregister_handler(int signum)
Unregister a SignalHandler for a signal.
Definition: signal.cpp:136
static SignalHandler * register_handler(int signum, SignalHandler *handler)
Register a SignalHandler for a signal.
Definition: signal.cpp:113
This class gives access to SyncPoints.
RefPtr< SyncPoint > get_syncpoint(const std::string &component, const std::string &identifier)
Get a SyncPoint.
Base application thread manager.
virtual void try_recover(std::list< std::string > &recovered_threads)
Try to recover threads.
virtual void wait_for_timed_threads()
Wait for timed threads.
virtual bool timed_threads_exist()
Check if any timed threads exist.
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:499
CancelState
Cancel state.
Definition: thread.h:64
@ CANCEL_DISABLED
thread cannot be cancelled
Definition: thread.h:66
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Definition: thread.cpp:1396
void yield()
Yield the processor to another thread or process.
Definition: thread.cpp:883
void exit()
Exit the thread.
Definition: thread.cpp:582
void wakeup()
Wake up thread.
Definition: thread.cpp:995
void test_cancel()
Set cancellation point.
Definition: thread.cpp:871
Mutex * loopinterrupt_antistarve_mutex
Mutex to avoid starvation when trying to lock loop_mutex.
Definition: thread.h:153
Time wait utility.
Definition: wait.h:33
void mark_start()
Mark start of loop.
Definition: wait.cpp:68
void wait_systime()
Wait until minimum loop time has been reached in real time.
Definition: wait.cpp:96
A class for handling time.
Definition: time.h:93
Time & stamp_systime()
Set this time to the current system time.
Definition: time.cpp:720
Fawkes library namespace.