GCC Code Coverage Report


Directory: ./
File: pdserv/src/lib/Main.cpp
Date: 2025-07-20 04:11:05
Exec Total Coverage
Lines: 340 478 71.1%
Branches: 193 553 34.9%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright 2010 Richard Hacker (lerichi at gmx dot net)
4 * 2023 Florian Pose <fp@igh.de>
5 *
6 * This file is part of the pdserv library.
7 *
8 * The pdserv library is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation, either version 3 of the License, or (at
11 * your option) any later version.
12 *
13 * The pdserv library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>.
20 *
21 *****************************************************************************/
22
23 #include "Main.h"
24
25 #include "config.h"
26 #include "pdserv.h"
27
28 #include "Event.h"
29 #include "Parameter.h"
30 #include "Pointer.h"
31 #include "ShmemDataStructures.h"
32 #include "Signal.h"
33 #include "Task.h"
34
35 #include "../Config.h"
36 #include "../Database.h"
37 #include "../Debug.h"
38 #include "../Exceptions.h"
39 #include "../Session.h"
40
41 #include <cerrno> // EIO / errno
42 #include <cstdio> // perror()
43 #include <fstream> // ofstream
44 #include <iostream>
45
46 #include <pthread.h>
47 #include <signal.h> // signal()
48 #include <sys/mman.h> // mmap(), munmap()
49 #include <sys/time.h> // gettimeofday()
50 #include <sys/wait.h>
51 #include <unistd.h> // exit(), sleep()
52 #include <yaml.h>
53
54 #ifdef _PDSERV_CUSTOM_GCOV_HOOK
55 extern "C" void __gcov_dump(void);
56 #endif // _PDSERV_CUSTOM_GCOV_HOOK
57
58 /////////////////////////////////////////////////////////////////////////////
59 struct SDO {
60 enum {ParamChange = 1, PollSignal} type;
61
62 union {
63 struct {
64 const Parameter *parameter;
65 unsigned int offset;
66 unsigned int count;
67 } paramChangeReq;
68
69 struct {
70 int rv;
71 struct timespec time;
72 } paramChangeAck;
73
74 const Signal* signal;
75 struct timespec time;
76 };
77 };
78
79 /////////////////////////////////////////////////////////////////////////////
80 const double Main::bufferTime = 2.0;
81
82 /////////////////////////////////////////////////////////////////////////////
83 /////////////////////////////////////////////////////////////////////////////
84 157 Main::Main( const char *name, const char *version,
85 157 int (*gettime)(struct timespec*)):
86 PdServ::Main(name, version),
87
1/2
✓ Branch 3 taken 157 times.
✗ Branch 4 not taken.
314 pthread::Thread(std::string("pdserv-main")),
88
8/16
✓ Branch 5 taken 157 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 157 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 157 times.
✗ Branch 17 not taken.
✓ Branch 30 taken 157 times.
✗ Branch 31 not taken.
✓ Branch 44 taken 157 times.
✗ Branch 45 not taken.
✓ Branch 62 taken 157 times.
✗ Branch 63 not taken.
✓ Branch 72 taken 157 times.
✗ Branch 73 not taken.
✓ Branch 74 taken 157 times.
✗ Branch 75 not taken.
471 rttime(gettime ? gettime : &PdServ::Main::localtime)
89 157 {}
90
91 /////////////////////////////////////////////////////////////////////////////
92 471 Main::~Main()
93 {
94 157 int cancel_state;
95 157 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
96
97 157 terminate();
98 157 join();
99
100
101
2/2
✓ Branch 2 taken 314 times.
✓ Branch 3 taken 157 times.
785 while (task.size()) {
102
1/2
✓ Branch 3 taken 314 times.
✗ Branch 4 not taken.
314 delete task.front();
103 314 task.pop_front();
104 }
105
106
2/2
✓ Branch 2 taken 942 times.
✓ Branch 3 taken 157 times.
942 while (parameters.size()) {
107
1/2
✓ Branch 3 taken 942 times.
✗ Branch 4 not taken.
942 delete parameters.front();
108 942 parameters.pop_front();
109 }
110
111 // close pipes
112 157 ::close(terminatePipe);
113 157 ::close(ipcTx);
114 157 ::close(ipcRx);
115 157 ::close(nrtFeedbackPipe);
116
117
2/2
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 2 times.
157 if (pid > 0)
118 {
119 155 int stat = 0;
120 155 waitpid(pid, &stat, 0);
121 }
122
123
2/2
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 2 times.
157 if (shmem)
124 155 ::munmap(shmem, shmem_len);
125 157 pthread_setcancelstate(cancel_state, nullptr);
126 314 }
127
128 /////////////////////////////////////////////////////////////////////////////
129 149 void Main::setConfigFile(const char *file)
130 {
131 149 configFile = file;
132 149 }
133
134 /////////////////////////////////////////////////////////////////////////////
135 157 void Main::setParameterWriteLock(void (*fn)(int, void*), void* priv_data)
136 {
137 157 writelock_cb = fn;
138 157 writelock_data = priv_data;
139 157 }
140
141 /////////////////////////////////////////////////////////////////////////////
142 156 int Main::setup()
143 {
144 156 int rv;
145 156 int ipc_pipe[4][2];
146 time_t persistTimeout;
147 156 fd_set fds;
148 156 struct timeval timeout, now, delay;
149 156 struct EventData* eventData = nullptr;
150
151
1/2
✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
156 rv = readConfiguration();
152
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155 times.
156 if (rv)
153 1 return rv;
154
1/2
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
155 setupLogging(configFile);
155
1/2
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
155 persistTimeout = setupPersistent();
156
157
3/6
✓ Branch 3 taken 155 times.
✗ Branch 4 not taken.
✓ Branch 9 taken 155 times.
✗ Branch 10 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 5 times.
310 auto exportPath {m_config["eventexport"].toString()};
158
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
155 if (not exportPath.empty()) {
159 exportEvents(exportPath.c_str());
160 }
161
162 // Initialize library
163
1/2
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
155 rv = prefork_init();
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 if (rv)
165 return rv;
166
167 // Open a pipe between the two processes. This is used to inform the
168 // child that the parent has died
169
3/6
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 155 times.
465 if (::pipe(ipc_pipe[0]) or ::pipe(ipc_pipe[1]) or ::pipe(ipc_pipe[2])
170
2/4
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
310 or ::pipe(ipc_pipe[3])) {
171 rv = errno;
172 ::perror("pipe()");
173 return rv;
174 }
175
176 // Immediately split off a child. The parent returns to the caller so
177 // that he can get on with his job.
178 //
179 // The child continues from here.
180 //
181 // It is intentional that the child has the same process group as
182 // the parent so that it gets all the signals too.
183 155 pid = ::fork();
184
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 309 times.
309 if (pid < 0) {
185 // Some error occurred
186 ::perror("fork()");
187 return errno;
188 }
189
2/2
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 154 times.
309 else if (pid) {
190 // Parent here. Return to the caller
191
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 ::close(ipc_pipe[0][0]);
192 155 ipcTx = ipc_pipe[0][1];
193
194 155 ipcRx = ipc_pipe[1][0];
195
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 ::close(ipc_pipe[1][1]);
196
197
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 ::close(ipc_pipe[2][0]);
198 155 terminatePipe = ipc_pipe[2][1];
199
200 155 nrtFeedbackPipe = ipc_pipe[3][0];
201
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 ::close(ipc_pipe[3][1]);
202
203 // Send PID to the child, indicating that parent is running
204
2/4
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
155 if (::write(terminatePipe, &pid, sizeof(pid)) != sizeof(pid)) {
205 perror("Main::setup(): pid ::write() failed");
206 return errno;
207 }
208 // waiting for child to initialize
209
2/4
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
155 if (::read(nrtFeedbackPipe, &rv, sizeof(rv)) != sizeof(rv)) {
210 rv = errno;
211 perror("Main::setup(): nRT setup: read() failed");
212 ::close(nrtFeedbackPipe);
213 nrtFeedbackPipe = -1;
214 return rv;
215 }
216
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 150 times.
155 if (rv != 0) {
217 // child init has failed, it will terminate itself
218
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 ::close(nrtFeedbackPipe);
219 5 nrtFeedbackPipe = -1;
220 5 return rv;
221 }
222
223
1/2
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
150 return postfork_rt_setup();
224 }
225
226 // Only child runs after this point
227 154 ipcRx = ipc_pipe[0][0];
228
1/2
✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
154 ::close(ipc_pipe[0][1]);
229
230
1/2
✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
154 ::close(ipc_pipe[1][0]);
231 154 ipcTx = ipc_pipe[1][1];
232
233 154 terminatePipe = ipc_pipe[2][0];
234
1/2
✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
154 ::close(ipc_pipe[2][1]);
235
236
1/2
✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
154 ::close(ipc_pipe[3][0]);
237 154 nrtFeedbackPipe = ipc_pipe[3][1];
238
239
240 // Wait till main thread has been initialized
241
2/4
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
308 if (::read(terminatePipe, &pid, sizeof(pid)) != sizeof(pid)
242
2/4
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
154 or pid != getpid()) {
243 perror("Main::setup(): pid ::read() failed");
244 std::quick_exit(errno);
245 }
246
247 // Ignore common terminating signals
248 154 ::signal(SIGINT, SIG_IGN);
249 154 ::signal(SIGTERM, SIG_IGN);
250
251
1/2
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
154 postfork_nrt_setup();
252
253 154 rv = 0;
254 try {
255
2/2
✓ Branch 4 taken 149 times.
✓ Branch 5 taken 5 times.
154 startServers();
256 }
257
1/4
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
10 catch (PdServ::Errno const & err) {
258 5 rv = -err.getErrno();
259 }
260
261
2/4
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
154 if (::write(nrtFeedbackPipe, &rv, sizeof(rv)) != sizeof(rv)) {
262 perror("Main::setup(): nRT setup: write() failed");
263 close(nrtFeedbackPipe);
264 std::quick_exit(errno);
265 }
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 154 times.
154 if (rv == -1)
267 {
268 close(nrtFeedbackPipe);
269 #ifdef _PDSERV_CUSTOM_GCOV_HOOK
270 __gcov_dump();
271 #endif
272 std::quick_exit(-1);
273 }
274
275 try {
276
1/2
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
154 postfork_nrt_subscribe_persistent_signals();
277 } catch (PdServ::Main::RtProcessExited&) {
278 goto exit;
279 }
280
281 154 ::gettimeofday(&timeout, 0);
282 154 timeout.tv_sec += persistTimeout;
283
284 154 FD_ZERO(&fds);
285
286 // Stay in this loop until real-time thread exits, in which case
287 // ipc_pipe[0] becomes readable
288 154 eventData = eventDataStart;
289 154 ipc_error = false;
290 723 do {
291 try {
292
0/2
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2631 for (TaskList::iterator it = task.begin();
293
2/2
✓ Branch 6 taken 1754 times.
✓ Branch 7 taken 877 times.
2631 it != task.end(); ++it)
294
1/2
✓ Branch 6 taken 1754 times.
✗ Branch 7 not taken.
1754 static_cast<Task*>(*it)->nrt_update();
295 } catch (RtProcessExited&) {
296 rv = 0;
297 break;
298 }
299
300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 877 times.
877 if (persistTimeout) {
301 if (::gettimeofday(&now, 0)) {
302 rv = errno;
303 break;
304 }
305
306 if ( now.tv_sec >= timeout.tv_sec) {
307 timeout.tv_sec += persistTimeout;
308 savePersistent();
309 }
310 }
311
312
2/2
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 877 times.
931 while (eventData != *eventDataWp) {
313
1/2
✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
54 newEvent(eventData->event, eventData->index,
314 27 eventData->state, &eventData->time);
315
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
27 if (++eventData == eventDataEnd)
316 eventData = eventDataStart;
317 }
318
319
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 877 times.
877 FD_SET(terminatePipe, &fds);
320 877 delay.tv_sec = 0;
321 877 delay.tv_usec = 50000; // 20Hz
322
1/2
✓ Branch 4 taken 877 times.
✗ Branch 5 not taken.
877 rv = ::select(terminatePipe + 1, &fds, 0, 0, &delay);
323
4/6
✓ Branch 0 taken 723 times.
✓ Branch 1 taken 154 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 723 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 723 times.
877 } while (!(rv or ipc_error));
324
325 154 exit:
326 // Ignore rv if ipc_pipe[0] is readable
327
1/2
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
154 if (rv == 1)
328 154 rv = 0;
329
330
1/2
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
154 stopServers();
331
332
1/2
✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
154 close(nrtFeedbackPipe);
333 #ifdef _PDSERV_CUSTOM_GCOV_HOOK
334
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
154 __gcov_dump();
335 #endif
336 std::quick_exit(rv);
337 }
338
339 /////////////////////////////////////////////////////////////////////////////
340
341 3 void Main::sleep(int msecs) const
342 {
343 3 fd_set fds;
344 3 FD_ZERO(&fds);
345
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 FD_SET(terminatePipe, &fds);
346
347 3 struct timeval delay;
348
349 3 delay.tv_sec = msecs / 1000;
350 3 delay.tv_usec = (msecs - delay.tv_sec * 1000) * 1000;
351
352
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 const int rv = ::select(terminatePipe + 1, &fds, 0, 0, &delay);
353
2/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
3 if (rv == 1 or (rv == -1 && errno != EINTR)) {
354 throw PdServ::Main::RtProcessExited{};
355 }
356 3 }
357
358 /////////////////////////////////////////////////////////////////////////////
359 156 int Main::readConfiguration()
360 {
361 const char *env;
362 156 const char *err = 0;
363
364 // Load custom configuration file
365
2/2
✓ Branch 2 taken 148 times.
✓ Branch 3 taken 8 times.
156 if (!configFile.empty()) {
366 148 err = m_config.load(configFile.c_str());
367
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 147 times.
148 if (err)
368 std::cerr
369 << "Error loading configuration file "
370 << configFile << " specified on command line: "
371 1 << err << std::endl;
372 else {
373 log_debug("Loaded specified configuration file %s",
374 configFile.c_str());
375 }
376 }
377
3/8
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
8 else if ((env = ::getenv("PDSERV_CONFIG")) and ::strlen(env)) {
378 // Try to load environment configuration file
379 err = m_config.load(env);
380
381 if (err)
382 std::cerr << "Error loading configuration file " << env
383 << " specified in environment variable PDSERV_CONFIG: "
384 << err << std::endl;
385 else {
386 configFile = env;
387 log_debug("Loaded ENV config %s", env);
388 }
389 }
390 else {
391 // Try to load default configuration file
392 8 const char *f = QUOTE(SYSCONFDIR) "/pdserv.conf";
393
394
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if (::access(f, R_OK) == 0) {
395 // file exists
396 8 err = m_config.load(f);
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (err) {
398 std::cerr << "Error loading default configuration file "
399 << f << ": " << err << std::endl;
400 }
401 else {
402 8 configFile = f;
403 log_debug("Loaded default configuration file %s", f);
404 }
405 }
406 else {
407 std::cerr << "No configuration file found at " << f
408 << ". Using defaults." << std::endl;
409 }
410 }
411
412 156 if (!m_config) {
413 log_debug("No configuration loaded");
414 }
415
416
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155 times.
156 if (err) {
417 1 return -1;
418 }
419
420 155 return 0;
421 }
422
423 /////////////////////////////////////////////////////////////////////////////
424 1314 PdServ::Config Main::config(const char* key) const
425 {
426 1314 return m_config[key];
427 }
428
429 /////////////////////////////////////////////////////////////////////////////
430 314 Task* Main::addTask(double sampleTime, const char *name)
431 {
432
2/4
✓ Branch 7 taken 314 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 314 times.
✗ Branch 12 not taken.
314 task.push_back(new Task(this, task.size(), sampleTime, name));
433 314 return task.back();
434 }
435
436 /////////////////////////////////////////////////////////////////////////////
437 211 int Main::gettime(struct timespec* t) const
438 {
439 211 return rttime(t);
440 }
441
442 /////////////////////////////////////////////////////////////////////////////
443 314 Event* Main::addEvent (
444 const char *path, size_t nelem, const char * const *messages)
445 {
446
2/4
✓ Branch 5 taken 314 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 314 times.
✗ Branch 11 not taken.
314 events.push_back(std::unique_ptr<Event>{new Event(this, path, nelem, messages)});
447
448 314 return events.back().get();
449 }
450
451 /////////////////////////////////////////////////////////////////////////////
452 942 Parameter* Main::addParameter( const char *path,
453 unsigned int mode, const PdServ::DataType& datatype,
454 void *addr, size_t n, const size_t *dim)
455 {
456
1/2
✓ Branch 4 taken 942 times.
✗ Branch 5 not taken.
1884 parameters.push_back(
457
1/2
✓ Branch 3 taken 942 times.
✗ Branch 4 not taken.
1884 new Parameter(this, addr, path, mode, datatype, n, dim));
458
459 942 return parameters.back();
460 }
461
462 /////////////////////////////////////////////////////////////////////////////
463 16 void Main::setEvent(const Event* event,
464 size_t element, PdServ::Event::Priority prio, const timespec *time)
465 {
466 32 pthread::MutexLock lock(eventMutex);
467
468 16 struct EventData *eventData = *eventDataWp;
469 16 eventData->event = event;
470 16 eventData->index = element;
471 16 eventData->state = prio;
472 16 eventData->time = *time;
473
474
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
16 if (++eventData == eventDataEnd)
475 eventData = eventDataStart;
476 16 *eventDataWp = eventData;
477 16 }
478
479 /////////////////////////////////////////////////////////////////////////////
480 11 void Main::resetEvent(const Event* event,
481 size_t element, const timespec *time)
482 {
483 22 pthread::MutexLock lock(eventMutex);
484
485 11 struct EventData *eventData = *eventDataWp;
486 11 eventData->event = event;
487 11 eventData->index = element;
488 11 eventData->state = -1;
489 11 eventData->time = *time;
490
491
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 if (++eventData == eventDataEnd)
492 eventData = eventDataStart;
493 11 *eventDataWp = eventData;
494 11 }
495
496 /////////////////////////////////////////////////////////////////////////////
497 static int yaml_output_handler(
498 void *data,
499 unsigned char *buffer,
500 size_t size)
501 {
502 std::ostream* out = static_cast<std::ostream *>(data);
503 out->write(reinterpret_cast<char *>(buffer), size);
504 return out->good() ? 1 : 0;
505 }
506
507 /////////////////////////////////////////////////////////////////////////////
508 static int emit_helper(yaml_emitter_t *emitter, yaml_event_t *event, int line)
509 {
510 int ret = yaml_emitter_emit(emitter, event);
511 if (not ret) {
512 log_debug("YAML emit error in line %u: %s", line, emitter->problem);
513 }
514 return ret;
515 }
516 #define emit(emitter, event) \
517 if (not emit_helper(emitter, event, __LINE__)) goto out_error;
518
519 /////////////////////////////////////////////////////////////////////////////
520 int Main::exportEvents(const char *path) const
521 {
522 int success {0};
523 log_debug("Exporting events to %s", path);
524
525 std::ofstream ofs(path);
526 if (not ofs) {
527 std::cerr << "Failed to open file.\n";
528 log_debug("Failed to open %s", path);
529 return -EIO;
530 }
531
532 yaml_emitter_t emitter;
533 yaml_event_t event;
534
535 if (not yaml_emitter_initialize(&emitter)) {
536 log_debug("Failed to initialize YAML emitter.");
537 return -EIO;
538 }
539 yaml_emitter_set_output(&emitter, yaml_output_handler, &ofs);
540
541 // Start stream
542 yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
543 emit(&emitter, &event);
544
545 // Start document
546 yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
547 emit(&emitter, &event);
548
549 // Start Mapping
550 yaml_mapping_start_event_initialize(&event, NULL, NULL, 1,
551 YAML_BLOCK_MAPPING_STYLE);
552 emit(&emitter, &event);
553
554 // Version
555 yaml_scalar_event_initialize(&event, NULL, NULL,
556 (yaml_char_t *) "version", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
557 emit(&emitter, &event);
558
559 yaml_scalar_event_initialize(&event, NULL, NULL,
560 (yaml_char_t *) "1",
561 -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
562 emit(&emitter, &event);
563
564 // Events
565 yaml_scalar_event_initialize(&event, NULL, NULL,
566 (yaml_char_t *) "events", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
567 emit(&emitter, &event);
568
569 // Start sequence with events
570 yaml_sequence_start_event_initialize(&event, NULL, NULL, 1,
571 YAML_BLOCK_SEQUENCE_STYLE);
572 emit(&emitter, &event);
573
574 for (auto pdserv_event : getEvents()) {
575 yaml_mapping_start_event_initialize(&event, NULL, NULL, 1,
576 YAML_BLOCK_MAPPING_STYLE);
577 emit(&emitter, &event);
578
579 yaml_scalar_event_initialize(&event, NULL, NULL,
580 (yaml_char_t *) "path", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
581 emit(&emitter, &event);
582
583 yaml_scalar_event_initialize(&event, NULL, NULL,
584 (yaml_char_t *) pdserv_event->path.c_str(),
585 -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
586 emit(&emitter, &event);
587
588 yaml_scalar_event_initialize(&event, NULL, NULL,
589 (yaml_char_t *) "nelem", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
590 emit(&emitter, &event);
591
592 std::string nelem { std::to_string(pdserv_event->nelem) };
593 yaml_scalar_event_initialize(&event, NULL, NULL,
594 (yaml_char_t *) nelem.c_str(),
595 -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
596 emit(&emitter, &event);
597
598 if (pdserv_event->message) {
599 yaml_scalar_event_initialize(&event, NULL, NULL,
600 (yaml_char_t *) "messages", -1, 1, 0,
601 YAML_PLAIN_SCALAR_STYLE);
602 emit(&emitter, &event);
603
604 // Start sequence with messages
605 yaml_sequence_start_event_initialize(&event, NULL, NULL, 1,
606 YAML_BLOCK_SEQUENCE_STYLE);
607 emit(&emitter, &event);
608
609 for (unsigned int i = 0; i < pdserv_event->nelem; i++) {
610 yaml_scalar_event_initialize(&event, NULL, NULL,
611 (yaml_char_t *) pdserv_event->message[i],
612 -1, 1, 0, YAML_PLAIN_SCALAR_STYLE);
613 emit(&emitter, &event);
614 }
615
616 // End sequence
617 yaml_sequence_end_event_initialize(&event);
618 emit(&emitter, &event);
619 }
620
621 yaml_mapping_end_event_initialize(&event);
622 emit(&emitter, &event);
623 }
624
625 // End sequence
626 yaml_sequence_end_event_initialize(&event);
627 emit(&emitter, &event);
628
629 // End mapping
630 yaml_mapping_end_event_initialize(&event);
631 emit(&emitter, &event);
632
633 // End document
634 yaml_document_end_event_initialize(&event, 1);
635 emit(&emitter, &event);
636
637 // End stream
638 yaml_stream_end_event_initialize(&event);
639 emit(&emitter, &event);
640
641 success = 1;
642
643 out_error:
644 yaml_emitter_delete(&emitter);
645 ofs.close();
646
647 if (not ofs or not success) {
648 log_debug("Failed to write all YAML data.");
649 return -EIO;
650 }
651
652 return 0;
653 }
654
655 /////////////////////////////////////////////////////////////////////////////
656 43 int Main::setValue(const PdServ::ProcessParameter* p,
657 const char* buf, size_t offset, size_t count,
658 const char** value, const struct timespec** time)
659 {
660 86 pthread::MutexLock lock(sdoMutex);
661
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 const Parameter* param = static_cast<const Parameter*>(p);
662 43 char* shmAddr = param->shmAddr + offset;
663
664 // Backup old values in case of write failure
665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 char backup[count];
666
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 std::copy(shmAddr, shmAddr + count, backup);
667
668 // Copy new data to shared memory
669
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 std::copy(buf, buf + count, shmAddr);
670
671 // Setup change request
672 43 struct SDO sdo;
673 43 sdo.type = SDO::ParamChange;
674 43 sdo.paramChangeReq.parameter = param;
675 43 sdo.paramChangeReq.offset = offset;
676 43 sdo.paramChangeReq.count = count;
677
678
2/4
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 43 times.
43 if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo)) {
679 log_debug("Main::setValue(): SDO ::write() failed");
680 ipc_error = true;
681 return -EIO;
682 }
683
684
2/4
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 43 times.
43 if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo)) {
685 log_debug("Main::setValue(): SDO ::read() failed");
686 ipc_error = true;
687 return -EIO;
688 }
689
690
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if (!sdo.paramChangeAck.rv)
691 43 param->mtime = sdo.paramChangeAck.time; // Save time of update
692 else
693 // Write failure. Restore data
694 std::copy(backup, backup + count, shmAddr);
695
696 43 *value = param->shmAddr;
697 43 *time = &param->mtime;
698
699 86 return sdo.paramChangeAck.rv;
700 }
701
702 /////////////////////////////////////////////////////////////////////////////
703 5 void Main::initializeParameter(PdServ::Parameter* p,
704 const char* data, const struct timespec* mtime,
705 const PdServ::Signal* s)
706 {
707
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (data) {
708 log_debug("Restoring %s", p->path.c_str());
709
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 const Parameter *parameter = static_cast<const Parameter*>(p);
710 4 std::copy(data, data + parameter->memSize, parameter->addr);
711 4 parameter->mtime = *mtime;
712 }
713
714
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (s) {
715
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 const Signal* signal = static_cast<const Signal*>(s);
716 4 signal->task->makePersistent(signal);
717 }
718 5 }
719
720 /////////////////////////////////////////////////////////////////////////////
721 3 bool Main::getPersistentSignalValue(const PdServ::Signal *s,
722 char* buf, struct timespec* time)
723 {
724 3 const struct timespec* t;
725
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 bool rv = static_cast<const Signal*>(s)->task->getPersistentValue(
726
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 s, buf, &t);
727
728
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (rv and time)
729 3 *time = *t;
730
731 3 return rv;
732 }
733
734 /////////////////////////////////////////////////////////////////////////////
735 57 int Main::getValue(const Signal *signal, void* dest, struct timespec* time)
736 {
737 114 pthread::MutexLock lock(sdoMutex);
738 57 struct SDO sdo;
739
740 57 sdo.type = SDO::PollSignal;
741 57 sdo.signal = signal;
742
743
2/4
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 57 times.
57 if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo)) {
744 log_debug("Main::getValue(): SDO ::write() failed");
745 ipc_error = true;
746 return -EIO;
747 }
748
749
2/4
✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 57 times.
57 if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo)) {
750 log_debug("Main::getValue(): SDO ::read() failed");
751 ipc_error = true;
752 return -EIO;
753 }
754
755
1/2
✓ Branch 10 taken 57 times.
✗ Branch 11 not taken.
57 std::copy(signalData, signalData + signal->memSize,
756 reinterpret_cast<char*>(dest));
757
758
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
57 if (time)
759 57 *time = sdo.time;
760
761 57 return 0;
762 }
763
764 /////////////////////////////////////////////////////////////////////////////
765 6 PdServ::Parameter* Main::findParameter(const std::string& path) const
766 {
767
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
28 for (ParameterList::const_iterator it = parameters.begin();
768
1/2
✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
28 it != parameters.end(); ++it)
769
2/2
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 22 times.
28 if ((*it)->path == path)
770 6 return *it;
771 return 0;
772 }
773
774 /////////////////////////////////////////////////////////////////////////////
775 // Orangization of shared memory:
776 // struct SDOStruct sdo
777 // char parameterData (binary data of all parameters)
778 // char pdoData
779 // struct EventData eventDataStart
780 //
781 155 int Main::prefork_init()
782 {
783 155 size_t numTasks = task.size();
784
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 size_t taskMemSize[numTasks];
785 size_t i, eventCount;
786 155 size_t maxSignalSize = 0;
787
788 // Find out the largest signal size to reserve space in
789 // shared memory for polling
790 465 for (TaskList::const_iterator it = task.begin();
791
2/2
✓ Branch 6 taken 310 times.
✓ Branch 7 taken 155 times.
465 it != task.end(); ++it) {
792 310 std::list<const PdServ::Signal*> signals =
793
1/2
✓ Branch 5 taken 310 times.
✗ Branch 6 not taken.
620 static_cast<PdServ::Task*>(*it)->getSignals();
794
795
2/2
✓ Branch 1 taken 930 times.
✓ Branch 2 taken 310 times.
2170 while (!signals.empty()) {
796 930 const PdServ::Signal* s = signals.front();
797
798
2/2
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 775 times.
930 if (s->memSize > maxSignalSize)
799 155 maxSignalSize = s->memSize;
800
801 930 signals.pop_front();
802 }
803
804 }
805 155 shmem_len += maxSignalSize;
806
807 // The following two variables are used to organize parameters according
808 // to the size of their elements so that their data type alignment is
809 // correct.
810 //
811 // dataTypeIndex[] maps the data type to the index in parameterDataOffset,
812 // e.g. a parameter with data type double (sizeof() = 8) will then go into
813 // container parameterDataOffset[dataTypeIndex[8]]
814 //
815 // parameterDataOffset[] holds the start index of a data types with
816 // 8, 4, 2 and 1 bytes alignment
817 155 const size_t dataTypeIndex[PdServ::DataType::maxWidth+1] = {
818 3 /*0*/, 3 /*1*/, 2 /*2*/, 3 /*3*/,
819 1 /*4*/, 3 /*5*/, 3 /*6*/, 3 /*7*/, 0 /*8*/
820 };
821 155 size_t parameterDataOffset[5] = {0, 0, 0, 0, 0}; // need one extra!
822
823 1085 for (ParameterList::iterator it = parameters.begin();
824
2/2
✓ Branch 6 taken 930 times.
✓ Branch 7 taken 155 times.
1085 it != parameters.end(); it++) {
825 930 const Parameter *p = static_cast<const Parameter*>(*it);
826
827 // Push the next smaller data type forward by the parameter's
828 // memory requirement
829
1/2
✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
930 parameterDataOffset[dataTypeIndex[p->dtype.align()] + 1] += p->memSize;
830 }
831
832 // Accumulate the offsets so that they follow each other in the shared
833 // data space. This also has the effect, that the value of
834 // parameterDataOffset[4] is the total memory requirement of all
835 // parameters
836
2/2
✓ Branch 0 taken 620 times.
✓ Branch 1 taken 155 times.
775 for (i = 1; i < 5; ++i)
837 620 parameterDataOffset[i] += parameterDataOffset[i-1];
838
839 // Extend shared memory size with the parameter memory requirement
840 // and as many sdo's for every parameter.
841 155 shmem_len += parameterDataOffset[4];
842
843 // Now check how much memory is required for events
844 155 eventCount = 0;
845
2/2
✓ Branch 8 taken 310 times.
✓ Branch 9 taken 155 times.
465 for (EventList::iterator it = events.begin(); it != events.end(); ++it)
846 310 eventCount += (*it)->nelem;
847
848 // Increase shared memory by the number of events as well as
849 // enough capacity to store eventDataLen event changes
850 155 const size_t eventLen = 10; // Arbitrary
851 155 shmem_len += sizeof(*eventDataStart) * eventLen * eventCount;
852
853 155 shmem_len += sizeof(*eventDataWp); // Memory location for write pointer
854
855 // Find out the memory requirement for the tasks to pipe their variables
856 // out of the real time environment
857 155 i = 0;
858
2/4
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 155 times.
✗ Branch 7 not taken.
155 size_t bufLimit = m_config["sharedmemlimit"].toUInt();
859 465 for (TaskList::const_iterator it = task.begin();
860
2/2
✓ Branch 6 taken 310 times.
✓ Branch 7 taken 155 times.
465 it != task.end(); ++it) {
861
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 taskMemSize[i] = ptr_align(
862 310 static_cast<const Task*>(*it)->getShmemSpace(bufferTime, bufLimit));
863 310 shmem_len += taskMemSize[i++];
864 }
865
866 // Fudge factor. Every ptr_align can silently increase the pointer in
867 // shmem by sizeof(unsigned long).
868 // At the moment there are roughly 6 ptr_align's, take 10 to make sure!
869 155 shmem_len += (10 + task.size())*sizeof(unsigned long);
870
871 155 shmem = ::mmap(0, shmem_len, PROT_READ | PROT_WRITE,
872 MAP_SHARED | MAP_ANON, -1, 0);
873
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
155 if (MAP_FAILED == shmem) {
874 // log(LOGCRIT, "could not mmap
875 // err << "mmap(): " << strerror(errno);
876 ::perror("mmap()");
877 return errno;
878 }
879
880 // Clear memory; at the same time prefault it, so it does not
881 // get swapped out
882
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
155 ::memset(shmem, 0, shmem_len);
883
884 // Now spread the shared memory for the users thereof
885
886 // 1: Parameter data
887 155 parameterData = ptr_align<char>(shmem);
888 1085 for (ParameterList::iterator it = parameters.begin();
889
2/2
✓ Branch 6 taken 930 times.
✓ Branch 7 taken 155 times.
1085 it != parameters.end(); it++) {
890 930 Parameter *p = *it;
891 2790 p->shmAddr = parameterData
892
1/2
✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
1860 + parameterDataOffset[dataTypeIndex[p->dtype.align()]];
893
1/2
✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
930 parameterDataOffset[dataTypeIndex[p->dtype.align()]] += p->memSize;
894
895
1/2
✓ Branch 13 taken 930 times.
✗ Branch 14 not taken.
930 std::copy(p->addr, p->addr + p->memSize, p->shmAddr);
896 }
897
898 // 2: Signal data area for polling
899 155 signalData = ptr_align<char>(parameterData + parameterDataOffset[4]);
900
901 // 3: Streaming data for tasks
902 155 char* buf = ptr_align<char>(signalData + maxSignalSize);
903 155 i = 0;
904
2/2
✓ Branch 8 taken 310 times.
✓ Branch 9 taken 155 times.
465 for (TaskList::iterator it = task.begin(); it != task.end(); ++it) {
905
1/2
✓ Branch 8 taken 310 times.
✗ Branch 9 not taken.
310 static_cast<Task*>(*it)->prepare(buf, buf + taskMemSize[i]);
906 310 buf = ptr_align<char>(buf + taskMemSize[i++]);
907 }
908
909 // 4: Event data
910 155 struct EventData** ptr = ptr_align<struct EventData*>(buf);
911 155 eventDataWp = ptr++;
912 155 eventDataStart = ptr_align<struct EventData>(ptr++);
913 155 eventDataEnd = eventDataStart + eventLen * eventCount;
914 155 *eventDataWp = eventDataStart;
915
916 log_debug("shmem=%p shmem_end=%p(%zu)\n"
917 "param=%p(%zi)\n"
918 "stream=%p(%zi)\n"
919 "end=%p(%zi)\n",
920 shmem, (char*)shmem + shmem_len, shmem_len,
921 signalData, (char*)signalData - (char*)shmem,
922 eventDataWp, (char*)eventDataWp - (char*)shmem,
923 eventDataEnd, (char*)eventDataEnd - (char*)shmem);
924
925
1/2
✗ Branch 9 not taken.
✓ Branch 10 taken 155 times.
155 if ((void*)(eventDataEnd + 1) > (void*)((char*)shmem + shmem_len)) {
926 log_debug("Not enough memory");
927 return ENOMEM;
928 }
929
930 310 return 0;
931 }
932
933 /////////////////////////////////////////////////////////////////////////////
934 150 int Main::postfork_rt_setup()
935 {
936 // Set the priority of the real time helper thread. This priority
937 // should be set to the highest real time priority.
938 150 set_priority(sched_get_priority_max(SCHED_FIFO));
939
940 // Parent here; go back to the caller
941 450 for (TaskList::iterator it = task.begin();
942
2/2
✓ Branch 6 taken 300 times.
✓ Branch 7 taken 150 times.
450 it != task.end(); ++it)
943
1/2
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
300 static_cast<Task*>(*it)->rt_init();
944
945 // Start supervisor thread
946 150 return start();
947 }
948
949 /////////////////////////////////////////////////////////////////////////////
950 154 int Main::postfork_nrt_setup()
951 {
952 // Parent here; go back to the caller
953 462 for (TaskList::iterator it = task.begin();
954
2/2
✓ Branch 6 taken 308 times.
✓ Branch 7 taken 154 times.
462 it != task.end(); ++it)
955
1/2
✓ Branch 6 taken 308 times.
✗ Branch 7 not taken.
308 static_cast<Task*>(*it)->nrt_init();
956 154 return 0;
957 }
958
959 /////////////////////////////////////////////////////////////////////////////
960
961 154 void Main::postfork_nrt_subscribe_persistent_signals()
962 {
963
2/2
✓ Branch 6 taken 308 times.
✓ Branch 7 taken 154 times.
462 for (auto i : task)
964
1/2
✓ Branch 4 taken 308 times.
✗ Branch 5 not taken.
308 i->nrt_init_persistent();
965 154 }
966
967 /////////////////////////////////////////////////////////////////////////////
968 154 std::list<const PdServ::Parameter*> Main::getParameters() const
969 {
970 return std::list<const PdServ::Parameter*>(
971
1/2
✓ Branch 7 taken 154 times.
✗ Branch 8 not taken.
154 parameters.begin(), parameters.end());
972 }
973
974 /////////////////////////////////////////////////////////////////////////////
975 std::list<const PdServ::Event*> Main::getEvents() const
976 {
977 std::list<const PdServ::Event*> ans;
978 for (const auto& e : events)
979 ans.push_back(e.get());
980 return ans;
981 }
982
983 /////////////////////////////////////////////////////////////////////////////
984 308 std::list<const PdServ::Task*> Main::getTasks() const
985 {
986
1/2
✓ Branch 7 taken 308 times.
✗ Branch 8 not taken.
308 return std::list<const PdServ::Task*>(task.begin(), task.end());
987 }
988
989 /////////////////////////////////////////////////////////////////////////////
990 148 void Main::prepare(PdServ::Session* session) const
991 {
992 148 PdServ::Main::prepare(session);
993 148 }
994
995 /////////////////////////////////////////////////////////////////////////////
996 148 void Main::cleanup(const PdServ::Session* session) const
997 {
998 148 PdServ::Main::cleanup(session);
999 148 }
1000
1001 /////////////////////////////////////////////////////////////////////////////
1002 148 void Main::run()
1003 {
1004 148 struct SDO sdo;
1005
1006 while (true) {
1007
3/4
✓ Branch 4 taken 100 times.
✓ Branch 5 taken 148 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 100 times.
348 if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo))
1008 throw std::runtime_error("Main::run(): SDO ::read() failed");
1009
1010
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 57 times.
✗ Branch 4 not taken.
100 switch (sdo.type) {
1011 43 case SDO::ParamChange:
1012
1/2
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
129 sdo.paramChangeAck.rv = setParameterValue(
1013 sdo.paramChangeReq.parameter,
1014 43 sdo.paramChangeReq.offset,
1015 43 sdo.paramChangeReq.count,
1016 &sdo.paramChangeAck.time);
1017
1018 log_debug("Parameter change rv=%i\n", sdo.paramChangeAck.rv);
1019
1020 43 break;
1021
1022 57 case SDO::PollSignal:
1023
1/2
✓ Branch 16 taken 57 times.
✗ Branch 17 not taken.
114 sdo.signal->task->pollSignalValue(
1024 57 sdo.signal, signalData, &sdo.time);
1025 57 break;
1026 };
1027
1028
2/4
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 100 times.
100 if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo))
1029 throw std::runtime_error("Main::run(): SDO ::write() failed");
1030 }
1031 }
1032
1033 /////////////////////////////////////////////////////////////////////////////
1034 43 int Main::setParameterValue(const Parameter* param,
1035 size_t offset, size_t len, struct timespec* mtime) const
1036 {
1037 struct LockGuard {
1038 43 LockGuard(const Main* m): main(m) {
1039
1/2
✓ Branch 6 taken 43 times.
✗ Branch 7 not taken.
43 if (main->writelock_cb)
1040 43 main->writelock_cb(1, main->writelock_data);
1041 43 }
1042 86 ~LockGuard() {
1043
1/2
✓ Branch 6 taken 43 times.
✗ Branch 7 not taken.
43 if (main->writelock_cb)
1044 43 main->writelock_cb(0, main->writelock_data);
1045 43 }
1046 const Main* const main;
1047 };
1048
1/2
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
86 LockGuard guard(this);
1049
1050
1/2
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
86 return param->setValue(offset, len, mtime);
1051 }
1052
1053
1054 /////////////////////////////////////////////////////////////////////////////
1055 148 void Main::final()
1056 {
1057 148 ::close(ipcRx);
1058 148 ::close(ipcTx);
1059 157 }
1060