RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
rtos_posix.cpp
Go to the documentation of this file.
1 /*
2  The Real-Time eXperiment Interface (RTXI)
3  Copyright (C) 2011 Georgia Institute of Technology, University of Utah,
4  Will Cornell Medical College
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include <chrono>
22 #include <iostream>
23 
24 #include "rtos.hpp"
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/resource.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include "debug.hpp"
38 
39 // NOLINTNEXTLINE
40 thread_local bool realtime_key = false;
41 // NOLINTNEXTLINE
42 thread_local int64_t DEFAULT_PERIOD_VALUE = 0;
43 // NOLINTNEXTLINE
44 thread_local int64_t* RT_PERIOD = &DEFAULT_PERIOD_VALUE;
45 
47 {
48  /*
49  * I want users to be very much aware that they aren't running in realtime.
50  */
51  std::cout << "***WARNING*** You are using the POSIX compatibility layer, "
52  "RTXI is NOT running in realtime!!!\n";
53  std::string strbuf(256, '\0');
54  int retval = mlockall(MCL_CURRENT | MCL_FUTURE); // NOLINT
55  strerror_r(errno, strbuf.data(), strbuf.size());
56  if (retval != 0) {
57  ERROR_MSG("RT::OS(POSIX)::initiate : failed to lock memory : {}", strbuf);
58  }
59  realtime_key = true;
61  RT_PERIOD = &(task->period);
62  task->thread_id = std::any(pthread_self());
63  return 0;
64 }
65 
67 {
68  munlockall();
69  realtime_key = false;
70  task->task_finished = true;
72 }
73 
74 int RT::OS::createTask(Task* task, void (*func)(void*), void* arg)
75 {
76  int result = 0;
77  // Should not be creating real-time tasks from another real-time task
78  if (RT::OS::isRealtime()) {
79  ERROR_MSG("RT::OS::createTask : Task cannot be created from rt context");
80  return -1;
81  }
82  auto wrapper = [](RT::OS::Task* tsk, void (*fn)(void*), void* args)
83  {
84  std::string strbuf(256, '\0');
85  auto resval = RT::OS::initiate(tsk);
86  strerror_r(errno, strbuf.data(), strbuf.size());
87  if (resval != 0) {
88  ERROR_MSG("RT::OS::createTask : RT::OS::initiate() : {}", strbuf);
89  }
90  fn(args);
91  RT::OS::shutdown(tsk);
92  };
93  std::thread thread_obj(wrapper, task, func, arg);
94  RT::OS::renameOSThread(thread_obj, std::string("RealTimeThread"));
95  if (thread_obj.joinable()) {
96  task->rt_thread = std::move(thread_obj);
97  } else {
98  result = -1;
99  }
100  return result;
101 }
102 
104 {
105  // Should not be deleting real-time tasks from another real-time task
106  if (RT::OS::isRealtime()) {
107  ERROR_MSG("RT::OS::createTask : Task cannot be deleted from rt context");
108  return;
109  }
110  // task->task_finished = true;
111  if (task->rt_thread.joinable()) {
112  task->rt_thread.join();
113  }
114 }
115 
116 bool RT::OS::isRealtime()
117 {
118  return realtime_key;
119 }
120 
121 int64_t RT::OS::getTime()
122 {
123  return std::chrono::steady_clock::now().time_since_epoch().count();
124 }
125 
126 int RT::OS::setPeriod(RT::OS::Task* task, int64_t period)
127 {
128  task->period = period;
129  return 0;
130 }
131 
132 int64_t RT::OS::getPeriod()
133 {
134  // This function should only ever be accessed withint a real-time context
135  if (RT_PERIOD == nullptr || !RT::OS::isRealtime()) {
136  return -1;
137  };
138  return *(RT_PERIOD);
139 }
140 
142 {
143  const int64_t current_time = RT::OS::getTime();
144  if (task->next_t < current_time) {
145  task->next_t = current_time + task->period;
146  return;
147  }
148  const int64_t wakeup_time = task->next_t;
149  task->next_t += task->period;
150 
151  const struct timespec ts = {wakeup_time / RT::OS::SECONDS_TO_NANOSECONDS,
152  wakeup_time % RT::OS::SECONDS_TO_NANOSECONDS};
153 
154  clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
155 }
156 
157 void RT::OS::renameOSThread(std::thread& thread, const std::string& name)
158 {
159  if (pthread_setname_np(thread.native_handle(), name.c_str()) != 0) {
160  ERROR_MSG("RT::OS::renameOSThread : unable to set name to thread");
161  }
162 }
163 
164 // NOLINTNEXTLINE
166 // NOLINTNEXTLINE
167 timespec last_proc_time;
168 
169 double RT::OS::getCpuUsage()
170 {
171  // Should not attempt this in the real-time thread
172  if (RT::OS::isRealtime()) {
173  return 0.0;
174  }
175 
176  double cpu_percent = 0.0;
177  int64_t cpu_time_elapsed = 0;
178  int64_t proc_time_elapsed = 0;
179 
180  timespec clock_time = {};
181  timespec proc_time = {};
182  // rusage resource_usage;
183 
184  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &proc_time);
185  clock_gettime(CLOCK_REALTIME, &clock_time);
186  // getrusage(RUSAGE_SELF, &resource_usage);
187 
188  cpu_time_elapsed = RT::OS::SECONDS_TO_NANOSECONDS
189  * (clock_time.tv_sec - last_clock_read.tv_sec)
190  + (clock_time.tv_nsec - last_clock_read.tv_nsec);
191  if (cpu_time_elapsed <= 0) {
192  return 0.0;
193  }
194  proc_time_elapsed = RT::OS::SECONDS_TO_NANOSECONDS
195  * (proc_time.tv_sec - last_proc_time.tv_sec)
196  + (proc_time.tv_nsec - last_proc_time.tv_nsec);
197  cpu_percent = 100.0 * (static_cast<double>(proc_time_elapsed))
198  / static_cast<double>(cpu_time_elapsed);
199 
200  last_proc_time = proc_time;
201  last_clock_read = clock_time;
202  return cpu_percent;
203 }
void ERROR_MSG(const std::string &errmsg, Args... args)
Definition: debug.hpp:36
int createTask(Task *task, void(*func)(void *), void *arg)
Definition: rtos_evl.cpp:86
int64_t getPeriod()
Definition: rtos_evl.cpp:150
int setPeriod(Task *task, int64_t period)
Definition: rtos_evl.cpp:144
const int64_t SECONDS_TO_NANOSECONDS
Definition: rtos.hpp:17
void shutdown(RT::OS::Task *task)
Definition: rtos_evl.cpp:72
bool isRealtime()
void sleepTimestep(Task *task)
Definition: rtos_evl.cpp:159
void renameOSThread(std::thread &thread, const std::string &name)
Definition: rtos_evl.cpp:175
int64_t getTime()
int initiate(RT::OS::Task *task)
Definition: rtos_evl.cpp:39
void deleteTask(Task *task)
Definition: rtos_evl.cpp:117
const int64_t DEFAULT_PERIOD
Definition: rtos.hpp:19
double getCpuUsage()
thread_local int64_t DEFAULT_PERIOD_VALUE
Definition: rtos_posix.cpp:42
timespec last_clock_read
Definition: rtos_posix.cpp:165
timespec last_proc_time
Definition: rtos_posix.cpp:167
thread_local int64_t * RT_PERIOD
Definition: rtos_posix.cpp:44
thread_local bool realtime_key
Definition: rtos_posix.cpp:40
bool task_finished
Definition: rtos.hpp:34
std::any thread_id
Definition: rtos.hpp:36
int64_t next_t
Definition: rtos.hpp:33
std::thread rt_thread
Definition: rtos.hpp:35
int64_t period
Definition: rtos.hpp:32