Skip to content

Commit 60eb742

Browse files
use signalfd for signal handling
1 parent 7611f70 commit 60eb742

File tree

3 files changed

+68
-24
lines changed

3 files changed

+68
-24
lines changed

src/Modbus_TCP_Client_poll.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Client_Poll::Client_Poll(const std::string &host,
2525
modbus_mapping_t *mapping,
2626
std::size_t tcp_timeout,
2727
std::size_t max_clients)
28-
: max_clients(max_clients), poll_fds(max_clients + 1, {0, 0, 0}) {
28+
: max_clients(max_clients), poll_fds(max_clients + 2, {0, 0, 0}) {
2929
const char *host_str = "::";
3030
if (!(host.empty() || host == "any")) host_str = host.c_str();
3131

@@ -72,7 +72,7 @@ Client_Poll::Client_Poll(const std::string &host,
7272
modbus_mapping_t **mappings,
7373
std::size_t tcp_timeout,
7474
std::size_t max_clients)
75-
: max_clients(max_clients), poll_fds(max_clients + 1, {0, 0, 0}) {
75+
: max_clients(max_clients), poll_fds(max_clients + 2, {0, 0, 0}) {
7676
const char *host_str = "::";
7777
if (!(host.empty() || host == "any")) host_str = host.c_str();
7878

@@ -242,9 +242,16 @@ double Client_Poll::get_response_timeout() {
242242
return static_cast<double>(timeout.sec) + (static_cast<double>(timeout.usec) / (1000.0 * 1000.0));
243243
}
244244

245-
bool Client_Poll::run(bool reconnect, int timeout) {
245+
Client_Poll::run_t Client_Poll::run(int signal_fd, bool reconnect, int timeout) {
246246
std::size_t i = 0;
247247

248+
// poll signal fd
249+
{
250+
auto &fd = poll_fds[i++];
251+
fd.fd = signal_fd;
252+
fd.events = POLLIN;
253+
}
254+
248255
// do not poll server socket if maximum number of connections is reached
249256
const auto active_clients = client_addrs.size();
250257
const bool poll_server = active_clients < max_clients;
@@ -262,18 +269,31 @@ bool Client_Poll::run(bool reconnect, int timeout) {
262269
}
263270

264271
// number of files to poll
265-
const nfds_t poll_size = active_clients + (poll_server ? 1 : 0);
272+
const nfds_t poll_size = active_clients + (poll_server ? 2 : 1);
266273

267274
int tmp = poll(poll_fds.data(), poll_size, timeout);
268275
if (tmp == -1) {
269-
if (errno == EINTR) return true;
276+
if (errno == EINTR) return run_t::interrupted;
270277
throw std::system_error(errno, std::generic_category(), "Failed to poll socket(s)");
271278
} else if (tmp == 0) {
272279
// poll timed out
273-
return true;
280+
return run_t::timeout;
274281
}
275282

276283
i = 0;
284+
{
285+
auto &fd = poll_fds[i++];
286+
if (fd.revents) {
287+
if (fd.revents & POLLNVAL) throw std::logic_error("poll (server socket) returned POLLNVAL");
288+
if (fd.revents & POLLERR) throw std::logic_error("poll (signal fd) returned POLLERR");
289+
if (fd.revents & POLLHUP) throw std::logic_error("poll (signal fd) returned POLLHUP");
290+
if (fd.revents & POLLIN) return run_t::term_signal;
291+
std::ostringstream sstr;
292+
sstr << "poll (signal fd) returned unknown revent: " << fd.revents;
293+
throw std::logic_error(sstr.str());
294+
}
295+
}
296+
277297
if (poll_server) {
278298
auto &fd = poll_fds[i++];
279299

@@ -377,10 +397,10 @@ bool Client_Poll::run(bool reconnect, int timeout) {
377397

378398
// check if there are any connections
379399
if (!reconnect) {
380-
if (client_addrs.empty()) return false;
400+
if (client_addrs.empty()) return run_t::term_nocon;
381401
}
382402

383-
return true;
403+
return run_t::ok;
384404
}
385405

386406
std::string Client_Poll::get_listen_addr() {

src/Modbus_TCP_Client_poll.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ class Client_Poll {
2020
public:
2121
static constexpr std::size_t MAX_CLIENT_IDS = 256;
2222

23+
enum class run_t {
24+
term_signal,
25+
term_nocon,
26+
ok,
27+
timeout,
28+
interrupted,
29+
};
30+
2331
private:
2432
const std::size_t max_clients;
2533
std::vector<struct pollfd> poll_fds;
@@ -118,10 +126,11 @@ class Client_Poll {
118126
* @param reconnect false: terminate once the last active connection disconnects
119127
* true: continue listening for new connections if there is no client (Mosbus Server) left
120128
* @param timeout timeout valoue for call of poll (see: man 2 poll)
129+
* @param signal_fd signal file descriptor for termination signals
121130
* @return true continue
122131
* @return false terminate
123132
*/
124-
bool run(bool reconnect = true, int timeout = -1);
133+
run_t run(int signal_fd, bool reconnect = true, int timeout = -1);
125134

126135
private:
127136
#ifdef OS_LINUX

src/main.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <filesystem>
1010
#include <iostream>
1111
#include <memory>
12+
#include <sys/signalfd.h>
1213
#include <sysexits.h>
1314
#include <thread>
1415
#include <unistd.h>
@@ -48,7 +49,6 @@ static int socket = -1;
4849
/*! \brief signal handler (SIGINT and SIGTERM)
4950
*
5051
*/
51-
static void sig_term_handler(int) { terminate = true; }
5252

5353
constexpr std::array<int, 10> TERM_SIGNALS = {SIGINT,
5454
SIGTERM,
@@ -83,16 +83,22 @@ int main(int argc, char **argv) {
8383
# pragma clang diagnostic push
8484
# pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
8585
#endif
86-
// establish signal handler
87-
struct sigaction term_sa;
88-
term_sa.sa_handler = sig_term_handler;
89-
term_sa.sa_flags = SA_RESTART;
90-
sigemptyset(&term_sa.sa_mask);
86+
// create signal fd
87+
sigset_t sigmask;
88+
sigemptyset(&sigmask);
9189
for (const auto SIGNO : TERM_SIGNALS) {
92-
if (sigaction(SIGNO, &term_sa, nullptr)) {
93-
perror("Failed to establish signal handler");
94-
return EX_OSERR;
95-
}
90+
sigaddset(&sigmask, SIGNO);
91+
}
92+
93+
if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {
94+
perror("sigprocmask");
95+
return EX_OSERR;
96+
}
97+
98+
int signal_fd = signalfd(-1, &sigmask, 0);
99+
if (signal_fd == -1) {
100+
perror("signal_fd");
101+
return EX_OSERR;
96102
}
97103
#ifdef COMPILER_CLANG
98104
# pragma clang diagnostic pop
@@ -352,12 +358,21 @@ int main(int argc, char **argv) {
352358
std::cerr << "Listening on " << client->get_listen_addr() << " for connections." << std::endl;
353359

354360
try {
355-
while (true) {
356-
if (!client->run(RECONNECT, -1)) {
357-
std::cerr << "No more active connections." << std::endl;
358-
break;
361+
[&]() {
362+
while (true) {
363+
auto ret = client->run(signal_fd, RECONNECT, -1);
364+
365+
switch (ret) {
366+
case Modbus::TCP::Client_Poll::run_t::ok: continue;
367+
case Modbus::TCP::Client_Poll::run_t::term_signal: return;
368+
case Modbus::TCP::Client_Poll::run_t::term_nocon:
369+
std::cerr << "No more active connections." << std::endl;
370+
return;
371+
case Modbus::TCP::Client_Poll::run_t::timeout:
372+
case Modbus::TCP::Client_Poll::run_t::interrupted: continue;
373+
}
359374
}
360-
}
375+
}();
361376
} catch (const std::exception &e) {
362377
if (!terminate) std::cerr << e.what() << std::endl;
363378
}

0 commit comments

Comments
 (0)