New Version (0.4) of Boost.Application (Aspect Based) available to comments (RFC).
Hi Stas, Thanks for your comments!
I think it worth noting in documentation that SIGUSR1 is reserved for internal purposes. I will do!
What do you think about self pipe built-in functionality? It is a widely used trick. I think it can be implemented as an aspect in a quite straightforward way.
Can you give more details about what you're proposing here? Elegant signal handler is my main problem in this lib! I want ideas to get it correctly designed! Thanks for documentation appointments, I will fix all! And I forgot to put the main question on message: "Is there any interest in a library like this?" What you think? Thanks a lot!
2013/11/7 Renato Forti
What do you think about self pipe built-in functionality? It is a widely used trick. I think it can be implemented as an aspect in a quite straightforward way.
Can you give more details about what you're proposing here? Elegant signal handler is my main problem in this lib! I want ideas to get it correctly designed!
The problem is to transfer interruption signal to the thread performing I/O multiplexing, for instance, waiting for new data during select(2) call. Default signal handler will force select(..) to return, but as far as we want to have a custom one, we need to abort select(..) manually. One can set a reasonable timeout for I/O operation, and check whether interruption signal have been delivered or not after timeout expires. For example, while (1) { if (termination_signal_received) { break; } // imagine signal received here, termination_signal_received set to true. // nevertheless, to get out of the loop we have to wait until timeout expires select(...); } This is a feasible solution, but it yields delayed termination and can not be accepted for some particular cases. The tricky solution is to set up an non-blocking pipe and add its read end to select(2)'s readfds array. Hence select(..) will return immediately when any data will be written to the pipe's write end. Therefore, write(selfpipe_write_fd, "", 1); must be called in signal handler after the termination_signal_received is set to true. The same effect can be achieved with signalfd(2). Unfortunately, this syscall is linux-specific. Here is a draft of the proposed aspect: class selfpipe : noncopyable { protected: void setup() { pipe(fd_); fcntl(fd_[READ_FD], F_SETFL, fcntl(fd_[READ_FD], F_GETFL) | O_NONBLOCK); fcntl(fd_[WRITE_FD], F_SETFL, fcntl(fd_[WRITE_FD], F_GETFL) | O_NONBLOCK); } void teardown() { close(fd_[READ_FD]; close(fd_[WRITE_FD]; } public: int readfd() const { return fd_[READ_FD]; } void poke() { write(fd_[WRITE_FD], "", 1); } private: enum { READ_FD = 0, WRITE_FD = 1 }; int fd_[2]; }; If an application has selfpipe aspect, the selfpipe::setup() method must be called during application's initialization, and selfpipe::teardown() during termination. -- Regards, Stas.
The problem is to transfer interruption signal to the thread performing I/O multiplexing, for instance, waiting for new data during select(2) call. Default signal handler will force select(..) to return, but as far as we want to have a custom one, we need to abort select(..) manually.
The tricky solution is to set up an non-blocking pipe and add its read end to select(2)'s readfds array. Hence select(..) will return immediately when any data will be written to the pipe's write end. Therefore, write(selfpipe_write_fd, "", 1); must be called in signal handler after the termination_signal_received is set to true.
The same effect can be achieved with signalfd(2). Unfortunately, this syscall is linux-specific.
Here is a draft of the proposed aspect:
class selfpipe : noncopyable { protected: void setup() { pipe(fd_); fcntl(fd_[READ_FD], F_SETFL, fcntl(fd_[READ_FD], F_GETFL) | O_NONBLOCK); fcntl(fd_[WRITE_FD], F_SETFL, fcntl(fd_[WRITE_FD], F_GETFL) | O_NONBLOCK); } void teardown() { close(fd_[READ_FD]; close(fd_[WRITE_FD]; } public: int readfd() const { return fd_[READ_FD]; } void poke() { write(fd_[WRITE_FD], "", 1); } private: enum { READ_FD = 0, WRITE_FD = 1 }; int fd_[2]; }; Thanks a lot for the code, I want to implement it on lib. Some questions: The user need use 'select' to use this aspect? How user will use it? (on final application code) I can't figure it! I tell this because, One question that comes to mind! We have something like that in Windows side? I must/want provide an 'aspect' with same functionality or interface on both sides! I need tie all internally on lib, and provide a normalized interface, but signals are completely different on windows (windows don't have signal) and posix. Any idea ? Thanks a lot.
2013/11/8 Renato Forti
The user need use 'select' to use this aspect? How user will use it? (on final application code)
The aspect exposes a raw file descriptor in it's public interface selfpipe::readfd() and this descriptor can be passed into some I/O processing library. For example, one can assign native file descriptor to boost::asio::posix::stream_descriptor to use selfpipe with boost::asio. Also one can add this file descriptor to libevent's set of monitored descriptors. And of course, this file descriptor can be passed into direct select/poll calls. selfpipe::poke() interface can be called both explicitly and implicitly. In must be called implicitly on app's termination to allow I/O threads terminate gracefully. Explicit call may be useful when handling non-termination signals. For instance, configuration reload without restarting application is often done using user signals. Switching database backend without dropping active connections is a good aim, isn't it? :) We have something like that in Windows side? AFAIK, no. Nevertheless, I think even POSIX-only selfpipe aspect will be useful. -- Regards, Stas.
On 08/11/2013 11:43, Renato Forti wrote:
class selfpipe : noncopyable { protected: void setup() { pipe(fd_); fcntl(fd_[READ_FD], F_SETFL, fcntl(fd_[READ_FD], F_GETFL) | O_NONBLOCK); fcntl(fd_[WRITE_FD], F_SETFL, fcntl(fd_[WRITE_FD], F_GETFL) | O_NONBLOCK); }
void teardown() { close(fd_[READ_FD]; close(fd_[WRITE_FD]; }
public: int readfd() const { return fd_[READ_FD]; } void poke() { write(fd_[WRITE_FD], "", 1); }
private: enum { READ_FD = 0, WRITE_FD = 1 }; int fd_[2]; };
How is that supposed to work in Windows? (Does it need to?) Its sensible to expose 'something' that can be integrated to native event loops, but the ideal choice under Windows is probably an event, not a pipe descriptor. I think some analysis is needed.
participants (3)
-
james
-
Renato Forti
-
Stanislav Ivochkin