RTXI  3.0.0
The Real-Time eXperiment Interface Reference Manual
rtos_evl.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 <iostream>
22 
23 #include "rtos.hpp"
24 
25 #include <errno.h>
26 #include <evl/evl.h>
27 #include <pthread.h>
28 #include <sched.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "fifo.hpp"
33 
34 // NOLINTNEXTLINE
35 thread_local bool realtime_key = false;
36 // NOLINTNEXTLINE
37 thread_local int64_t* RT_PERIOD = nullptr;
38 
40 {
41  std::string strbuf(256, '\0');
42  int retval = evl_init();
43  if (retval != 0) {
44  strerror_r(errno, strbuf.data(), strbuf.size());
45  ERROR_MSG("RT::OS(EVL)::initiate : evl_init() : {}", strbuf);
46  return retval;
47  }
48 
49  // set high affinity to obtain real-time guarantees
50  struct sched_param param
51  {
52  };
53  param.sched_priority = 8;
54  retval = pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
55  if (retval != 0) {
56  ERROR_MSG("RT::OS(EVL)::initiate : Unable to set scheduling parameters");
57  return retval;
58  }
59 
60  int thread_fd = evl_attach_self("RTXI-RT-Thread:%d", getpid()); // NOLINT
61  if (thread_fd < 0) {
62  strerror_r(errno, strbuf.data(), strbuf.size());
63  ERROR_MSG("RT::OS(EVL)::initiate : evl_attach_self() : {}", strbuf);
64  }
65  realtime_key = true;
67  RT_PERIOD = &(task->period);
68  task->thread_id = std::any(thread_fd);
69  return retval;
70 }
71 
73 {
74  int retval = evl_detach_self();
75  std::string strbuf(256, '\0');
76  if (retval != 0) {
77  strerror_r(errno, strbuf.data(), strbuf.size());
78  ERROR_MSG("Unable to detach thread from evl core!");
79  ERROR_MSG("RT::OS(EVL)::shutdown : evl_detach_self() : {}", strbuf);
80  }
81  realtime_key = false;
82  task->task_finished = true;
83  RT_PERIOD = nullptr;
84 }
85 
86 int RT::OS::createTask(Task* task, void (*func)(void*), void* arg)
87 {
88  int result = 0;
89  // Should not be creating real-time tasks from another real-time task
90  if (RT::OS::isRealtime()) {
91  ERROR_MSG("RT::OS::createTask : Task cannot be created from rt context");
92  return -1;
93  }
94  auto wrapper = [](RT::OS::Task* tsk, void (*fn)(void*), void* args)
95  {
96  std::string strbuf(256, '\0');
97  auto resval = RT::OS::initiate(tsk);
98  if (resval != 0) {
99  strerror_r(errno, strbuf.data(), strbuf.size());
100  ERROR_MSG("RT::OS::createTask : RT::OS::initiate() : {}", strbuf);
101  // In the event that we fail to initiate real-time environment let's just
102  // quit
103  return;
104  }
105  fn(args);
106  RT::OS::shutdown(tsk);
107  };
108  std::thread thread_obj(wrapper, task, func, arg);
109  if (thread_obj.joinable()) {
110  task->rt_thread = std::move(thread_obj);
111  } else {
112  result = -1;
113  }
114  return result;
115 }
116 
118 {
119  // Should not be deleting real-time tasks from another real-time task
120  if (RT::OS::isRealtime()) {
121  ERROR_MSG("RT::OS::createTask : Task cannot be deleted from rt context");
122  return;
123  }
124  task->task_finished = true;
125  if (task->rt_thread.joinable()) {
126  task->rt_thread.join();
127  }
128 }
129 
130 bool RT::OS::isRealtime()
131 {
132  return realtime_key;
133 }
134 
135 int64_t RT::OS::getTime()
136 {
137  timespec tp = {};
138 
139  evl_read_clock(EVL_CLOCK_MONOTONIC, &tp);
140 
141  return RT::OS::SECONDS_TO_NANOSECONDS * tp.tv_sec + tp.tv_nsec;
142 }
143 
144 int RT::OS::setPeriod(RT::OS::Task* task, int64_t period)
145 {
146  task->period = period;
147  return 0;
148 }
149 
151 {
152  // This function should only ever be accessed withint a real-tim context
153  if (RT_PERIOD == nullptr || !RT::OS::isRealtime()) {
154  return -1;
155  };
156  return *(RT_PERIOD);
157 }
158 
160 {
161  const int64_t current_time = RT::OS::getTime();
162  if (task->next_t < current_time) {
163  task->next_t = current_time + task->period;
164  return;
165  }
166  int64_t wakeup_time = task->next_t;
167  task->next_t += task->period;
168 
169  const struct timespec ts = {wakeup_time / RT::OS::SECONDS_TO_NANOSECONDS,
170  wakeup_time % RT::OS::SECONDS_TO_NANOSECONDS};
171 
172  evl_sleep_until(EVL_CLOCK_MONOTONIC, &ts);
173 }
174 
175 void RT::OS::renameOSThread(std::thread& thread, const std::string& name)
176 {
177  if (RT::OS::isRealtime()) {
178  return;
179  }
180 
181  if (pthread_setname_np(thread.native_handle(), name.c_str()) != 0) {
182  ERROR_MSG("RT::OS::renameOSThread : unable to set name to thread");
183  }
184 }
185 
186 // NOLINTNEXTLINE
188 // NOLINTNEXTLINE
189 timespec last_proc_time;
190 
191 double RT::OS::getCpuUsage()
192 {
193  // Should not attempt this in the real-time thread
194  if (RT::OS::isRealtime()) {
195  return 0.0;
196  }
197 
198  double cpu_percent = 0.0;
199  int64_t cpu_time_elapsed = 0;
200  int64_t proc_time_elapsed = 0;
201 
202  timespec clock_time = {};
203  timespec proc_time = {};
204  // rusage resource_usage;
205 
206  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &proc_time);
207  clock_gettime(CLOCK_REALTIME, &clock_time);
208  // getrusage(RUSAGE_SELF, &resource_usage);
209 
210  cpu_time_elapsed = RT::OS::SECONDS_TO_NANOSECONDS
211  * (clock_time.tv_sec - last_clock_read.tv_sec)
212  + (clock_time.tv_nsec - last_clock_read.tv_nsec);
213  if (cpu_time_elapsed <= 0) {
214  return 0.0;
215  }
216  proc_time_elapsed = RT::OS::SECONDS_TO_NANOSECONDS
217  * (proc_time.tv_sec - last_proc_time.tv_sec)
218  + (proc_time.tv_nsec - last_proc_time.tv_nsec);
219  cpu_percent = 100.0 * (static_cast<double>(proc_time_elapsed))
220  / static_cast<double>(cpu_time_elapsed);
221 
222  last_proc_time = proc_time;
223  last_clock_read = clock_time;
224  return cpu_percent;
225 }
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()
timespec last_clock_read
Definition: rtos_evl.cpp:187
timespec last_proc_time
Definition: rtos_evl.cpp:189
thread_local int64_t * RT_PERIOD
Definition: rtos_evl.cpp:37
thread_local bool realtime_key
Definition: rtos_evl.cpp:35
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