How to use boost::asio with Linux GPIOs - c++

I have a single-threaded Linux application using boost::asio for asynchronous input/output. Now I need to extend this application to read in GPIO inputs on /sys/class/gpio/gpioXX/value.
It is possible to do that with boost::asio::posix::stream_descriptor on edge-triggered GPIO inputs?
I configured the GPIO input like follows:
echo XX >/sys/class/gpio/export
echo in >/sys/class/gpio/gpioXX/direction
echo both >/sys/class/gpio/gpioXX/edge
I managed to write a epoll based test application that blocks on the GPIO file descriptor until the GPIO signal changes but boost::asio does not seem to be able to block properly. A call to boost::asio::async_read always immediately invokes the handler (of course only within io_service.run()) with either EOF or - in case the file pointer was set back - 2 bytes data.
I'm not an expert in boost::asio internals but could the reason be that the boost::asio epoll reactor is level triggered instead of edge triggered in case of posix::stream_descriptor?
Here is my code:
#include <fcntl.h>
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <boost/asio.hpp>
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor sd(io_service);
boost::asio::streambuf streambuf;
void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error.value() == boost::asio::error::eof) {
// If we don't reset the file pointer we only get EOFs
lseek(sd.native_handle(), 0, SEEK_SET);
} else if (error)
throw std::runtime_error(std::string("Error ") + std::to_string(error.value()) + " occurred (" + error.message() + ")");
std::copy_n(std::istreambuf_iterator<char>(&streambuf), bytes_transferred, std::ostreambuf_iterator<char>(std::cout));
streambuf.consume(bytes_transferred);
boost::asio::async_read(sd, streambuf, &read_handler);
}
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
int fd = open(argv[1], O_RDONLY);
if (fd < 1)
return 1;
try {
sd.assign(fd);
boost::asio::async_read(sd, streambuf, &read_handler);
io_service.run();
} catch (...) {
close(fd);
return 1;
}
close(fd);
return 0;
}

As far as I know, it is not possible to get this particular behavior with Boost.Asio. While the kernel flags some files on the procfs and sysfs as pollable, they do not provide the stream-like behavior that is expected from boost::asio::posix::stream_descriptor and its operations.
Boost.Asio's epoll reactor is edge-triggered (see Boost.Asio 1.43 revision history notes). Under certain conditions1, Boost.Asio will attempt the I/O operation within the context of the initiating function (e.g. async_read()). If the I/O operation completes (success or failure), then the completion handler is posted into the io_service as-if by io_service.post(). Otherwise, the file descriptor will be added to the event demultiplexer for monitoring. The documentation alludes to this behavior:
Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using boost::asio::io_service::post().
For composed operations, such as async_read(), EOF is treated as an error, as it indicates a violation in the operation's contract (i.e. completion condition will never be satisfied because no more data will be available). In this particular case, the I/O system call will occur within the async_read() initiating function, reading from the start of the file (offset 0) to the end of file, causing the operation to fail with boost::asio::error::eof. As the operation has completed, it is never added to the event demultiplexer for edge-triggered monitoring:
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor stream_descriptor(io_service);
void read_handler(const boost::system::error_code& error, ...)
{
if (error.value() == boost::asio::error::eof)
{
// Reset to start of file.
lseek(sd.native_handle(), 0, SEEK_SET);
}
// Same as below. ::readv() will occur within this context, reading
// from the start of file to end-of-file, causing the operation to
// complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);
}
int main()
{
int fd = open( /* sysfs file */, O_RDONLY);
// This would throw an exception for normal files, as they are not
// poll-able. However, the kernel flags some files on procfs and
// sysfs as pollable.
stream_descriptor.assign(fd);
// The underlying ::readv() system call will occur within the
// following function (not deferred until edge-triggered notification
// by the reactor). The operation will read from start of file to
// end-of-file, causing the operation to complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);
// Run will invoke the ready-to-run completion handler from the above
// operation.
io_service.run();
}
1. Internally, Boost.Asio refers to this behavior as speculative operations. It is an implementation detail, but the I/O operation will be attempted within the initiating function if the operation may not need event notification (e.g. it can immediately attempt to a non-blocking I/O call), and and there are neither pending operations of the same type nor pending out-of-band operations on the I/O object. There are no customization hooks to prevent this behavior.

