1 | /* |
2 | * IXSelectInterruptPipe.cpp |
3 | * Author: Benjamin Sergeant |
4 | * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. |
5 | */ |
6 | |
7 | // |
8 | // On UNIX we use pipes to wake up select. There is no way to do that |
9 | // on Windows so this file is compiled out on Windows. |
10 | // |
11 | #ifndef _WIN32 |
12 | |
13 | #include "IXSelectInterruptPipe.h" |
14 | |
15 | #include <assert.h> |
16 | #include <errno.h> |
17 | #include <fcntl.h> |
18 | #include <sstream> |
19 | #include <string.h> // for strerror |
20 | #include <unistd.h> // for write |
21 | |
22 | namespace ix |
23 | { |
24 | // File descriptor at index 0 in _fildes is the read end of the pipe |
25 | // File descriptor at index 1 in _fildes is the write end of the pipe |
26 | const int SelectInterruptPipe::kPipeReadIndex = 0; |
27 | const int SelectInterruptPipe::kPipeWriteIndex = 1; |
28 | |
29 | SelectInterruptPipe::SelectInterruptPipe() |
30 | { |
31 | _fildes[kPipeReadIndex] = -1; |
32 | _fildes[kPipeWriteIndex] = -1; |
33 | } |
34 | |
35 | SelectInterruptPipe::~SelectInterruptPipe() |
36 | { |
37 | ::close(_fildes[kPipeReadIndex]); |
38 | ::close(_fildes[kPipeWriteIndex]); |
39 | _fildes[kPipeReadIndex] = -1; |
40 | _fildes[kPipeWriteIndex] = -1; |
41 | } |
42 | |
43 | bool SelectInterruptPipe::init(std::string& errorMsg) |
44 | { |
45 | std::lock_guard<std::mutex> lock(_fildesMutex); |
46 | |
47 | // calling init twice is a programming error |
48 | assert(_fildes[kPipeReadIndex] == -1); |
49 | assert(_fildes[kPipeWriteIndex] == -1); |
50 | |
51 | if (pipe(_fildes) < 0) |
52 | { |
53 | std::stringstream ss; |
54 | ss << "SelectInterruptPipe::init() failed in pipe() call" |
55 | << " : " << strerror(errno); |
56 | errorMsg = ss.str(); |
57 | return false; |
58 | } |
59 | |
60 | if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1) |
61 | { |
62 | std::stringstream ss; |
63 | ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call" |
64 | << " : " << strerror(errno); |
65 | errorMsg = ss.str(); |
66 | |
67 | _fildes[kPipeReadIndex] = -1; |
68 | _fildes[kPipeWriteIndex] = -1; |
69 | return false; |
70 | } |
71 | |
72 | if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1) |
73 | { |
74 | std::stringstream ss; |
75 | ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call" |
76 | << " : " << strerror(errno); |
77 | errorMsg = ss.str(); |
78 | |
79 | _fildes[kPipeReadIndex] = -1; |
80 | _fildes[kPipeWriteIndex] = -1; |
81 | return false; |
82 | } |
83 | |
84 | #ifdef F_SETNOSIGPIPE |
85 | if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) |
86 | { |
87 | std::stringstream ss; |
88 | ss << "SelectInterruptPipe::init() failed in fcntl(.... F_SETNOSIGPIPE) call" |
89 | << " : " << strerror(errno); |
90 | errorMsg = ss.str(); |
91 | |
92 | _fildes[kPipeReadIndex] = -1; |
93 | _fildes[kPipeWriteIndex] = -1; |
94 | return false; |
95 | } |
96 | |
97 | if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) |
98 | { |
99 | std::stringstream ss; |
100 | ss << "SelectInterruptPipe::init() failed in fcntl(..., F_SETNOSIGPIPE) call" |
101 | << " : " << strerror(errno); |
102 | errorMsg = ss.str(); |
103 | |
104 | _fildes[kPipeReadIndex] = -1; |
105 | _fildes[kPipeWriteIndex] = -1; |
106 | return false; |
107 | } |
108 | #endif |
109 | |
110 | return true; |
111 | } |
112 | |
113 | bool SelectInterruptPipe::notify(uint64_t value) |
114 | { |
115 | std::lock_guard<std::mutex> lock(_fildesMutex); |
116 | |
117 | int fd = _fildes[kPipeWriteIndex]; |
118 | if (fd == -1) return false; |
119 | |
120 | ssize_t ret = -1; |
121 | do |
122 | { |
123 | ret = ::write(fd, &value, sizeof(value)); |
124 | } while (ret == -1 && errno == EINTR); |
125 | |
126 | // we should write 8 bytes for an uint64_t |
127 | return ret == 8; |
128 | } |
129 | |
130 | // TODO: return max uint64_t for errors ? |
131 | uint64_t SelectInterruptPipe::read() |
132 | { |
133 | std::lock_guard<std::mutex> lock(_fildesMutex); |
134 | |
135 | int fd = _fildes[kPipeReadIndex]; |
136 | |
137 | uint64_t value = 0; |
138 | |
139 | ssize_t ret = -1; |
140 | do |
141 | { |
142 | ret = ::read(fd, &value, sizeof(value)); |
143 | } while (ret == -1 && errno == EINTR); |
144 | |
145 | return value; |
146 | } |
147 | |
148 | bool SelectInterruptPipe::clear() |
149 | { |
150 | return true; |
151 | } |
152 | |
153 | int SelectInterruptPipe::getFd() const |
154 | { |
155 | std::lock_guard<std::mutex> lock(_fildesMutex); |
156 | |
157 | return _fildes[kPipeReadIndex]; |
158 | } |
159 | } // namespace ix |
160 | |
161 | #endif // !_WIN32 |
162 | |