Apache Mesos
fd.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_WINDOWS_FD_HPP__
14 #define __STOUT_OS_WINDOWS_FD_HPP__
15 
16 #include <array>
17 #include <memory>
18 #include <ostream>
19 
20 #include <stout/check.hpp>
21 #include <stout/nothing.hpp>
22 #include <stout/try.hpp>
23 #include <stout/unreachable.hpp>
24 #include <stout/windows.hpp> // For `WinSock2.h`.
25 
26 namespace os {
27 
28 // The `WindowsFD` class exists to provide an common interface with the POSIX
29 // file descriptor. While the bare `int` representation of the POSIX file
30 // descriptor API is undesirable, we rendezvous there in order to maintain the
31 // existing code in Mesos.
32 //
33 // In the platform-agnostic code paths, the `int_fd` type is aliased to
34 // `WindowsFD`. The `os::*` functions return a type appropriate to the platform,
35 // which allows us to write code like this:
36 //
37 // Try<int_fd> fd = os::open(...);
38 //
39 // The `WindowsFD` constructs off one of:
40 // (1) `int` - from the WinCRT API
41 // (2) `HANDLE` - from the Win32 API
42 // (3) `SOCKET` - from the WinSock API
43 //
44 // The `os::*` functions then take an instance of `WindowsFD`, examines
45 // the state and dispatches to the appropriate API.
46 
47 class WindowsFD
48 {
49 public:
50  enum Type
51  {
55  };
56 
57  WindowsFD() = default;
58 
60  : type_(FD_CRT),
61  crt_(crt),
62  handle_(
63  crt < 0 ? INVALID_HANDLE_VALUE
64  : reinterpret_cast<HANDLE>(::_get_osfhandle(crt))) {}
65 
66  // IMPORTANT: The `HANDLE` here is expected to be file handles. Specifically,
67  // `HANDLE`s returned by file API such as `CreateFile`. There are
68  // APIs that return `HANDLE`s with different error values, and
69  // therefore must be handled accordingly. For example, a thread API
70  // such as `CreateThread` returns `NULL` as the error value, rather
71  // than `INVALID_HANDLE_VALUE`.
72  // TODO(mpark): Consider adding a second parameter which tells us what the
73  // error values are.
74  WindowsFD(HANDLE handle)
75  : type_(FD_HANDLE),
76  crt_(
77  handle == INVALID_HANDLE_VALUE
78  ? -1
79  : ::_open_osfhandle(reinterpret_cast<intptr_t>(handle), O_RDWR)),
80  handle_(handle) {}
81 
82  WindowsFD(SOCKET socket) : type_(FD_SOCKET), socket_(socket) {}
83 
84  // On Windows, libevent's `evutil_socket_t` is set to `intptr_t`.
85  WindowsFD(intptr_t socket)
86  : type_(FD_SOCKET),
87  socket_(static_cast<SOCKET>(socket)) {}
88 
89  WindowsFD(const WindowsFD&) = default;
90  WindowsFD(WindowsFD&&) = default;
91 
92  ~WindowsFD() = default;
93 
94  WindowsFD& operator=(const WindowsFD&) = default;
95  WindowsFD& operator=(WindowsFD&&) = default;
96 
97  int crt() const
98  {
99  CHECK((type() == FD_CRT) || (type() == FD_HANDLE));
100  return crt_;
101  }
102 
103  operator HANDLE() const
104  {
105  CHECK((type() == FD_CRT) || (type() == FD_HANDLE));
106  return handle_;
107  }
108 
109  operator SOCKET() const
110  {
111  CHECK_EQ(FD_SOCKET, type());
112  return socket_;
113  }
114 
115  operator intptr_t() const
116  {
117  CHECK_EQ(FD_SOCKET, type());
118  return static_cast<intptr_t>(socket_);
119  }
120 
121  Type type() const { return type_; }
122 
123 private:
124  Type type_;
125 
126  union
127  {
128  // We keep both a CRT FD as well as a `HANDLE`
129  // regardless of whether we were constructed
130  // from a file or a handle.
131  //
132  // This is because once we request for a CRT FD
133  // from a `HANDLE`, we're required to close it
134  // via `_close`. If we were to do the conversion
135  // lazily upon request, the resulting CRT FD
136  // would be dangling.
137  struct
138  {
139  int crt_;
141  };
142  SOCKET socket_;
143  };
144 };
145 
146 
147 inline std::ostream& operator<<(std::ostream& stream, const WindowsFD& fd)
148 {
149  switch (fd.type()) {
150  case WindowsFD::FD_CRT: {
151  stream << fd.crt();
152  break;
153  }
154  case WindowsFD::FD_HANDLE: {
155  stream << static_cast<HANDLE>(fd);
156  break;
157  }
158  case WindowsFD::FD_SOCKET: {
159  stream << static_cast<SOCKET>(fd);
160  break;
161  }
162  }
163  return stream;
164 }
165 
166 
167 // The complexity in this function is due to our effort in trying to support the
168 // cases where file descriptors are compared as an `int` on POSIX. For example,
169 // we use expressions such as `fd < 0` to check for validity.
170 // TODO(mpark): Consider introducing an `is_valid` function for `int_fd`.
171 inline bool operator<(const WindowsFD& left, const WindowsFD& right)
172 {
173  // In general, when compared against a `WindowsFD` in the `FD_CRT`, we map
174  // `INVALID_HANDLE_VALUE` and `INVALID_SOCKET` to `-1` before performing the
175  // comparison. The check for `< 0` followed by cast to `HANDLE` or `SOCKET` is
176  // due to the fact that `HANDLE` and `SOCKET` are both unsigned.
177  switch (left.type()) {
178  case WindowsFD::FD_CRT: {
179  switch (right.type()) {
180  case WindowsFD::FD_CRT: {
181  return left.crt() < right.crt();
182  }
183  case WindowsFD::FD_HANDLE: {
184  if (static_cast<HANDLE>(right) == INVALID_HANDLE_VALUE) {
185  return left.crt() < -1;
186  }
187  if (left.crt() < 0) {
188  return true;
189  }
190 #pragma warning(push)
191 #pragma warning(disable : 4312)
192  // Disable `int`-to-`HANDLE` compiler warning. This is safe to do,
193  // see comment above.
194  return reinterpret_cast<HANDLE>(left.crt()) <
195  static_cast<HANDLE>(right);
196 #pragma warning(pop)
197  }
198  case WindowsFD::FD_SOCKET: {
199  if (static_cast<SOCKET>(right) == INVALID_SOCKET) {
200  return left.crt() < -1;
201  }
202  if (left.crt() < 0) {
203  return true;
204  }
205  return static_cast<SOCKET>(left.crt()) < static_cast<SOCKET>(right);
206  }
207  }
208  }
209  case WindowsFD::FD_HANDLE: {
210  switch (right.type()) {
211  case WindowsFD::FD_CRT: {
212  if (static_cast<HANDLE>(left) == INVALID_HANDLE_VALUE) {
213  return -1 < right.crt();
214  }
215  if (right.crt() < 0) {
216  return false;
217  }
218 #pragma warning(push)
219 #pragma warning(disable : 4312)
220  // Disable `int`-to-`HANDLE` compiler warning. This is safe to do,
221  // see comment above.
222  return static_cast<HANDLE>(left) <
223  reinterpret_cast<HANDLE>(right.crt());
224 #pragma warning(pop)
225  }
226  case WindowsFD::FD_HANDLE: {
227  return static_cast<HANDLE>(left) < static_cast<HANDLE>(right);
228  }
229  case WindowsFD::FD_SOCKET: {
230  return static_cast<HANDLE>(left) <
231  reinterpret_cast<HANDLE>(static_cast<SOCKET>(right));
232  }
233  }
234  }
235  case WindowsFD::FD_SOCKET: {
236  switch (right.type()) {
237  case WindowsFD::FD_CRT: {
238  if (static_cast<SOCKET>(left) == INVALID_SOCKET) {
239  return -1 < right.crt();
240  }
241  if (right.crt() < 0) {
242  return false;
243  }
244  return static_cast<SOCKET>(left) < static_cast<SOCKET>(right.crt());
245  }
246  case WindowsFD::FD_HANDLE: {
247  return reinterpret_cast<HANDLE>(static_cast<SOCKET>(left)) <
248  static_cast<HANDLE>(right);
249  }
250  case WindowsFD::FD_SOCKET: {
251  return static_cast<SOCKET>(left) < static_cast<SOCKET>(right);
252  }
253  }
254  }
255  }
256  UNREACHABLE();
257 }
258 
259 
260 inline bool operator<(int left, const WindowsFD& right)
261 {
262  return WindowsFD(left) < right;
263 }
264 
265 
266 inline bool operator<(const WindowsFD& left, int right)
267 {
268  return left < WindowsFD(right);
269 }
270 
271 
272 inline bool operator>(const WindowsFD& left, const WindowsFD& right)
273 {
274  return right < left;
275 }
276 
277 
278 inline bool operator>(int left, const WindowsFD& right)
279 {
280  return WindowsFD(left) > right;
281 }
282 
283 
284 inline bool operator>(const WindowsFD& left, int right)
285 {
286  return left > WindowsFD(right);
287 }
288 
289 
290 inline bool operator<=(const WindowsFD& left, const WindowsFD& right)
291 {
292  return !(left > right);
293 }
294 
295 
296 inline bool operator<=(int left, const WindowsFD& right)
297 {
298  return WindowsFD(left) <= right;
299 }
300 
301 
302 inline bool operator<=(const WindowsFD& left, int right)
303 {
304  return left <= WindowsFD(right);
305 }
306 
307 
308 inline bool operator>=(const WindowsFD& left, const WindowsFD& right)
309 {
310  return !(left < right);
311 }
312 
313 
314 inline bool operator>=(int left, const WindowsFD& right)
315 {
316  return WindowsFD(left) >= right;
317 }
318 
319 
320 inline bool operator>=(const WindowsFD& left, int right)
321 {
322  return left >= WindowsFD(right);
323 }
324 
325 
326 // The complexity in this function is due to our effort in trying to support the
327 // cases where file descriptors are compared as an `int` on POSIX. For example,
328 // we use expressions such as `fd != -1` to check for validity.
329 // TODO(mpark): Consider introducing an `is_valid` function for `int_fd`.
330 inline bool operator==(const WindowsFD& left, const WindowsFD& right)
331 {
332  // In general, when compared against a `WindowsFD` in the `FD_CRT`, we map
333  // `INVALID_HANDLE_VALUE` and `INVALID_SOCKET` to `-1` before performing the
334  // comparison. The check for `< 0` followed by cast to `HANDLE` or `SOCKET` is
335  // due to the fact that `HANDLE` and `SOCKET` are both unsigned.
336  switch (left.type()) {
337  case WindowsFD::FD_CRT: {
338  switch (right.type()) {
339  case WindowsFD::FD_CRT: {
340  return left.crt() == right.crt();
341  }
342  case WindowsFD::FD_HANDLE: {
343  if (static_cast<HANDLE>(right) == INVALID_HANDLE_VALUE) {
344  return left.crt() == -1;
345  }
346  if (left.crt() < 0) {
347  return false;
348  }
349 #pragma warning(push)
350 #pragma warning(disable : 4312)
351  // Disable `int`-to-`HANDLE` compiler warning. This is safe to do,
352  // see comment above.
353  return reinterpret_cast<HANDLE>(left.crt()) ==
354  static_cast<HANDLE>(right);
355 #pragma warning(pop)
356  }
357  case WindowsFD::FD_SOCKET: {
358  if (static_cast<SOCKET>(right) == INVALID_SOCKET) {
359  return left.crt() == -1;
360  }
361  if (left.crt() < 0) {
362  return false;
363  }
364  return static_cast<SOCKET>(left.crt()) == static_cast<SOCKET>(right);
365  }
366  }
367  }
368  case WindowsFD::FD_HANDLE: {
369  switch (right.type()) {
370  case WindowsFD::FD_CRT: {
371  if (static_cast<HANDLE>(left) == INVALID_HANDLE_VALUE) {
372  return -1 == right.crt();
373  }
374  if (right.crt() < 0) {
375  return false;
376  }
377 #pragma warning(push)
378 #pragma warning(disable : 4312)
379  // Disable `int`-to-`HANDLE` compiler warning. This is safe to do,
380  // see comment above.
381  return static_cast<HANDLE>(left) ==
382  reinterpret_cast<HANDLE>(right.crt());
383 #pragma warning(pop)
384  }
385  case WindowsFD::FD_HANDLE: {
386  return static_cast<HANDLE>(left) == static_cast<HANDLE>(right);
387  }
388  case WindowsFD::FD_SOCKET: {
389  return static_cast<HANDLE>(left) ==
390  reinterpret_cast<HANDLE>(static_cast<SOCKET>(right));
391  }
392  }
393  }
394  case WindowsFD::FD_SOCKET: {
395  switch (right.type()) {
396  case WindowsFD::FD_CRT: {
397  if (static_cast<SOCKET>(left) == INVALID_SOCKET) {
398  return -1 == right.crt();
399  }
400  if (right.crt() < 0) {
401  return false;
402  }
403  return static_cast<SOCKET>(left) == static_cast<SOCKET>(right.crt());
404  }
405  case WindowsFD::FD_HANDLE: {
406  return reinterpret_cast<HANDLE>(static_cast<SOCKET>(left)) ==
407  static_cast<HANDLE>(right);
408  }
409  case WindowsFD::FD_SOCKET: {
410  return static_cast<SOCKET>(left) == static_cast<SOCKET>(right);
411  }
412  }
413  }
414  }
415  UNREACHABLE();
416 }
417 
418 
419 inline bool operator==(int left, const WindowsFD& right)
420 {
421  return WindowsFD(left) == right;
422 }
423 
424 
425 inline bool operator==(const WindowsFD& left, int right)
426 {
427  return left == WindowsFD(right);
428 }
429 
430 
431 inline bool operator!=(const WindowsFD& left, const WindowsFD& right)
432 {
433  return !(left == right);
434 }
435 
436 
437 inline bool operator!=(int left, const WindowsFD& right)
438 {
439  return WindowsFD(left) != right;
440 }
441 
442 
443 inline bool operator!=(const WindowsFD& left, int right)
444 {
445  return left != WindowsFD(right);
446 }
447 
448 } // namespace os {
449 
450 namespace std {
451 
452 template <>
453 struct hash<os::WindowsFD>
454 {
456  using result_type = size_t;
457 
459  {
460  switch (fd.type()) {
461  case os::WindowsFD::FD_CRT: {
462  return static_cast<result_type>(fd.crt());
463  }
465  return reinterpret_cast<result_type>(static_cast<HANDLE>(fd));
466  }
468  return static_cast<result_type>(static_cast<SOCKET>(fd));
469  }
470  }
471  UNREACHABLE();
472  }
473 };
474 
475 } // namespace std {
476 
477 #endif // __STOUT_OS_WINDOWS_FD_HPP__
Definition: fd.hpp:54
bool operator>=(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:308
Definition: fd.hpp:53
Type type() const
Definition: fd.hpp:121
SOCKET socket_
Definition: fd.hpp:142
Definition: fd.hpp:47
bool operator==(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:330
result_type operator()(const argument_type &fd) const
Definition: fd.hpp:458
int crt_
Definition: fd.hpp:139
~WindowsFD()=default
constexpr Handle HANDLE
Definition: ingress.hpp:37
Type
Definition: fd.hpp:50
bool operator<(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:171
WindowsFD(HANDLE handle)
Definition: fd.hpp:74
WindowsFD(int crt)
Definition: fd.hpp:59
#define UNREACHABLE()
Definition: unreachable.hpp:22
int crt() const
Definition: fd.hpp:97
bool operator!=(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:431
std::ostream & operator<<(std::ostream &stream, const ProcessTree &tree)
Definition: process.hpp:126
WindowsFD()=default
WindowsFD & operator=(const WindowsFD &)=default
WindowsFD(SOCKET socket)
Definition: fd.hpp:82
WindowsFD(intptr_t socket)
Definition: fd.hpp:85
#define O_RDWR
Definition: fcntl.hpp:27
Try< Netlink< struct nl_sock > > socket(int protocol=NETLINK_ROUTE)
Definition: internal.hpp:91
bool operator>(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:272
bool operator<=(const WindowsFD &left, const WindowsFD &right)
Definition: fd.hpp:290
Definition: fd.hpp:52
size_t result_type
Definition: fd.hpp:456
HANDLE handle_
Definition: fd.hpp:140