Related

OS Signal handling loop - blocking or non-blocking read?

My application has a thread for handling OS signals, so to not block the programLoop(). This thread, processOSSignals, basically keeps on reading the file descriptor for signals SIGINT, SIGTERM, SIGQUIT. On their reception, loopOver being initially true, is set to false.
int mSigDesc = -1;
void init()
{
// creates file descriptor for reading SIGINT, SIGTERM, SIGQUIT
// blocks signals with sigprocmask(SIG_BLOCK, &mask, nullptr)
...
mSigDesc = signalfd(mSigDesc, &mask, SFD_NONBLOCK); // OR 3rd param = 0?
}
void processOSSignals()
{
while (loopOver)
{
struct signalfd_siginfo fdsi;
auto readedBytes = read(mSigDesc, &fdsi, sizeof(fdsi));
...
}
}
int main()
{
init();
std::thread ossThread(processOSSignals);
programLoop();
ossThread.join();
}
My question is - should mSigDesc be set to blocking or non-blocking (asynchronous) mode?
In non-blocking mode, this thread is always busy, but inefficiently reading and returning EAGAIN over and over again.
In blocking mode, it waits until one of the signals is received, but if it is never sent, the ossThread will never join.
How should it be handled? Use sleep() in the non-blocking mode, to attempt reading only occasionally? Or maybe use select() in the blocking mode, to monitor mSigDesc and read only when sth. is available there?
Whether you use blocking or non-blocking I/O depends on how you want to handle your I/O.
Typically, if you have a single thread which is dedicated to reading from the signal file descriptor and you simply want it to wait until it gets a signal, then you should use blocking I/O.
However, in many contexts, spawning a single thread for each I/O operation is inefficient. A thread requires a stack, which may consume a couple megabytes, and it's often more efficient to process many file descriptors (which may be of many different types) by putting them all in non-blocking mode and waiting until one of them is ready.
Typically, this is done portably using poll(2). select(2) is possible, but on many systems, it is limited to a certain number of file descriptors (on Linux, 1024), and many programs will exceed that number. On Linux, the epoll(7) family of functions can also be used, and you may prefer that if you're already using such non-portable constructions as signalfd(2).
For example, you might want to handle signal FDs as part of your main loop, in which case including that FD as one the FDs that your main loop processes using poll(2) or one of the other functions might be more desirable.
What you should avoid doing is spinning in a loop or sleeping with a non-blocking socket. If you use poll(2), you can specify a timeout after which the operation returns 0 if no file descriptor was ready, so you can already control a timeout without needing to resort to sleep.
Same advise as bk2204 outlined: Just use poll. If you want to have a separate thread, a simple way to signal that thread is to add the read side of a pipe (or socket) to the set of polled file descriptors. The main thread then closes the write side when it wants the thread to stop. poll will then return and signal that reading from the pipe is possible (since it will signal EOF).
Here is the outline of an implementation:
We start by defining an RAII class for file descriptors.
#include <unistd.h>
// using pipe, close
#include <utility>
// using std::swap, std::exchange
struct FileHandle
{
int fd;
constexpr FileHandle(int fd=-1) noexcept
: fd(fd)
{}
FileHandle(FileHandle&& o) noexcept
: fd(std::exchange(o.fd, -1))
{}
~FileHandle()
{
if(fd >= 0)
::close(fd);
}
void swap(FileHandle& o) noexcept
{
using std::swap;
swap(fd, o.fd);
}
FileHandle& operator=(FileHandle&& o) noexcept
{
FileHandle tmp = std::move(o);
swap(tmp);
return *this;
}
operator bool() const noexcept
{ return fd >= 0; }
void reset(int fd=-1) noexcept
{ *this = FileHandle(fd); }
void close() noexcept
{ reset(); }
};
Then we use that to construct our pipe or socket pair.
#include <cerrno>
#include <system_error>
struct Pipe
{
FileHandle receive, send;
Pipe()
{
int fds[2];
if(pipe(fds))
throw std::system_error(errno, std::generic_category(), "pipe");
receive.reset(fds[0]);
send.reset(fds[1]);
}
};
The thread then uses poll on the receive end and its signalfd.
#include <poll.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <cassert>
void processOSSignals(const FileHandle& stop)
{
sigset_t mask;
sigemptyset(&mask);
FileHandle sighandle{ signalfd(-1, &mask, 0) };
if(! sighandle)
throw std::system_error(errno, std::generic_category(), "signalfd");
struct pollfd fds[2];
fds[0].fd = sighandle.fd;
fds[1].fd = stop.fd;
fds[0].events = fds[1].events = POLLIN;
while(true) {
if(poll(fds, 2, -1) < 0)
throw std::system_error(errno, std::generic_category(), "poll");
if(fds[1].revents & POLLIN) // stop signalled
break;
struct signalfd_siginfo fdsi;
// will not block
assert(fds[0].revents != 0);
auto readedBytes = read(sighandle.fd, &fdsi, sizeof(fdsi));
}
}
All that remains to be done is create our various RAII classes in such an order that the write side of the pipe is closed before the thread is joined.
#include <thread>
int main()
{
std::thread ossThread;
Pipe stop; // declare after thread so it is destroyed first
ossThread = std::thread(processOSSignals, std::move(stop.receive));
programLoop();
stop.send.close(); // also handled by destructor
ossThread.join();
}
Other things to note:
Consider switching to std::jthread so that it joins automatically even if the program loop throws an exception
Depending on what your background thread does, you can also simply abandon it on program end by calling std::thread::detach
If the thread may stay busy (not calling poll) for long loops, you can pair the pipe up with an std::atomic<bool> or jthread's std::stop_token to signal the stop event. That way the thread can check the flag in between loop iterations. Incidentally, your use of a plain global int was invalid as you read and write from different threads at the same time
You could also use the signalfd and send a specific signal to the thread for it to quit

