YARP
Yet Another Robot Platform
PeriodicThread.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * Copyright (C) 2006-2010 RobotCub Consortium
4  * All rights reserved.
5  *
6  * This software may be modified and distributed under the terms of the
7  * BSD-3-Clause license. See the accompanying LICENSE file for details.
8  */
9 
10 #include <yarp/os/PeriodicThread.h>
11 
12 #include <yarp/os/SystemClock.h>
15 
16 #include <cmath>
17 #include <mutex>
18 
19 using namespace yarp::os::impl;
20 using namespace yarp::os;
21 
22 
24 {
25 private:
26  double adaptedPeriod;
27  PeriodicThread* owner;
28  mutable std::mutex mutex;
29 
30  double elapsed;
31  double sleepPeriod;
32 
33  bool suspended;
34  double totalUsed; //total time taken iterations
35  unsigned int count; //number of iterations from last reset
36  unsigned int estPIt; //number of useful iterations for period estimation
37  double totalT; //time bw run, accumulated
38  double sumTSq; //cumulative sum sq of estimated period dT
39  double sumUsedSq; //cumulative sum sq of estimated thread tun
40  double previousRun; //time when last iteration started
41  double currentRun; //time when this iteration started
42  bool scheduleReset;
43 
44  using NowFuncPtr = double (*)();
45  using DelayFuncPtr = void (*)(double);
46  const NowFuncPtr nowFunc;
47  const DelayFuncPtr delayFunc;
48 
49  void _resetStat()
50  {
51  totalUsed = 0;
52  count = 0;
53  estPIt = 0;
54  totalT = 0;
55  sumUsedSq = 0;
56  sumTSq = 0;
57  elapsed = 0;
58  scheduleReset = false;
59  }
60 
61 public:
63  adaptedPeriod(p),
64  owner(owner),
65  elapsed(0),
66  sleepPeriod(adaptedPeriod),
67  suspended(false),
68  totalUsed(0),
69  count(0),
70  estPIt(0),
71  totalT(0),
72  sumTSq(0),
73  sumUsedSq(0),
74  previousRun(0),
75  currentRun(0),
76  scheduleReset(false),
77  nowFunc(useSystemClock == ShouldUseSystemClock::Yes ? SystemClock::nowSystem : yarp::os::Time::now),
78  delayFunc(useSystemClock == ShouldUseSystemClock::Yes ? SystemClock::delaySystem : yarp::os::Time::delay)
79  {
80  }
81 
82  void resetStat()
83  {
84  scheduleReset = true;
85  }
86 
87  double getEstimatedPeriod() const
88  {
89  double ret;
90  lock();
91  if (estPIt == 0) {
92  ret = 0;
93  } else {
94  ret = (totalT / estPIt);
95  }
96  unlock();
97  return ret;
98  }
99 
100  void getEstimatedPeriod(double& av, double& std) const
101  {
102  lock();
103  if (estPIt == 0) {
104  av = 0;
105  std = 0;
106  } else {
107  av = totalT / estPIt;
108  if (estPIt > 1) {
109  std = sqrt(((1.0 / (estPIt - 1)) * (sumTSq - estPIt * av * av)));
110  } else {
111  std = 0;
112  }
113  }
114  unlock();
115  }
116 
117  unsigned int getIterations() const
118  {
119  lock();
120  unsigned int ret = count;
121  unlock();
122  return ret;
123  }
124 
125  double getEstimatedUsed() const
126  {
127  double ret;
128  lock();
129  if (count < 1) {
130  ret = 0.0;
131  } else {
132  ret = totalUsed / count;
133  }
134  unlock();
135  return ret;
136  }
137 
138  void getEstimatedUsed(double& av, double& std) const
139  {
140  lock();
141  if (count < 1) {
142  av = 0;
143  std = 0;
144  } else {
145  av = totalUsed / count;
146  if (count > 1) {
147  std = sqrt((1.0 / (count - 1)) * (sumUsedSq - count * av * av));
148  } else {
149  std = 0;
150  }
151  }
152  unlock();
153  }
154 
155 
156  void step()
157  {
158  lock();
159  currentRun = nowFunc();
160 
161  if (scheduleReset) {
162  _resetStat();
163  }
164 
165  if (count > 0) {
166  double dT = (currentRun - previousRun);
167 
168  sumTSq += dT * dT;
169  totalT += dT;
170 
171  if (adaptedPeriod < 0) {
172  adaptedPeriod = 0;
173  }
174 
175  estPIt++;
176  }
177 
178  previousRun = currentRun;
179  unlock();
180 
181  if (!suspended) {
182  owner->run();
183  }
184 
185 
186  // At the end of each run of updateModule function, the thread is supposed
187  // to be suspended and release CPU to other threads.
188  // Calling a yield here will help the threads to alternate in the execution.
189  // Note: call yield BEFORE computing elapsed time, so that any time spent due to
190  // yield is took into account and the sleep time is correct.
191  yield();
192 
193  lock();
194  count++;
195  double elapsed = nowFunc() - currentRun;
196  //save last
197  totalUsed += elapsed;
198  sumUsedSq += elapsed * elapsed;
199  unlock();
200 
201  sleepPeriod = adaptedPeriod - elapsed; // everything is in [seconds] except period, for it is used in the interface as [ms]
202 
203  delayFunc(sleepPeriod);
204  }
205 
206  void run() override
207  {
208  while (!isClosing()) {
209  step();
210  }
211  }
212 
213  bool threadInit() override
214  {
215  return owner->threadInit();
216  }
217 
218  void threadRelease() override
219  {
220  owner->threadRelease();
221  }
222 
223  bool setPeriod(double period)
224  {
225  adaptedPeriod = period;
226  return true;
227  }
228 
229  double getPeriod() const
230  {
231  return adaptedPeriod;
232  }
233 
234  bool isSuspended() const
235  {
236  return suspended;
237  }
238 
239  void suspend()
240  {
241  suspended = true;
242  }
243 
244  void resume()
245  {
246  suspended = false;
247  }
248 
249  void afterStart(bool s) override
250  {
251  owner->afterStart(s);
252  }
253 
254  void beforeStart() override
255  {
256  owner->beforeStart();
257  }
258 
259  void lock() const
260  {
261  mutex.lock();
262  }
263 
264  void unlock() const
265  {
266  mutex.unlock();
267  }
268 };
269 
270 
272  mPriv(new Private(this, period, useSystemClock))
273 {
274 }
275 
277 {
278  delete mPriv;
279 }
280 
281 bool PeriodicThread::setPeriod(double period)
282 {
283  return mPriv->setPeriod(period);
284 }
285 
287 {
288  return mPriv->getPeriod();
289 }
290 
292 {
293  return mPriv->isSuspended();
294 }
295 
297 {
298  mPriv->close();
299 }
300 
302 {
303  mPriv->askToClose();
304 }
305 
307 {
308  mPriv->step();
309 }
310 
312 {
313  return mPriv->start();
314 }
315 
317 {
318  return mPriv->isRunning();
319 }
320 
322 {
323  mPriv->suspend();
324 }
325 
327 {
328  mPriv->resume();
329 }
330 
331 unsigned int PeriodicThread::getIterations() const
332 {
333  return mPriv->getIterations();
334 }
335 
337 {
338  return mPriv->getEstimatedPeriod();
339 }
340 
342 {
343  return mPriv->getEstimatedUsed();
344 }
345 
346 void PeriodicThread::getEstimatedPeriod(double& av, double& std) const
347 {
348  mPriv->getEstimatedPeriod(av, std);
349 }
350 
351 void PeriodicThread::getEstimatedUsed(double& av, double& std) const
352 {
353  mPriv->getEstimatedUsed(av, std);
354 }
355 
357 {
358  mPriv->resetStat();
359 }
360 
362 {
363  return true;
364 }
365 
367 {
368 }
369 
371 {
372 }
373 
374 void PeriodicThread::afterStart(bool success)
375 {
376  YARP_UNUSED(success);
377 }
378 
379 int PeriodicThread::setPriority(int priority, int policy)
380 {
381  return mPriv->setPriority(priority, policy);
382 }
383 
385 {
386  return mPriv->getPriority();
387 }
388 
390 {
391  return mPriv->getPolicy();
392 }
bool ret
void getEstimatedUsed(double &av, double &std) const
void getEstimatedPeriod(double &av, double &std) const
Private(PeriodicThread *owner, double p, ShouldUseSystemClock useSystemClock)
An abstraction for a periodic thread.
void resetStat()
Reset thread statistics.
bool setPeriod(double period)
Set the (new) period of the thread.
int getPriority() const
Query the current priority of the thread, if the OS supports that.
virtual void run()=0
Loop function.
virtual void beforeStart()
Called just before a new thread starts.
unsigned int getIterations() const
Return the number of iterations performed since last reset.
bool isRunning() const
Returns true when the thread is started, false otherwise.
PeriodicThread(double period, ShouldUseSystemClock useSystemClock=ShouldUseSystemClock::No)
Constructor.
int getPolicy() const
Query the current scheduling policy of the thread, if the OS supports that.
bool isSuspended() const
Returns true when the thread is suspended, false otherwise.
virtual bool threadInit()
Initialization method.
void resume()
Resume the thread if previously suspended.
virtual void afterStart(bool success)
Called just after a new thread starts (or fails to start), this is executed by the same thread that c...
void suspend()
Suspend the thread, the thread keeps running by doLoop is never executed.
int setPriority(int priority, int policy=-1)
Set the priority and scheduling policy of the thread, if the OS supports that.
void askToStop()
Stop the thread.
double getEstimatedUsed() const
Return the estimated duration of the run() function since last reset.
double getEstimatedPeriod() const
Return estimated period since last reset.
bool start()
Call this to start the thread.
void step()
Call this to "step" the thread rather than starting it.
double getPeriod() const
Return the current period of the thread.
void stop()
Call this to stop the thread, this call blocks until the thread is terminated (and releaseThread() ca...
virtual void threadRelease()
Release method.
An abstraction for a thread of execution.
Definition: ThreadImpl.h:26
yarp::rosmsg::std_msgs::Time Time
Definition: Time.h:24
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition: Time.cpp:147
void yield()
The calling thread releases its remaining quantum upon calling this function.
Definition: Time.cpp:141
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.
ShouldUseSystemClock
Definition: Time.h:23
The main, catch-all namespace for YARP.
Definition: environment.h:18
#define YARP_UNUSED(var)
Definition: api.h:159