Fawkes API Fawkes Development Version
timing_thread.cpp
1/***************************************************************************
2 * timing_thread.cpp - Timing thread to achieve a desired main loop time
3 *
4 * Created: Thu Jul 23 14:45:42 2015
5 * Copyright 2015-2017 Till Hofmann
6 ****************************************************************************/
7/* This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
16 *
17 * Read the full text in the LICENSE.GPL file in the doc directory.
18 */
19
20#include <baseapp/timing_thread.h>
21
22#include <unistd.h>
23
24#define CFG_PREFIX "/fawkes/mainapp/"
25
26namespace fawkes {
27
28/** @class FawkesTimingThread <baseapp/timing_thread.h>
29 * Thread to control the main loop timing.
30 * This thread uses the preloop and postloop SyncPoints to control the timing of
31 * the main loop. It waits for the preloop SyncPoint at the beginning of the
32 * main loop and emits the postloop SyncPoint at the end of the loop. If the
33 * loop time is less than the desired loop time, the thread waits before
34 * releasing the postloop SyncPoint. If the loop time is longer than the maximum
35 * loop time, a warning is printed.
36 */
37FawkesTimingThread::FawkesTimingThread() : Thread("FawkesTimingThread", Thread::OPMODE_CONTINUOUS)
38// ConfigurationChangeHandler(CFG_PREFIX)
39{
40}
41
42/** Initialize.
43 * Get the pre- and postloop SyncPoints and read all relevant config values.
44 */
45void
46FawkesTimingThread::init()
47{
48 clock_ = Clock::instance();
49 loop_start_ = new Time(clock_);
50 loop_end_ = new Time(clock_);
51
52 syncpoint_loop_start_ = syncpoint_manager->get_syncpoint(name(), "/preloop/start");
53 syncpoint_loop_end_ = syncpoint_manager->get_syncpoint(name(), "/postloop/end");
54
55 try {
56 desired_loop_time_usec_ = config->get_uint("/fawkes/mainapp/desired_loop_time");
57 } catch (Exception &e) {
58 desired_loop_time_usec_ = 0;
59 logger->log_info(name(), "Desired loop time not set, assuming 0");
60 }
61 desired_loop_time_sec_ = (float)desired_loop_time_usec_ / 1000000.f;
62
63 try {
64 min_loop_time_usec_ = config->get_uint("/fawkes/mainapp/min_loop_time");
65 } catch (Exception &e) {
66 min_loop_time_usec_ = 0;
67 logger->log_info(name(), "Minimal loop time not set, assuming 0");
68 }
69 min_loop_time_sec_ = (float)min_loop_time_usec_ / 1000000.f;
70
71 try {
72 enable_looptime_warnings_ = config->get_bool("/fawkes/mainapp/enable_looptime_warnings");
73 if (!enable_looptime_warnings_) {
74 logger->log_debug(name(), "loop time warnings are disabled");
75 }
76 } catch (Exception &e) {
77 enable_looptime_warnings_ = true;
78 }
79}
80
81/** Thread loop.
82 * This loop runs parallel to the main loop. At the beginning of the main loop,
83 * it waits for the preloop SyncPoint. At the end of the main loop, it emits the
84 * postloop SyncPoint.
85 */
86void
87FawkesTimingThread::loop()
88{
89 syncpoint_loop_start_->wait(name());
90 loop_start_->stamp_systime();
91
92 syncpoint_loop_end_->wait(name());
93 loop_end_->stamp_systime();
94 float loop_time = *loop_end_ - loop_start_;
95
96 if (loop_time < min_loop_time_usec_) {
97 logger->log_warn(name(), "Minimal loop time not reached, extending loop");
98 usleep(min_loop_time_usec_ - loop_time);
99 loop_end_->stamp_systime();
100 loop_time = *loop_end_ - loop_start_;
101 }
102
103 if (desired_loop_time_sec_ > 0) {
104 if (enable_looptime_warnings_) {
105 // give some extra 10% to eliminate frequent false warnings due to regular
106 // time jitter (TimeWait might not be all that precise)
107 if (loop_time > 1.1 * desired_loop_time_sec_) {
108 logger->log_warn(name(),
109 "Loop time exceeded, "
110 "desired: %f sec (%u usec), actual: %f sec",
111 desired_loop_time_sec_,
112 desired_loop_time_usec_,
113 loop_time);
114 } else {
115 logger->log_warn(name(),
116 "Desired loop time achieved, "
117 "desired: %f sec (%u usec), actual: %f sec",
118 desired_loop_time_sec_,
119 desired_loop_time_usec_,
120 loop_time);
121 }
122 }
123 }
124}
125
126/** Finalize the thread.
127 * Release all SyncPoints and do other cleanup.
128 */
129void
130FawkesTimingThread::finalize()
131{
132 syncpoint_manager->release_syncpoint(name(), syncpoint_loop_start_);
133 syncpoint_manager->release_syncpoint(name(), syncpoint_loop_end_);
134 delete loop_start_;
135 delete loop_end_;
136}
137
138} // end namespace fawkes
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.
Base class for exceptions in Fawkes.
Definition: exception.h:36
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
void release_syncpoint(const std::string &component, RefPtr< SyncPoint > syncpoint)
Release a SyncPoint.
RefPtr< SyncPoint > get_syncpoint(const std::string &component, const std::string &identifier)
Get a SyncPoint.
A class for handling time.
Definition: time.h:93
Fawkes library namespace.