-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to gracefully shutdown server thread. #173
Comments
It's better to use mailing list for question, isn't it? So no, you can't gracefully interrupt modbus_receive but this change #95 could improve the situation. Could you pursue talk on mailing list, please? |
Thanks for the response Stephane. I believe you are better off capturing this in a GitHub issue as it does revolve around a fairly substantial deficiency in using libmodbus to create a slave server. I'd very much like to avoid using timeouts to kluge a solution. My apologies that the original issue request came across like a forum post. My intent was to formally request that libmodbus gracefully stop a blocked server call to modbus_receive(). I would greatly appreciate seeing this issue reopened and addressed -- libmodbus would be even better and more robust as result. |
The libmodbus goal is to implement Modbus protocol not to provide a slave or server out the box. |
Hi, http://vgrin.host56.com/software/libmodbus-master-withshutdown.zip Additionally it contains test projects: tests\break-test-client.c and ests\break-test-server.c and projects files in tests\Win32\ . The break-test-server requires the code for UNIX – I have only Windows implementation, sorry :-). The server terminates itself from its own thread which waits some time. |
I tried your implementation of this signal-handlers in mbtools. It is calling Without change libmodbus, what I will do is to call select on my own before calling |
@pboettch can you please show the code you used in order to bypass this issue? |
I haz teh codez! ;-) My tcp-server is a single thread server handling all clients sequentially if there are multiple requests at the same time. It's using At global (or better object-scope) you will need a pipe for self-piping int selfPipe_[2]; This is the "server"-code modbus_t *ctx = modbus_new_tcp("0.0.0.0", port_);
int server_socket = modbus_tcp_listen(ctx, 2);
if (server_socket < 0) {
// error handling
modbus_free(ctx);
return;
}
const modbus_reply_callbacks_t callbacks{
nullptr,
&ModbusTCPServer::verify,
&ModbusTCPServer::read,
&ModbusTCPServer::write};
modbus_set_reply_callbacks(ctx, &callbacks, this); // this is the code not yet upstream
std::vector<struct pollfd> sockets; // using a vector as behind it is a C-array and can be passed to poll()
sockets.push_back({selfPipe_[0], POLLIN, 0}); // add self-pipe to poll-list
sockets.push_back({server_socket, POLLIN, 0}); // add the server-socket
bool running = true;
while (running) {
int ret = poll(sockets.data(), sockets.size(), -1); // poll on all sockets, infinitely
if (ret < 0) {
// error handling
break;
}
// poll woke up, something happened on one of the sockets
struct pollfd new_socket { // for later use if new client connection
- 1
};
for (auto p = sockets.begin(); p != sockets.end();) { // which socket has triggered the wakeup?
if (p->fd == selfPipe_[0]) { // was it the self-pipe, we will stop here
if (p->revents != 0) {
running = false;
break;
}
} else if (p->fd == server_socket) { // server-socket - new client
if (p->revents & POLLIN) {
std::cerr << "new connection\n";
int fd = modbus_tcp_accept(ctx, &server_socket);
if (fd >= 0)
new_socket = {fd, POLLIN, 0}; // prepare poll-structure for new client
else {
// error handling
}
} else if (p->revents != 0) { // server socket closed, end
running = false;
break;
}
} else { // handle one client request
if (p->revents & POLLIN) {
modbus_set_socket(ctx, p->fd); // set the socket to libmodbus answers to the right client
uint8_t query[MODBUS_RTU_MAX_ADU_LENGTH];
int rc = modbus_receive(ctx, query);
if (rc <= 0) { // connection has been closed actually by the client
::close(p->fd);
p = sockets.erase(p); // remove it from the poll-array
continue;
}
/* rc is the query size */
rc = modbus_reply_callback(ctx, query, rc); // handle the reply (via the callbacks)
if (rc < 0) {
// error handling - invalid request
::close(p->fd); // close client
p = sockets.erase(p);
continue;
}
} else if (p->revents != 0) {
// handle unexpected revents for user-connection
::close(p->fd);
p = sockets.erase(p);
continue;
}
}
p++;
}
if (new_socket.fd != -1) // we have seen a new connection coming, doing it outside the loop because cannot add an element to vector while looping
sockets.push_back(new_socket);
}
// stopping server -> closing all connections
for (auto &p : sockets)
::close(p.fd);
modbus_close(ctx);
modbus_free(ctx); You need to run a thread for the code above, before you need to create the self-pipe if (pipe(selfPipe_) != 0) // oh oh!
// error handling and when you're done with your work to stop the thread you do close(selfPipe_[1]); |
I need the ability to gracefully close/exit/abort a Modbus server thread that is blocked in modbus_receive(). At present, I am unable to get the blocking call to modbus_receive() to return. Invoking modbus_close() on the context from another thread does not abort the blocking call. On Linux, it appears to have no effect. On Windows, the modbus_close() call blocks indefinitely as well.
There does not seem to be any test coverage or examples of this, so my assumption is that this is not currently achievable in libmodbus. Please let me know if I am wrong, and I'd appreciate some guidance.
Note: Terminating the server thread is not a viable solution. I need to be able to gracefully get the call to modbus_receive to return.
The text was updated successfully, but these errors were encountered: