Skip to content

Commit 9711be7

Browse files
option to protect shared memory with a named semaphore
1 parent a05ec3e commit 9711be7

File tree

6 files changed

+55
-2
lines changed

6 files changed

+55
-2
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "libs/cxxshm"]
88
path = libs/cxxshm
99
url = https://github.com/NikolasK-source/cxxshm.git
10+
[submodule "libs/cxxsemaphore"]
11+
path = libs/cxxsemaphore
12+
url = https://github.com/NikolasK-source/cxxsemaphore.git

libs/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ target_link_libraries(${Target} PRIVATE cxxopts)
1313

1414
add_subdirectory(cxxshm EXCLUDE_FROM_ALL)
1515
target_link_libraries(${Target} PRIVATE cxxshm)
16+
17+
add_subdirectory(cxxsemaphore EXCLUDE_FROM_ALL)
18+
target_link_libraries(${Target} PRIVATE cxxsemaphore)

libs/cxxsemaphore

Submodule cxxsemaphore added at 9e7ea69

src/Modbus_TCP_Client_poll.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ namespace TCP {
2121

2222
static constexpr int MAX_REGS = 0x10000;
2323

24+
//* maximum time to wait for semaphore (100ms)
25+
static constexpr struct timespec SEMAPHORE_MAX_TIME = {0, 100'000};
26+
2427
Client_Poll::Client_Poll(const std::string &host,
2528
const std::string &service,
2629
modbus_mapping_t *mapping,
@@ -174,6 +177,12 @@ void Client_Poll::set_tcp_timeout(std::size_t tcp_timeout) {
174177
}
175178
#endif
176179

180+
void Client_Poll::enable_semaphore(const std::string &name, bool force) {
181+
if (semaphore) throw std::logic_error("semaphore already enabled");
182+
183+
semaphore = std::make_unique<cxxsemaphore::Semaphore>(name, 1, force);
184+
}
185+
177186
void Client_Poll::set_debug(bool debug) {
178187
if (modbus_set_debug(modbus, debug)) {
179188
const std::string error_msg = modbus_strerror(errno);
@@ -369,7 +378,14 @@ Client_Poll::run_t Client_Poll::run(int signal_fd, bool reconnect, int timeout)
369378
auto mapping = mappings[CLIENT_ID];
370379

371380
// handle request
381+
if (semaphore) {
382+
if (!semaphore->wait(SEMAPHORE_MAX_TIME))
383+
std::cerr << Print_Time::iso << " WARNING: Failed to acquire semaphore '"
384+
<< semaphore->get_name() << "' within 100ms." << std::endl;
385+
}
372386
int ret = modbus_reply(modbus, query, rc, mapping);
387+
if (semaphore && semaphore->is_acquired()) semaphore->post();
388+
373389
if (ret == -1) {
374390
std::cerr << Print_Time::iso << " ERROR: modbus_reply failed: " << modbus_strerror(errno)
375391
<< std::endl;

src/Modbus_TCP_Client_poll.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#pragma once
66

77
#include <cstddef>
8+
#include <cxxsemaphore.hpp>
9+
#include <memory>
810
#include <modbus/modbus.h>
911
#include <string>
1012
#include <sys/poll.h>
@@ -39,6 +41,8 @@ class Client_Poll {
3941
int server_socket = -1; //!< socket of the modbus connection
4042
std::unordered_map<int, std::string> client_addrs;
4143

44+
std::unique_ptr<cxxsemaphore::Semaphore> semaphore;
45+
4246
public:
4347
/*! \brief create modbus client (TCP server)
4448
*
@@ -68,11 +72,19 @@ class Client_Poll {
6872
std::size_t tcp_timeout = 5,
6973
std::size_t max_clients = 1);
7074

71-
/*! \brief destroy the modbus client
72-
*
75+
/**
76+
* @brief destroy the modbus client
7377
*/
7478
~Client_Poll();
7579

80+
/**
81+
* @brief use the semaphore mechanism
82+
*
83+
* @param name name of the shared
84+
* @param force use the semaphore even if it already exists
85+
*/
86+
void enable_semaphore(const std::string &name, bool force = false);
87+
7688
/*! \brief enable/disable debugging output
7789
*
7890
* @param debug true: enable debug output

src/main.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ int main(int argc, char **argv) {
171171
("separate-all",
172172
"like --separate, but for all client ids (creates 1028 shared memory files! "
173173
"check/set 'ulimit -n' before using this option.)")
174+
("semaphore",
175+
"protect the shared memory with a named semaphore against simultaneous access",
176+
cxxopts::value<std::string>())
177+
("semaphore-force",
178+
"Force the use of the semaphore even if it already exists. "
179+
"Do not use this option per default! "
180+
"It should only be used if the semaphore of an improperly terminated instance continues "
181+
"to exist as an orphan and is no longer used.")
174182
("h,help",
175183
"print usage")
176184
("version",
@@ -383,6 +391,16 @@ int main(int argc, char **argv) {
383391
return EX_SOFTWARE;
384392
}
385393

394+
// add semaphore if required
395+
try {
396+
if (args.count("semaphore")) {
397+
client->enable_semaphore(args["semaphore"].as<std::string>(), args.count("semaphore-force"));
398+
}
399+
} catch (const std::system_error &e) {
400+
std::cerr << Print_Time::iso << " ERROR: " << e.what() << std::endl;
401+
return EX_SOFTWARE;
402+
}
403+
386404
auto RECONNECT = args.count("reconnect") != 0;
387405

388406
std::cerr << Print_Time::iso << " INFO: Listening on " << client->get_listen_addr() << " for connections."

0 commit comments

Comments
 (0)