1#pragma once
2
3#include <signal.h>
4#include <errno.h>
5#include <Common/Exception.h>
6
7
8namespace DB
9{
10
11namespace ErrorCodes
12{
13 extern const int CANNOT_MANIPULATE_SIGSET;
14 extern const int CANNOT_WAIT_FOR_SIGNAL;
15 extern const int CANNOT_BLOCK_SIGNAL;
16 extern const int CANNOT_UNBLOCK_SIGNAL;
17}
18
19#ifdef __APPLE__
20// We only need to support timeout = {0, 0} at this moment
21static int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * /*timeout*/)
22{
23 sigset_t pending;
24 int signo;
25 sigpending(&pending);
26
27 for (signo = 1; signo < NSIG; ++signo)
28 {
29 if (sigismember(set, signo) && sigismember(&pending, signo))
30 {
31 sigwait(set, &signo);
32 if (info)
33 {
34 memset(info, 0, sizeof *info);
35 info->si_signo = signo;
36 }
37 return signo;
38 }
39 }
40 errno = EAGAIN;
41
42 return -1;
43}
44#endif
45
46
47/** As long as there exists an object of this class - it blocks the INT signal, at the same time it lets you know if it came.
48 * This is necessary so that you can interrupt the execution of the request with Ctrl+C.
49 * Use only one instance of this class at a time.
50 * If `check` method returns true (the signal has arrived), the next call will wait for the next signal.
51 */
52class InterruptListener
53{
54private:
55 bool active;
56 sigset_t sig_set;
57
58public:
59 InterruptListener() : active(false)
60 {
61 if (sigemptyset(&sig_set)
62 || sigaddset(&sig_set, SIGINT))
63 throwFromErrno("Cannot manipulate with signal set.", ErrorCodes::CANNOT_MANIPULATE_SIGSET);
64
65 block();
66 }
67
68 ~InterruptListener()
69 {
70 unblock();
71 }
72
73 bool check()
74 {
75 if (!active)
76 return false;
77
78 timespec timeout = { 0, 0 };
79
80 if (-1 == sigtimedwait(&sig_set, nullptr, &timeout))
81 {
82 if (errno == EAGAIN)
83 return false;
84 else
85 throwFromErrno("Cannot poll signal (sigtimedwait).", ErrorCodes::CANNOT_WAIT_FOR_SIGNAL);
86 }
87
88 return true;
89 }
90
91 void block()
92 {
93 if (!active)
94 {
95 if (pthread_sigmask(SIG_BLOCK, &sig_set, nullptr))
96 throwFromErrno("Cannot block signal.", ErrorCodes::CANNOT_BLOCK_SIGNAL);
97
98 active = true;
99 }
100 }
101
102 /// You can stop blocking the signal earlier than in the destructor.
103 void unblock()
104 {
105 if (active)
106 {
107 if (pthread_sigmask(SIG_UNBLOCK, &sig_set, nullptr))
108 throwFromErrno("Cannot unblock signal.", ErrorCodes::CANNOT_UNBLOCK_SIGNAL);
109
110 active = false;
111 }
112 }
113};
114
115}
116