Boost Asio: checking socket ability to be readable/writable

In my application I have to mix of both asio created sockets and native ones(coming from C posgresql library).
What I need is the ability to get notification out of boost's io_service class instance on the particular socket to be in non-blocking readable/writable state, but without performing an actual read/write(will be done by 3-rd party library), i.e. effectively doing only select()/poll()
Can it be achieved with passing 0 as buffer length to the function like async_read_some()?
I've made a quick test and indeed a call to async_read_some() with zero buffer length does call read event handler but I am not sure it is done after blocking in select()/poll() over the corresponding socket handle, waiting for the real "can read" state.
This is often referred to as reactor-style operations.
These can be obtained by providing boost::asio::null_buffers to the asynchronous operations. Reactor-style operations provide a way to be informed when a read or write operation can be performed, and are useful for integrating with third party libraries, using shared memory pools, etc. The Boost.Asio documentation provides some information and the following example code:
ip::tcp::socket socket(my_io_service);
...
socket.non_blocking(true);
...
socket.async_read_some(null_buffers(), read_handler);
...
void read_handler(boost::system::error_code ec)
{
if (!ec)
{
std::vector<char> buf(socket.available());
socket.read_some(buffer(buf));
}
}
Boost.Asio also provides an official nonblocking example, illustrating how to integrate with libraries that want to perform the read and write operations directly on a socket.
Providing a zero-length buffer to operations will often result in a no-op, as the operation's completion condition will have been met without attempting to perform any I/O. Here is a complete example demonstrating the difference between the two:
#include <array>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}
void print_status(
const boost::system::error_code& error,
std::size_t bytes_transferred,
boost::asio::ip::tcp::socket& socket)
{
std::cout << "error: " << error.message() << "; "
"transferred: " << bytes_transferred << "; "
"available: " << socket.available() << std::endl;
}
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket1(io_service);
tcp::socket socket2(io_service);
// Connect the sockets.
acceptor.async_accept(socket1, boost::bind(&noop));
socket2.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
io_service.run();
io_service.reset();
std::array<char, 512> buffer;
// Reading into a zero-length buffer is a no-op and will be
// considered immediately completed.
socket1.async_receive(boost::asio::buffer(buffer, 0),
boost::bind(&print_status,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
boost::ref(socket1))
);
// Guarantee the handler runs.
assert(1 == io_service.poll());
io_service.reset();
// Start a reactor-style read operation by providing a null_buffer.
socket1.async_receive(boost::asio::null_buffers(),
boost::bind(&print_status,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
boost::ref(socket1))
);
// Guarantee that the handler did not run.
assert(0 == io_service.poll());
// Write to the socket so that data becomes available.
boost::asio::write(socket2, boost::asio::buffer("hello"));
assert(1 == io_service.poll());
}
Output:
error: Success; transferred: 0; available: 0
error: Success; transferred: 0; available: 6

