Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laeThread.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Library: liblaelaps
6 //
7 // File: laeThread.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2015-09-05 10:00:50 -0700 (Sat, 05 Sep 2015) $
12  * $Rev: 4074 $
13  *
14  * \brief The Laelaps thread base class implementation.
15  *
16  * \par Copyright
17  * \h_copy 2015-2018. RoadNarrows LLC.\n
18  * http://www.roadnarrows.com\n
19  * All Rights Reserved
20  */
21 /*
22  * @EulaBegin@
23  *
24  * Unless otherwise stated explicitly, all materials contained are copyrighted
25  * and may not be used without RoadNarrows LLC's written consent,
26  * except as provided in these terms and conditions or in the copyright
27  * notice (documents and software) or other proprietary notice provided with
28  * the relevant materials.
29  *
30  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
31  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
32  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
33  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
34  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
35  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  *
37  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
39  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
40  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
41  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
42  *
43  * @EulaEnd@
44  */
45 ////////////////////////////////////////////////////////////////////////////////
46 
47 #include <sys/time.h>
48 #include <time.h>
49 #include <limits.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 #include <pthread.h>
53 #include <sched.h>
54 #include <math.h>
55 
56 #include "rnr/rnrconfig.h"
57 #include "rnr/log.h"
58 
59 #include "Laelaps/laelaps.h"
60 #include "Laelaps/laeUtils.h"
61 #include "Laelaps/laeThread.h"
62 
63 using namespace std;
64 using namespace laelaps;
65 
66 
67 // ---------------------------------------------------------------------------
68 // Local
69 // ---------------------------------------------------------------------------
70 
71 /*!
72  * \brief Print out timespec value to stderr.
73  *
74  * \param what What timespec.
75  * \param tx Timespec.
76  */
77 static void prts(string what, struct timespec ts)
78 {
79  fprintf(stderr, "%s = %ld.%09ld\n", what.c_str(), ts.tv_sec, ts.tv_nsec);
80 }
81 
82 
83 // ---------------------------------------------------------------------------
84 // LaeThread Class
85 // ---------------------------------------------------------------------------
86 
87 const double LaeThread::ThreadMinHz = 0.001; ///< once every 1000 seconds
88 
89 LaeThread::LaeThread(const string &strThreadName) :
90  m_strThreadName(strThreadName)
91 {
94 
95  pthread_mutex_init(&m_mutexSync, NULL);
96  pthread_cond_init(&m_condSync, NULL);
97 }
98 
100 {
101  terminateThread();
102 
103  pthread_cond_destroy(&m_condSync);
104  pthread_mutex_destroy(&m_mutexSync);
105 }
106 
107 int LaeThread::createThread(int nPriority)
108 {
109  pthread_attr_t attr;
110  struct sched_param parm;
111  double f;
112  int prioMin, prioMax;
113  int rc;
114 
115  // Initialize thread attributes.
116  pthread_attr_init(&attr);
117 
118  //
119  // Set thread scheduling policy.
120  // SCHED_FIFO:
121  // + preemptive priority scheduling
122  // + highest priority thread runs until it choses to block/sleep
123  // + when it unblock/wakes it goes to the end of the queue for its priority
124  //
125  pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
126 
127  //
128  // Get the system's minimum and maximum scheduling priorities for the given
129  // scheduling policy.
130  //
131  prioMin = sched_get_priority_min(SCHED_FIFO);
132  prioMax = sched_get_priority_max(SCHED_FIFO);
133 
134  //
135  // Map target priority between system min, max.
136  //
137  nPriority = cap(nPriority, ThreadPriorityMin, ThreadPriorityMax);
138  f = (double)nPriority/(double)(ThreadPriorityMax - ThreadPriorityMin + 1);
139  nPriority = (int)(f * (double)(prioMax - prioMin + 1) + 0.5);
140  m_nPriority = cap(nPriority, prioMin, prioMax);
141 
142  //
143  // Set the priority for this thread.
144  //
145  parm.sched_priority = m_nPriority;
146  pthread_attr_setschedparam(&attr, &parm);
147 
148  // Create kinematics thread.
149  rc = pthread_create(&m_thread, &attr, LaeThread::thread, (void *)this);
150 
151  // failure to create
152  if( rc != 0 )
153  {
154  LOGSYSERROR("pthread_create()");
156  return -LAE_ECODE_NO_EXEC;
157  }
158 
159  //
160  // Wait for thread to initialize.
161  //
162  lock();
163 
164  while( m_eState == ThreadStateUninit )
165  {
166  pthread_cond_wait(&m_condSync, &m_mutexSync);
167  }
168 
169  unlock();
170 
171  return LAE_OK;
172 }
173 
174 int LaeThread::runThread(double fHz)
175 {
176  int rc; // return code
177 
178  setHz(fHz);
179 
180  switch( m_eState )
181  {
182  case ThreadStateReady:
184  rc = LAE_OK;
185  break;
186 
187  case ThreadStateStart:
188  case ThreadStateRunning:
189  break;
190 
191  case ThreadStateExit:
192  default:
193  rc = -LAE_ECODE_GEN;
194  LOGERROR("%s thread in invalid state %d to run.",
195  m_strThreadName.c_str(), m_eState);
196  break;
197  }
198 
199  return rc;
200 }
201 
203 {
204  if( (m_eState == ThreadStateReady) ||
205  (m_eState == ThreadStateStart) ||
207  {
209  pthread_join(m_thread, NULL);
210  }
211  return LAE_OK;
212 }
213 
214 void LaeThread::setHz(const double fHz)
215 {
216  lock();
217 
218  m_fHz = fHz;
219 
220  if( m_fHz < ThreadMinHz )
221  {
222  m_fHz = ThreadMinHz;
223  }
224 
225  m_fTExec = 1.0 / m_fHz;
226 
227  //fprintf(stderr, "hz=%.2lf, m_fTExec=%lf\n", m_fHz, m_fTExec);
228 
229  m_tsExecPeriod.tv_sec = (long)floor(m_fTExec);
230  m_tsExecPeriod.tv_nsec = (long)fcap(
231  (m_fTExec-floor(m_fTExec)) * (double)BILLION,
232  0.0, (double)(BILLION-1) );
233 
234  //prts("execperiod", m_tsExecPeriod);
235 
237  m_tsJitter.tv_nsec += 50000000; // 5/100th of second
238 
239  m_nSlipErrCnt = 0;
240 
241  unlock();
242 }
243 
245 {
246  m_eState = eNewState;
247  pthread_cond_signal(&m_condSync);
248 }
249 
250 void LaeThread::timedWait(const struct timespec &tsTimeout)
251 {
252  lock();
253 
254  pthread_cond_timedwait(&m_condSync, &m_mutexSync, &tsTimeout);
255 
256  unlock();
257 }
258 
260 {
261  lock();
262 
263  while( m_eState == ThreadStateReady )
264  {
265  pthread_cond_wait(&m_condSync, &m_mutexSync);
266  }
267 
268  unlock();
269 }
270 
272 {
273  struct timespec tsNow; // now
274  struct timespec tsSlip = {0, 0}; // any slippage
275 
276  // now
277  clock_gettime(CLOCK_REALTIME, &tsNow);
278 
279  //prts("now ", tsNow);
280  //prts("sched", m_tsSched);
281 
282  //
283  // Next scheduled execution cycle is in the future. Block wait for the next
284  // cycle to begin.
285  //
286  if( tsNow < m_tsSched )
287  {
288  timedWait(m_tsSched); // block
289  clock_gettime(CLOCK_REALTIME, &tsNow); // now again
290  }
291 
292  //
293  // Determine any slip in task schedule.
294  //
295  if( tsNow > m_tsSched )
296  {
297  tsSlip = tsNow - m_tsSched;
298  }
299 
300  //
301  // Slipped by less than an execution cycles. Try to make up the time.
302  //
303  if( tsSlip <= m_tsExecPeriod )
304  {
305  m_tsSched = tsNow + m_tsExecPeriod - tsSlip; // next scheduled time
306  if( m_nSlipErrCnt > 0 )
307  {
308  --m_nSlipErrCnt;
309  }
310  }
311 
312  //
313  // Slipped by at least one full cycle, but within acceptable jitter.
314  //
315  else if( tsSlip < m_tsJitter )
316  {
317  m_tsSched = tsNow + m_tsExecPeriod;
318  }
319 
320  //
321  // Slipped badly by at least one full cycle.
322  //
323  else
324  {
325  m_tsSched = tsNow + m_tsExecPeriod;
326 
327  if( m_nSlipErrCnt < 1000 )
328  {
329  ++m_nSlipErrCnt;
330  }
331 
332  // log moderated slippage
333  if( m_nSlipErrCnt < 5 )
334  {
335  LOGWARN("%s thread: "
336  "Execution slipped by %ld.%09ld seconds.",
337  m_strThreadName.c_str(), tsSlip.tv_sec, tsSlip.tv_nsec);
338  }
339  }
340 }
341 
343 {
344 }
345 
347 {
348 }
349 
351 {
352  struct timespec tsDelta;
353 
354  tsDelta = m_tsExecThis - m_tsExecLast;
355 
356  printf("Thread %s: [%ld.%09ld] delta seconds.\n",
357  m_strThreadName.c_str(), tsDelta.tv_sec, tsDelta.tv_nsec);
358 
359  fflush(stdout);
360 }
361 
363 {
364 }
365 
366 void *LaeThread::thread(void *pArg)
367 {
368  LaeThread *pThis = (LaeThread *)pArg;
369  int oldstate;
370  int rc;
371 
372  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
373  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
374 
375  // Change state and signal calling thread that created this thread.
377 
378  // derived thread specific Ready state transition function
379  pThis->transToReady();
380 
381  LOGDIAG3("%s thread created at priority %d - ready to run.",
382  pThis->m_strThreadName.c_str(), pThis->m_nPriority);
383 
384  //
385  // Loop forever until exit
386  //
387  while( (pThis->m_eState != ThreadStateExit) )
388  {
389  switch( pThis->m_eState )
390  {
391  //
392  // Blocked on Ready "queue"
393  //
394  case ThreadStateReady:
395  pThis->readyBlock();
396  break;
397 
398  //
399  // Start running thread to execute task.
400  //
401  case ThreadStateStart:
402  LOGDIAG3("%s thread started - running at %.3lf Hz.",
403  pThis->m_strThreadName.c_str(), pThis->m_fHz);
404 
405  // force immediately execution of first task
406  clock_gettime(CLOCK_REALTIME, &pThis->m_tsSched);
407  pThis->m_tsExecThis = pThis->m_tsSched;
408  pThis->m_tsExecLast = pThis->m_tsSched;
409 
411 
412  // derived thread specific Running state transition function
413  pThis->transToRunning();
414  break;
415 
416  //
417  // Run wait,execute subcycle
418  //
419  case ThreadStateRunning:
420  pThis->schedBlock();
421  if( pThis->m_eState == ThreadStateRunning )
422  {
423  pThis->m_tsExecLast = pThis->m_tsExecThis;
424  clock_gettime(CLOCK_REALTIME, &pThis->m_tsExecThis);
425  pThis->lock();
426  pThis->exec();
427  pThis->unlock();
428  }
429  break;
430 
431  //
432  // Exit
433  //
434  case ThreadStateExit:
435  break;
436 
437  default:
438  LOGERROR("%d: Unexpected thread state.", pThis->m_eState);
439  pThis->m_eState = ThreadStateExit;
440  break;
441  }
442  }
443 
444  pThis->transToExit();
445 
446  LOGDIAG3("%s thread terminated.", pThis->m_strThreadName.c_str());
447 
448  return NULL;
449 }
virtual void transToRunning()
Ready to Running state transition function.
Definition: laeThread.cxx:346
Laelaps thread base class interface.
thread created but unitialized
Definition: laeThread.h:95
static const int LAE_ECODE_NO_EXEC
cannot execute error
Definition: laelaps.h:85
virtual int terminateThread()
Terminate the thread.
Definition: laeThread.cxx:202
pthread_t m_thread
pthread identifier
Definition: laeThread.h:200
void unlock()
Unlock the I2C bus.
Definition: laeThread.h:235
ThreadState m_eState
thread state
Definition: laeThread.h:197
int m_nSlipErrCnt
slipped error count leaky bucket
Definition: laeThread.h:211
static void prts(string what, struct timespec ts)
Print out timespec value to stderr.
Definition: laeThread.cxx:77
virtual void transToExit()
Any to Exit state transition function.
Definition: laeThread.cxx:362
pthread_mutex_t m_mutexSync
synchonization mutex
Definition: laeThread.h:198
void lock()
Lock the I2C bus.
Definition: laeThread.h:221
double fcap(double a, double min, double max)
Cap value within limits [min, max].
Definition: laeUtils.h:162
int m_nPriority
thread OS scheduling priority
Definition: laeThread.h:203
ThreadState
Kinematics thread states.
Definition: laeThread.h:93
std::string m_strThreadName
thread identifying name
Definition: laeThread.h:196
static void * thread(void *pArg)
The thread.
Definition: laeThread.cxx:366
static const int LAE_ECODE_GEN
general, unspecified error
Definition: laelaps.h:73
static const int ThreadPriorityMax
maximum scheduling priority
Definition: laeThread.h:86
virtual void readyBlock()
Block indefinitely while in the ready state.
Definition: laeThread.cxx:259
virtual ~LaeThread()
Destructor.
Definition: laeThread.cxx:99
struct timespec m_tsExecThis
start of this execution time stamp
Definition: laeThread.h:210
struct timespec m_tsSched
working scheduler time stamp
Definition: laeThread.h:208
thread created and blocked, ready to start
Definition: laeThread.h:96
thread exiting/exited
Definition: laeThread.h:99
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
virtual void schedBlock()
Block the thread until the next subcycle task is to be run.
Definition: laeThread.cxx:271
double m_fTExec
task execution cycle period (seconds)
Definition: laeThread.h:205
Laelaps common utilities.
int cap(int a, int min, int max)
Cap value within limits [min, max].
Definition: laeUtils.h:176
virtual void exec()
Execute task(s) within scheduled [sub]cycle.
Definition: laeThread.cxx:350
virtual void setHz(const double fHz)
Calculate thread new full cycle run rate.
Definition: laeThread.cxx:214
virtual int createThread(int nPriority)
Create the thread.
Definition: laeThread.cxx:107
void timedWait(const struct timespec &tsTimeout)
Timed wait until state change or time out.
Definition: laeThread.cxx:250
virtual void transToReady()
Uninitialized to Ready state transition function.
Definition: laeThread.cxx:342
double m_fHz
thread cycle run rate (Hertz)
Definition: laeThread.h:204
static const double ThreadMinHz
minimum thread Hertz
Definition: laeThread.h:88
struct timespec m_tsExecPeriod
task execution period (converted)
Definition: laeThread.h:206
virtual int runThread(const double fHz)
Run the thread.
Definition: laeThread.cxx:174
pthread_cond_t m_condSync
synchonization condition
Definition: laeThread.h:199
void changeState(ThreadState eNewState)
Change the thread state.
Definition: laeThread.cxx:244
struct timespec m_tsJitter
allowable scheduling jitter
Definition: laeThread.h:207
struct timespec m_tsExecLast
start of last execution time stamp
Definition: laeThread.h:209
Top-level package include file.
static const int LAE_OK
not an error, success
Definition: laelaps.h:71
static const int ThreadPriorityMin
minimum scheduling priority
Definition: laeThread.h:85