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 
26 namespace 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  */
37 FawkesTimingThread::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  */
45 void
46 FawkesTimingThread::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  */
86 void
87 FawkesTimingThread::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  */
129 void
130 FawkesTimingThread::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.