7
7
#include " license.hpp"
8
8
#include " readline.hpp"
9
9
10
+ #include " cxxsemaphore.hpp"
10
11
#include " cxxshm.hpp"
11
12
#include < array>
12
13
#include < chrono>
14
+ #include < cmath>
13
15
#include < csignal>
14
16
#include < cxxendian/endian.hpp>
15
17
#include < cxxopts.hpp>
@@ -30,6 +32,15 @@ static constexpr double MIN_BASH_SLEEP = 0.1;
30
32
// ! number of digits that have to be printed for bash sleep instructions
31
33
constexpr int SLEEP_DIGITS = 1 ;
32
34
35
+ // * value to increment error counter if semaphore could not be acquired
36
+ static constexpr long SEMAPHORE_ERROR_INC = 10 ;
37
+
38
+ // * value to decrement error counter if semaphore could be acquired
39
+ static constexpr long SEMAPHORE_ERROR_DEC = 1 ;
40
+
41
+ // * maximum value of semaphore error counter
42
+ static constexpr long SEMAPHORE_ERROR_MAX = 100 ;
43
+
33
44
constexpr std::array<int , 10 > TERM_SIGNALS = {SIGINT,
34
45
SIGTERM,
35
46
SIGHUP,
@@ -90,6 +101,12 @@ int main(int argc, char **argv) {
90
101
options.add_options ()(" license" , " show licenses" );
91
102
options.add_options ()(" data-types" , " show list of supported data type identifiers" );
92
103
options.add_options ()(" constants" , " list string constants that can be used as value" );
104
+ options.add_options ()(" semaphore" ,
105
+ " protect the shared memory with an existing named semaphore against simultaneous access" ,
106
+ cxxopts::value<std::string>());
107
+ options.add_options ()(" semaphore-timeout" ,
108
+ " maximum time (in seconds) to wait for semaphore (default: 0.1)" ,
109
+ cxxopts::value<double >()->default_value (" 0.1" ));
93
110
94
111
// parse arguments
95
112
cxxopts::ParseResult args;
@@ -133,7 +150,7 @@ int main(int argc, char **argv) {
133
150
options.set_width (120 );
134
151
std::cout << options.help () << std::endl;
135
152
std::cout << std::endl;
136
- print_format ();
153
+ print_format (true );
137
154
std::cout << std::endl;
138
155
std::cout << " This application uses the following libraries:" << std::endl;
139
156
std::cout << " - cxxopts by jarro2783 (https://github.com/jarro2783/cxxopts)" << std::endl;
@@ -371,6 +388,29 @@ int main(int argc, char **argv) {
371
388
372
389
std::mutex m; // to ensure that the program is not terminated while it writes to a shared memory
373
390
391
+ std::unique_ptr<cxxsemaphore::Semaphore> semaphore;
392
+ long semaphore_error_counter = 0 ;
393
+ if (args.count (" semaphore" )) {
394
+ try {
395
+ semaphore = std::make_unique<cxxsemaphore::Semaphore>(args[" semaphore" ].as <std::string>());
396
+ } catch (const std::exception &e) {
397
+ std::cerr << e.what () << std::endl;
398
+ return EX_SOFTWARE;
399
+ }
400
+ }
401
+
402
+ const double SEMAPHORE_TIMEOUT_S = args[" semaphore-timeout" ].as <double >();
403
+ if (SEMAPHORE_TIMEOUT_S < 0.000'001 ) {
404
+ std::cerr << " semaphore-timeout: invalid value" << std::endl;
405
+ return EX_USAGE;
406
+ }
407
+
408
+ double modf_dummy {};
409
+ const timespec SEMAPHORE_MAX_TIME = {
410
+ static_cast <time_t >(args[" semaphore-timeout" ].as <double >()),
411
+ static_cast <suseconds_t >(std::modf (SEMAPHORE_TIMEOUT_S, &modf_dummy) * 1'000'000 ),
412
+ };
413
+
374
414
std::cout << std::fixed;
375
415
376
416
auto last_time = std::chrono::steady_clock::now ();
@@ -441,6 +481,24 @@ int main(int argc, char **argv) {
441
481
442
482
// write value to target
443
483
std::lock_guard<std::mutex> guard (m);
484
+
485
+ if (semaphore) {
486
+ while (!semaphore->wait (SEMAPHORE_MAX_TIME)) {
487
+ std::cerr << " WARNING: Failed to acquire semaphore '" << semaphore->get_name () << " ' within "
488
+ << SEMAPHORE_TIMEOUT_S << " s." << std::endl;
489
+
490
+ semaphore_error_counter += SEMAPHORE_ERROR_INC;
491
+
492
+ if (semaphore_error_counter >= SEMAPHORE_ERROR_MAX) {
493
+ std::cerr << " ERROR: Repeatedly failed to acquire the semaphore" << std::endl;
494
+ return EX_SOFTWARE;
495
+ }
496
+ }
497
+
498
+ semaphore_error_counter -= SEMAPHORE_ERROR_DEC;
499
+ if (semaphore_error_counter < 0 ) semaphore_error_counter = 0 ;
500
+ }
501
+
444
502
for (auto &input_data : instructions) {
445
503
switch (input_data.register_type ) {
446
504
case InputParser::Instruction::register_type_t ::DO: {
@@ -542,6 +600,8 @@ int main(int argc, char **argv) {
542
600
break ;
543
601
}
544
602
}
603
+
604
+ if (semaphore && semaphore->is_acquired ()) semaphore->post ();
545
605
}
546
606
547
607
rl_clear_history ();
0 commit comments