How to safely cancel a Boost ASIO asynchronous accept operation?

Everything I've read in the Boost ASIO docs and here on StackOverflow suggests I can stop an async_accept operation by calling close on the acceptor socket. However, I get an intermittent not_socket error in the async_accept handler when I try to do this. Am I doing something wrong or does Boost ASIO not support this?
(Related questions: here and here.)
(Note: I'm running on Windows 7 and using the Visual Studio 2015 compiler.)
The core problem I face is a race condition between the async_accept operation accepting an incoming connection and my call to close. This happens even when using a strand, explicit or implicit.
Note my call to async_accept strictly happens before my call to close. I conclude the race condition is between my call to close and the under-the-hood code in Boost ASIO that accepts the incoming connection.
I've included code demonstrating the problem. The program repeatedly creates an acceptor, connects to it, and immediately closes the acceptor. It expects the async_accept operation to either complete successfully or else be canceled. Any other error causes the program to abort, which is what I'm seeing intermittently.
For synchronization the program uses an explicit strand. Nevertheless, the call to close is unsynchronized with the effect of the async_accept operation, so sometimes the acceptor closes before it accepts the incoming connection, sometimes it closes afterward, sometimes neither—hence the problem.
Here's the code:
#include <algorithm>
#include <boost/asio.hpp>
#include <cstdlib>
#include <future>
#include <iostream>
#include <memory>
#include <thread>
int main()
{
boost::asio::io_service ios;
auto work = std::make_unique<boost::asio::io_service::work>(ios);
const auto ios_runner = [&ios]()
{
boost::system::error_code ec;
ios.run(ec);
if (ec)
{
std::cerr << "io_service runner failed: " << ec.message() << '\n';
abort();
}
};
auto thread = std::thread{ios_runner};
const auto make_acceptor = [&ios]()
{
boost::asio::ip::tcp::resolver resolver{ios};
boost::asio::ip::tcp::resolver::query query{
"localhost",
"",
boost::asio::ip::resolver_query_base::passive |
boost::asio::ip::resolver_query_base::address_configured};
const auto itr = std::find_if(
resolver.resolve(query),
boost::asio::ip::tcp::resolver::iterator{},
[](const boost::asio::ip::tcp::endpoint& ep) { return true; });
assert(itr != boost::asio::ip::tcp::resolver::iterator{});
return boost::asio::ip::tcp::acceptor{ios, *itr};
};
for (auto i = 0; i < 1000; ++i)
{
auto acceptor = make_acceptor();
const auto saddr = acceptor.local_endpoint();
boost::asio::io_service::strand strand{ios};
boost::asio::ip::tcp::socket server_conn{ios};
// Start accepting.
std::promise<void> accept_promise;
strand.post(
[&]()
{
acceptor.async_accept(
server_conn,
strand.wrap(
[&](const boost::system::error_code& ec)
{
accept_promise.set_value();
if (ec.category() == boost::asio::error::get_system_category()
&& ec.value() == boost::asio::error::operation_aborted)
return;
if (ec)
{
std::cerr << "async_accept failed (" << i << "): " << ec.message() << '\n';
abort();
}
}));
});
// Connect to the acceptor.
std::promise<void> connect_promise;
strand.post(
[&]()
{
boost::asio::ip::tcp::socket client_conn{ios};
{
boost::system::error_code ec;
client_conn.connect(saddr, ec);
if (ec)
{
std::cerr << "connect failed: " << ec.message() << '\n';
abort();
}
connect_promise.set_value();
}
});
connect_promise.get_future().get(); // wait for connect to finish
// Close the acceptor.
std::promise<void> stop_promise;
strand.post([&acceptor, &stop_promise]()
{
acceptor.close();
stop_promise.set_value();
});
stop_promise.get_future().get(); // wait for close to finish
accept_promise.get_future().get(); // wait for async_accept to finish
}
work.reset();
thread.join();
}
Here's the output from a sample run:
async_accept failed (5): An operation was attempted on something that is not a socket
The number in parentheses denotes how many successfully iterations the program ran.
UPDATE #1: Based on Tanner Sansbury's answer, I've added a std::promise for signaling the completion of the async_accept handler. This has no effect on the behavior I'm seeing.
UPDATE #2: The not_socket error originates from a call to setsockopt, from call_setsockopt, from socket_ops::setsockopt in the file boost\asio\detail\impl\socket_ops.ipp (Boost version 1.59). Here's the full call:
socket_ops::setsockopt(new_socket, state,
SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
&update_ctx_param, sizeof(SOCKET), ec);
Microsoft's documentation for setsockopt says about SO_UPDATE_ACCEPT_CONTEXT:
Updates the accepting socket with the context of the listening socket.
I'm not sure what exactly this means, but it sounds like something that fails if the listening socket is closed. This suggests that, on Windows, one cannot safely close an acceptor that is currently running a completion handler for an async_accept operation.
I hope someone can tell me I'm wrong and that there is a way to safely close a busy acceptor.
The example program will not cancel the async_accept operation. Once the connection has been established, the async_accept operation will be posted internally for completion. At this point, the operation is no longer cancelable and is will not be affected by acceptor.close().
The issue being observed is the result of undefined behavior. The program fails to meet a lifetime requirement for async_accept's peer parameter:
The socket into which the new connection will be accepted. Ownership of the peer object is retained by the caller, which must guarantee that it is valid until the handler is called.
In particular, the peer socket, server_conn, has automatic scope within the for loop. The loop may begin a new iteration while the async_accept operation is outstanding, causing server_conn to be destroyed and violate the lifetime requirement. Consider extending server_conn's lifetime by either:
set a std::future within the accept handler and wait on the related std::promise before continuing to the next iteration of the loop
managing server_conn via a smart pointer and passing ownership to the accept handler

Boost and Windows sockets - Properly handling TCP client disconnect scenarios

I have a class called ServerConnectionHandler that creates a boost thread for reading data from the server. The boost thread is bound to the ServerConnectionHandler object. Relevant pieces of code are below:
ServerConnectionHandler::~ServerConnectionHandler()
{
close();
}
void ServerConnectionHandler::close()
{
closesocket(m_ConnectSocket);
WSACleanup();
}
void ServerConnectionHandler::MsgLoop()
{
int size_recv = 0;
char chunk[DEFAULT_BUFLEN];
while(1)
{
memset(chunk, 0, DEFAULT_BUFLEN);
size_recv = recv(m_ConnectSocket, chunk, DEFAULT_BUFLEN, 0);
if(size_recv > 0)
{
for( int i=0; i < size_recv; ++i )
{
if(chunk[i] == '\n')
{
m_tcpEventHandler.OnClientMessage(m_RecBuffer);
m_RecBuffer.clear();
}
else
{
m_RecBuffer.append(1, chunk[i]);
}
}
}
else if(size_recv == 0)
{
close();
const std::string error = "MsgReceiver Received 0 bytes because connection was closed. MsgReceiver shutting down.\n";
m_tcpEventHandler.OnClientSocketError(error);
break;
}
else
{
char error [512];
sprintf(error, "Error on Receiving Socket. Recv=[%d], WSAError=[%d]. MsgReceiver shutting down.\n", size_recv, WSAGetLastError());
m_tcpEventHandler.OnClientSocketError(error);
close();
break;
}
}
// NOTE: This will eventually call the destructor of ServerConnectionHandler...
m_tcpEventHandler.OnClientDisconnect("Disconnected. Reason: Remote host snapped connection.");
}
My problem is that when close() is called in the destructor, the receiver thread is still running and crashes when it attempts to call any of the m_tcpEventHandler.OnClient...() methods because the object has been destroyed at this point.
I need to be able to handle this cleanly in 3 different cases:
When the user manually disconnects the client (the destructor will be called in this case).
When the client is disconnected from the server (maybe because the server crashed for example).
When the application shuts down (needs to cleanly disconnect everything - similar to #1).
Right now, this code only works for case #2. I don't want to slow down the receiver thread with any locking as the performance is critical. From what I've read, I've seen people create a volatile bool flag that tells the receiver thread to stop. The problem I see with this approach is that what if it is in the middle of handling a message (m_tcpEventHandler.OnClientMessage()) right when the destructor is called? Then it could immediately hit code for the destroyed object (m_tcpEventHandler could in turn use ServerConnectionHandler's member variables or methods). I can't think of a clean way to handle all 3 cases here.
Before you close the socket in the destructor, shut it down for input. That will cause the receive thread to get an end of stream and exit nicely. You might want to add a little handshake between the dtor and the receiver thread before the final close, or you might just want to rely on the receiver thread closing the socket and not close it in the dtor at all.
"My problem is that when close() is called in the destructor, the receiver thread is still running" - seems to me your problem is merely thread synchronization, then. Little to do with connections.
Making the communications asynchronous gives you a lot more control over the receiving thread.
You could e.g. use Boost Asio to do the asynchronous socket reads (and writes, of course). If you add an "infinite" deadline_timer to the asynch queue, you can just cancel() that timer, which could be used by the receiving thread to stop the receive and do some more cleanups (e.g. write a "Goodbye" message to the remote end).
(If the latter were not required, just cancelling all async operations could be achieved by simply shutting down the io_service. That would be rather uncourteous, but not a bad idea in fast shutdown paths.)

How to handle a SIGPIPE error inside the object that generated it?

I have two applications, one server and other client, both written in C++ and Qt, but both of them also uses a C library that uses C socket methods to perform a socket communication between them (and this all in Linux).
When both of them are connected and I close the client, when the server tries to send a new message to it, it gets a SIGPIPE error and closes. I did some research on the web and in SO to see how could I create a handler for the SIGPIPE so instead of closing the application, I'ld tell the timers that constantly send the information to stop.
Now I did learn how to simply handle the signal: create a method that receives a int and use signal(SIGPIPE, myMethod) inside main() or global (note: learned that from SO and yes, I know that signal() is obsolete).
But the problem is that by doing this way I'm unable to stop the sending of information to the dead client, for the method that handles the signal needs to be either outside the class which sends the message or a static method, which don't have access to my server object.
To clarify, here is the current architecture:
//main.cpp
void signal_callback_handler(int signum)
{
qDebug() << "Caught signal SIGPIPE" << signum << "; closing the application";
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setApplicationName("ConnEmulator");
app.setApplicationVersion("1.0.0");
app.setOrganizationName("Embrasul");
app.setOrganizationDomain("http://www.embrasul.com.br");
MainWidget window;
window.show();
/* Catch Signal Handler SIGPIPE */
signal(SIGPIPE, signal_callback_handler);
return app.exec();
}
//The MainWidget class (simplified)
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget),
timerSendData(new QTimer(this))
{
ui->setupUi(this);
connect(timerSendData,SIGNAL(timeout()),this,SLOT(slotSendData()));
timerSendData->start();
//...
}
void MainWidget::slotSendData()
{
//Prepares data
//...
//Here the sending message is called with send()
if (hal_socket_write_to_client(&socket_descriptor, (u_int8_t *)buff_write, myBufferSize) == -1)
qDebug() << "Error writting to client";
}
//Socket library
int hal_socket_write_to_client(socket_t *obj, u_int8_t *buffer, int size)
{
struct s_socket_private * const socket_obj = (struct s_socket_private *)obj;
int retval = send(socket_obj->client_fd, buffer, size, 0);
if (retval < 0)
perror("write_to_client");
return retval;
}
So how can I make my MainWidget object created inside int main() handle the signal so he may call timerSendData->stop()?
SIGPIPE is ugly, but possible to deal with in a way that's fully encapsulated, thread-safe, and does not affect anything but the code making the write that might cause SIGPIPE. The general method is:
Block SIGPIPE with pthread_sigmask (or sigprocmask, but the latter is not guaranteed to be safe in multi-threaded programs) and save the original signal mask.
Perform the operation that might raise SIGPIPE.
Call sigtimedwait with a zero timeout to consume any pending SIGPIPE signal.
Restore the original signal mask (unblocking SIGPIPE if it was unblocked before).
Here's a try at some sample code using this method, in the form of a pure wrapper to write that avoids SIGPIPE:
ssize_t write_nosigpipe(int fd, void *buf, size_t len)
{
sigset_t oldset, newset;
ssize_t result;
siginfo_t si;
struct timespec ts = {0};
sigemptyset(&newset);
sigaddset(&newset, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
result = write(fd, buf, len);
while (sigtimedwait(newset, &si, &ts)>=0 || errno != EAGAIN);
pthread_sigmask(SIG_SETMASK, &oldset, 0);
return result;
}
It's untested (not even compiled) and may need minor fixes, but hopefully gets the point across. Obviously for efficiency you'd want to do this on a larger granularity than single write calls (for example, you could block SIGPIPE for the duration of the whole library function until it returns to the outside caller).
An alternate design would be simply blocking SIGPIPE and never unblocking it, and documenting in the function's interface that it leaves SIGPIPE blocked (note: blocking is thread-local and does not affect other threads) and possibly leaves SIGPIPE pending (in the blocked state). Then the caller would be responsible for restoring it if necessary, so the rare caller that wants SIGPIPE could get it (but after your function finishes) by unblocking the signal while the majority of callers could happily leave it blocked. The blocking code works like in the above, with the sigtimedwait/unblocking part removed. This is similar to Maxim's answer except that the impact is thread-local and thus thread-safe.
Now I did learn how to simply handle the signal: create a method that receives a int and use signal(SIGPIPE, myMethod)
You just need to ignore SIGPIPE, no handler is needed:
// don't raise SIGPIPE when sending into broken TCP connections
::signal(SIGPIPE, SIG_IGN);
But the problem is that by doing this way I'm unable to stop the sending of information to the dead client, for the method that handles the signal needs to be either outside the class which sends the message or a static method, which don't have access to my server object.
When SIGPIPE is ignored writing into a broken TCP connection returns error code EPIPE, which the socket wrappers you use should handle like the connection has been closed. Ideally, the socket wrapper should pass MSG_NOSIGNAL flag to send, so that send never raises SIGPIPE.