Incorporate changes from Dasynq upstream.
[oweals/dinit.git] / src / dasynq / dasynq-interrupt.h
1 #ifndef DASYNQ_INTERRUPT_H_INCLUDED
2 #define DASYNQ_INTERRUPT_H_INCLUDED
3
4 #include <unistd.h>
5 #include <fcntl.h>
6
7 #include "dasynq-config.h"
8 #include "dasynq-mutex.h"
9
10 /*
11  * Mechanism for interrupting an event loop wait.
12  */
13
14 namespace dasynq {
15
16 template <typename Base, typename Mutex = typename Base::mutex_t> class interrupt_channel;
17
18 // In the non-multi-thread case, this doesn't need to be supported:
19 template <typename Base> class interrupt_channel<Base, null_mutex> : public Base
20 {
21     public:
22     void interrupt_wait()
23     {
24
25     }
26 };
27
28 template <typename Base, typename Mutex> class interrupt_channel : public Base
29 {
30 #ifdef HAVE_PIPE2
31     int create_pipe(int filedes[2])
32     {
33         return pipe2(filedes, O_CLOEXEC | O_NONBLOCK);
34     }
35 #else
36     int create_pipe(int filedes[2])
37     {
38         int r = pipe(filedes);
39         if (r != -1) {
40             fcntl(filedes[0], F_SETFD, O_CLOEXEC);
41             fcntl(filedes[1], F_SETFD, O_CLOEXEC);
42             fcntl(filedes[0], F_SETFL, O_NONBLOCK);
43             fcntl(filedes[1], F_SETFL, O_NONBLOCK);
44         }
45         return r;
46     }
47 #endif
48
49     int pipe_r_fd;
50     int pipe_w_fd;
51
52     public:
53
54     template <typename T> void init(T *loop_mech)
55     {
56         int pipedes[2];
57         if (create_pipe(pipedes) == -1) {
58             throw std::system_error(errno, std::system_category());
59         }
60
61         pipe_r_fd = pipedes[0];
62         pipe_w_fd = pipedes[1];
63
64         try {
65             loop_mech->addFdWatch(pipe_r_fd, &pipe_r_fd, IN_EVENTS);
66         }
67         catch (...) {
68             close (pipe_r_fd);
69             close (pipe_w_fd);
70             throw;
71         }
72
73         Base::init(loop_mech);
74     }
75
76     template <typename T>
77     void receiveFdEvent(T &loop_mech, typename Base::FD_r fd_r, void * userdata, int flags)
78     {
79         if (userdata == &pipe_r_fd) {
80             // try to clear the pipe
81             char buf[64];
82             read(pipe_r_fd, buf, 64);
83         }
84         else {
85             Base::receiveFdEvent(loop_mech, fd_r, userdata, flags);
86         }
87     }
88
89     void interrupt_wait()
90     {
91         char buf[1] = { 0 };
92         write(pipe_w_fd, buf, 1);
93     }
94 };
95
96 }
97
98 #endif /* DASYNQ_INTERRUPT_H_INCLUDED */