]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // detail/impl/descriptor_ops.ipp | |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
f67539c2 | 5 | // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #ifndef BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP | |
12 | #define BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP | |
13 | ||
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
15 | # pragma once | |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
17 | ||
18 | #include <boost/asio/detail/config.hpp> | |
19 | #include <cerrno> | |
20 | #include <boost/asio/detail/descriptor_ops.hpp> | |
21 | #include <boost/asio/error.hpp> | |
22 | ||
23 | #if !defined(BOOST_ASIO_WINDOWS) \ | |
24 | && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \ | |
25 | && !defined(__CYGWIN__) | |
26 | ||
27 | #include <boost/asio/detail/push_options.hpp> | |
28 | ||
29 | namespace boost { | |
30 | namespace asio { | |
31 | namespace detail { | |
32 | namespace descriptor_ops { | |
33 | ||
34 | int open(const char* path, int flags, boost::system::error_code& ec) | |
35 | { | |
20effc67 TL |
36 | int result = ::open(path, flags); |
37 | get_last_error(ec, result < 0); | |
7c673cae FG |
38 | return result; |
39 | } | |
40 | ||
41 | int close(int d, state_type& state, boost::system::error_code& ec) | |
42 | { | |
43 | int result = 0; | |
44 | if (d != -1) | |
45 | { | |
20effc67 TL |
46 | result = ::close(d); |
47 | get_last_error(ec, result < 0); | |
7c673cae FG |
48 | |
49 | if (result != 0 | |
50 | && (ec == boost::asio::error::would_block | |
51 | || ec == boost::asio::error::try_again)) | |
52 | { | |
53 | // According to UNIX Network Programming Vol. 1, it is possible for | |
54 | // close() to fail with EWOULDBLOCK under certain circumstances. What | |
55 | // isn't clear is the state of the descriptor after this error. The one | |
56 | // current OS where this behaviour is seen, Windows, says that the socket | |
57 | // remains open. Therefore we'll put the descriptor back into blocking | |
58 | // mode and have another attempt at closing it. | |
f67539c2 | 59 | #if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae FG |
60 | int flags = ::fcntl(d, F_GETFL, 0); |
61 | if (flags >= 0) | |
62 | ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); | |
f67539c2 | 63 | #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae FG |
64 | ioctl_arg_type arg = 0; |
65 | ::ioctl(d, FIONBIO, &arg); | |
f67539c2 | 66 | #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae FG |
67 | state &= ~non_blocking; |
68 | ||
20effc67 TL |
69 | result = ::close(d); |
70 | get_last_error(ec, result < 0); | |
7c673cae FG |
71 | } |
72 | } | |
73 | ||
7c673cae FG |
74 | return result; |
75 | } | |
76 | ||
77 | bool set_user_non_blocking(int d, state_type& state, | |
78 | bool value, boost::system::error_code& ec) | |
79 | { | |
80 | if (d == -1) | |
81 | { | |
82 | ec = boost::asio::error::bad_descriptor; | |
83 | return false; | |
84 | } | |
85 | ||
f67539c2 | 86 | #if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
20effc67 TL |
87 | int result = ::fcntl(d, F_GETFL, 0); |
88 | get_last_error(ec, result < 0); | |
7c673cae FG |
89 | if (result >= 0) |
90 | { | |
7c673cae | 91 | int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); |
20effc67 TL |
92 | result = ::fcntl(d, F_SETFL, flag); |
93 | get_last_error(ec, result < 0); | |
7c673cae | 94 | } |
f67539c2 | 95 | #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae | 96 | ioctl_arg_type arg = (value ? 1 : 0); |
20effc67 TL |
97 | int result = ::ioctl(d, FIONBIO, &arg); |
98 | get_last_error(ec, result < 0); | |
f67539c2 | 99 | #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae FG |
100 | |
101 | if (result >= 0) | |
102 | { | |
7c673cae FG |
103 | if (value) |
104 | state |= user_set_non_blocking; | |
105 | else | |
106 | { | |
107 | // Clearing the user-set non-blocking mode always overrides any | |
108 | // internally-set non-blocking flag. Any subsequent asynchronous | |
109 | // operations will need to re-enable non-blocking I/O. | |
110 | state &= ~(user_set_non_blocking | internal_non_blocking); | |
111 | } | |
112 | return true; | |
113 | } | |
114 | ||
115 | return false; | |
116 | } | |
117 | ||
118 | bool set_internal_non_blocking(int d, state_type& state, | |
119 | bool value, boost::system::error_code& ec) | |
120 | { | |
121 | if (d == -1) | |
122 | { | |
123 | ec = boost::asio::error::bad_descriptor; | |
124 | return false; | |
125 | } | |
126 | ||
127 | if (!value && (state & user_set_non_blocking)) | |
128 | { | |
129 | // It does not make sense to clear the internal non-blocking flag if the | |
130 | // user still wants non-blocking behaviour. Return an error and let the | |
131 | // caller figure out whether to update the user-set non-blocking flag. | |
132 | ec = boost::asio::error::invalid_argument; | |
133 | return false; | |
134 | } | |
135 | ||
f67539c2 | 136 | #if defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
20effc67 TL |
137 | int result = ::fcntl(d, F_GETFL, 0); |
138 | get_last_error(ec, result < 0); | |
7c673cae FG |
139 | if (result >= 0) |
140 | { | |
7c673cae | 141 | int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); |
20effc67 TL |
142 | result = ::fcntl(d, F_SETFL, flag); |
143 | get_last_error(ec, result < 0); | |
7c673cae | 144 | } |
f67539c2 | 145 | #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae | 146 | ioctl_arg_type arg = (value ? 1 : 0); |
20effc67 TL |
147 | int result = ::ioctl(d, FIONBIO, &arg); |
148 | get_last_error(ec, result < 0); | |
f67539c2 | 149 | #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) |
7c673cae FG |
150 | |
151 | if (result >= 0) | |
152 | { | |
7c673cae FG |
153 | if (value) |
154 | state |= internal_non_blocking; | |
155 | else | |
156 | state &= ~internal_non_blocking; | |
157 | return true; | |
158 | } | |
159 | ||
160 | return false; | |
161 | } | |
162 | ||
163 | std::size_t sync_read(int d, state_type state, buf* bufs, | |
164 | std::size_t count, bool all_empty, boost::system::error_code& ec) | |
165 | { | |
166 | if (d == -1) | |
167 | { | |
168 | ec = boost::asio::error::bad_descriptor; | |
169 | return 0; | |
170 | } | |
171 | ||
172 | // A request to read 0 bytes on a stream is a no-op. | |
173 | if (all_empty) | |
174 | { | |
20effc67 | 175 | ec.assign(0, ec.category()); |
7c673cae FG |
176 | return 0; |
177 | } | |
178 | ||
179 | // Read some data. | |
180 | for (;;) | |
181 | { | |
182 | // Try to complete the operation without blocking. | |
20effc67 TL |
183 | signed_size_type bytes = ::readv(d, bufs, static_cast<int>(count)); |
184 | get_last_error(ec, bytes < 0); | |
185 | ||
186 | // Check if operation succeeded. | |
187 | if (bytes > 0) | |
188 | return bytes; | |
189 | ||
190 | // Check for EOF. | |
191 | if (bytes == 0) | |
192 | { | |
193 | ec = boost::asio::error::eof; | |
194 | return 0; | |
195 | } | |
196 | ||
197 | // Operation failed. | |
198 | if ((state & user_set_non_blocking) | |
199 | || (ec != boost::asio::error::would_block | |
200 | && ec != boost::asio::error::try_again)) | |
201 | return 0; | |
202 | ||
203 | // Wait for descriptor to become ready. | |
204 | if (descriptor_ops::poll_read(d, 0, ec) < 0) | |
205 | return 0; | |
206 | } | |
207 | } | |
208 | ||
209 | std::size_t sync_read1(int d, state_type state, void* data, | |
210 | std::size_t size, boost::system::error_code& ec) | |
211 | { | |
212 | if (d == -1) | |
213 | { | |
214 | ec = boost::asio::error::bad_descriptor; | |
215 | return 0; | |
216 | } | |
217 | ||
218 | // A request to read 0 bytes on a stream is a no-op. | |
219 | if (size == 0) | |
220 | { | |
221 | ec.assign(0, ec.category()); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | // Read some data. | |
226 | for (;;) | |
227 | { | |
228 | // Try to complete the operation without blocking. | |
229 | signed_size_type bytes = ::read(d, data, size); | |
230 | get_last_error(ec, bytes < 0); | |
7c673cae FG |
231 | |
232 | // Check if operation succeeded. | |
233 | if (bytes > 0) | |
234 | return bytes; | |
235 | ||
236 | // Check for EOF. | |
237 | if (bytes == 0) | |
238 | { | |
239 | ec = boost::asio::error::eof; | |
240 | return 0; | |
241 | } | |
242 | ||
243 | // Operation failed. | |
244 | if ((state & user_set_non_blocking) | |
245 | || (ec != boost::asio::error::would_block | |
246 | && ec != boost::asio::error::try_again)) | |
247 | return 0; | |
248 | ||
249 | // Wait for descriptor to become ready. | |
250 | if (descriptor_ops::poll_read(d, 0, ec) < 0) | |
251 | return 0; | |
252 | } | |
253 | } | |
254 | ||
255 | bool non_blocking_read(int d, buf* bufs, std::size_t count, | |
256 | boost::system::error_code& ec, std::size_t& bytes_transferred) | |
257 | { | |
258 | for (;;) | |
259 | { | |
260 | // Read some data. | |
20effc67 TL |
261 | signed_size_type bytes = ::readv(d, bufs, static_cast<int>(count)); |
262 | get_last_error(ec, bytes < 0); | |
7c673cae FG |
263 | |
264 | // Check for end of stream. | |
265 | if (bytes == 0) | |
266 | { | |
267 | ec = boost::asio::error::eof; | |
268 | return true; | |
269 | } | |
270 | ||
20effc67 TL |
271 | // Check if operation succeeded. |
272 | if (bytes > 0) | |
273 | { | |
274 | bytes_transferred = bytes; | |
275 | return true; | |
276 | } | |
277 | ||
7c673cae FG |
278 | // Retry operation if interrupted by signal. |
279 | if (ec == boost::asio::error::interrupted) | |
280 | continue; | |
281 | ||
282 | // Check if we need to run the operation again. | |
283 | if (ec == boost::asio::error::would_block | |
284 | || ec == boost::asio::error::try_again) | |
285 | return false; | |
286 | ||
20effc67 TL |
287 | // Operation failed. |
288 | bytes_transferred = 0; | |
289 | return true; | |
290 | } | |
291 | } | |
292 | ||
293 | bool non_blocking_read1(int d, void* data, std::size_t size, | |
294 | boost::system::error_code& ec, std::size_t& bytes_transferred) | |
295 | { | |
296 | for (;;) | |
297 | { | |
298 | // Read some data. | |
299 | signed_size_type bytes = ::read(d, data, size); | |
300 | get_last_error(ec, bytes < 0); | |
301 | ||
302 | // Check for end of stream. | |
303 | if (bytes == 0) | |
304 | { | |
305 | ec = boost::asio::error::eof; | |
306 | return true; | |
307 | } | |
308 | ||
309 | // Check if operation succeeded. | |
7c673cae FG |
310 | if (bytes > 0) |
311 | { | |
7c673cae | 312 | bytes_transferred = bytes; |
20effc67 | 313 | return true; |
7c673cae | 314 | } |
7c673cae | 315 | |
20effc67 TL |
316 | // Retry operation if interrupted by signal. |
317 | if (ec == boost::asio::error::interrupted) | |
318 | continue; | |
319 | ||
320 | // Check if we need to run the operation again. | |
321 | if (ec == boost::asio::error::would_block | |
322 | || ec == boost::asio::error::try_again) | |
323 | return false; | |
324 | ||
325 | // Operation failed. | |
326 | bytes_transferred = 0; | |
7c673cae FG |
327 | return true; |
328 | } | |
329 | } | |
330 | ||
331 | std::size_t sync_write(int d, state_type state, const buf* bufs, | |
332 | std::size_t count, bool all_empty, boost::system::error_code& ec) | |
333 | { | |
334 | if (d == -1) | |
335 | { | |
336 | ec = boost::asio::error::bad_descriptor; | |
337 | return 0; | |
338 | } | |
339 | ||
340 | // A request to write 0 bytes on a stream is a no-op. | |
341 | if (all_empty) | |
342 | { | |
20effc67 TL |
343 | ec.assign(0, ec.category()); |
344 | return 0; | |
345 | } | |
346 | ||
347 | // Write some data. | |
348 | for (;;) | |
349 | { | |
350 | // Try to complete the operation without blocking. | |
351 | signed_size_type bytes = ::writev(d, bufs, static_cast<int>(count)); | |
352 | get_last_error(ec, bytes < 0); | |
353 | ||
354 | // Check if operation succeeded. | |
355 | if (bytes > 0) | |
356 | return bytes; | |
357 | ||
358 | // Operation failed. | |
359 | if ((state & user_set_non_blocking) | |
360 | || (ec != boost::asio::error::would_block | |
361 | && ec != boost::asio::error::try_again)) | |
362 | return 0; | |
363 | ||
364 | // Wait for descriptor to become ready. | |
365 | if (descriptor_ops::poll_write(d, 0, ec) < 0) | |
366 | return 0; | |
367 | } | |
368 | } | |
369 | ||
370 | std::size_t sync_write1(int d, state_type state, const void* data, | |
371 | std::size_t size, boost::system::error_code& ec) | |
372 | { | |
373 | if (d == -1) | |
374 | { | |
375 | ec = boost::asio::error::bad_descriptor; | |
376 | return 0; | |
377 | } | |
378 | ||
379 | // A request to write 0 bytes on a stream is a no-op. | |
380 | if (size == 0) | |
381 | { | |
382 | ec.assign(0, ec.category()); | |
7c673cae FG |
383 | return 0; |
384 | } | |
385 | ||
386 | // Write some data. | |
387 | for (;;) | |
388 | { | |
389 | // Try to complete the operation without blocking. | |
20effc67 TL |
390 | signed_size_type bytes = ::write(d, data, size); |
391 | get_last_error(ec, bytes < 0); | |
7c673cae FG |
392 | |
393 | // Check if operation succeeded. | |
394 | if (bytes > 0) | |
395 | return bytes; | |
396 | ||
397 | // Operation failed. | |
398 | if ((state & user_set_non_blocking) | |
399 | || (ec != boost::asio::error::would_block | |
400 | && ec != boost::asio::error::try_again)) | |
401 | return 0; | |
402 | ||
403 | // Wait for descriptor to become ready. | |
404 | if (descriptor_ops::poll_write(d, 0, ec) < 0) | |
405 | return 0; | |
406 | } | |
407 | } | |
408 | ||
409 | bool non_blocking_write(int d, const buf* bufs, std::size_t count, | |
410 | boost::system::error_code& ec, std::size_t& bytes_transferred) | |
411 | { | |
412 | for (;;) | |
413 | { | |
414 | // Write some data. | |
20effc67 TL |
415 | signed_size_type bytes = ::writev(d, bufs, static_cast<int>(count)); |
416 | get_last_error(ec, bytes < 0); | |
417 | ||
418 | // Check if operation succeeded. | |
419 | if (bytes >= 0) | |
420 | { | |
421 | bytes_transferred = bytes; | |
422 | return true; | |
423 | } | |
7c673cae FG |
424 | |
425 | // Retry operation if interrupted by signal. | |
426 | if (ec == boost::asio::error::interrupted) | |
427 | continue; | |
428 | ||
429 | // Check if we need to run the operation again. | |
430 | if (ec == boost::asio::error::would_block | |
431 | || ec == boost::asio::error::try_again) | |
432 | return false; | |
433 | ||
20effc67 TL |
434 | // Operation failed. |
435 | bytes_transferred = 0; | |
436 | return true; | |
437 | } | |
438 | } | |
439 | ||
440 | bool non_blocking_write1(int d, const void* data, std::size_t size, | |
441 | boost::system::error_code& ec, std::size_t& bytes_transferred) | |
442 | { | |
443 | for (;;) | |
444 | { | |
445 | // Write some data. | |
446 | signed_size_type bytes = ::write(d, data, size); | |
447 | get_last_error(ec, bytes < 0); | |
448 | ||
449 | // Check if operation succeeded. | |
7c673cae FG |
450 | if (bytes >= 0) |
451 | { | |
7c673cae | 452 | bytes_transferred = bytes; |
20effc67 | 453 | return true; |
7c673cae | 454 | } |
7c673cae | 455 | |
20effc67 TL |
456 | // Retry operation if interrupted by signal. |
457 | if (ec == boost::asio::error::interrupted) | |
458 | continue; | |
459 | ||
460 | // Check if we need to run the operation again. | |
461 | if (ec == boost::asio::error::would_block | |
462 | || ec == boost::asio::error::try_again) | |
463 | return false; | |
464 | ||
465 | // Operation failed. | |
466 | bytes_transferred = 0; | |
7c673cae FG |
467 | return true; |
468 | } | |
469 | } | |
470 | ||
471 | int ioctl(int d, state_type& state, long cmd, | |
472 | ioctl_arg_type* arg, boost::system::error_code& ec) | |
473 | { | |
474 | if (d == -1) | |
475 | { | |
476 | ec = boost::asio::error::bad_descriptor; | |
477 | return -1; | |
478 | } | |
479 | ||
20effc67 TL |
480 | int result = ::ioctl(d, cmd, arg); |
481 | get_last_error(ec, result < 0); | |
7c673cae FG |
482 | |
483 | if (result >= 0) | |
484 | { | |
7c673cae FG |
485 | // When updating the non-blocking mode we always perform the ioctl syscall, |
486 | // even if the flags would otherwise indicate that the descriptor is | |
487 | // already in the correct state. This ensures that the underlying | |
488 | // descriptor is put into the state that has been requested by the user. If | |
489 | // the ioctl syscall was successful then we need to update the flags to | |
490 | // match. | |
491 | if (cmd == static_cast<long>(FIONBIO)) | |
492 | { | |
493 | if (*arg) | |
494 | { | |
495 | state |= user_set_non_blocking; | |
496 | } | |
497 | else | |
498 | { | |
499 | // Clearing the non-blocking mode always overrides any internally-set | |
500 | // non-blocking flag. Any subsequent asynchronous operations will need | |
501 | // to re-enable non-blocking I/O. | |
502 | state &= ~(user_set_non_blocking | internal_non_blocking); | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | return result; | |
508 | } | |
509 | ||
510 | int fcntl(int d, int cmd, boost::system::error_code& ec) | |
511 | { | |
512 | if (d == -1) | |
513 | { | |
514 | ec = boost::asio::error::bad_descriptor; | |
515 | return -1; | |
516 | } | |
517 | ||
20effc67 TL |
518 | int result = ::fcntl(d, cmd); |
519 | get_last_error(ec, result < 0); | |
7c673cae FG |
520 | return result; |
521 | } | |
522 | ||
523 | int fcntl(int d, int cmd, long arg, boost::system::error_code& ec) | |
524 | { | |
525 | if (d == -1) | |
526 | { | |
527 | ec = boost::asio::error::bad_descriptor; | |
528 | return -1; | |
529 | } | |
530 | ||
20effc67 TL |
531 | int result = ::fcntl(d, cmd, arg); |
532 | get_last_error(ec, result < 0); | |
7c673cae FG |
533 | return result; |
534 | } | |
535 | ||
536 | int poll_read(int d, state_type state, boost::system::error_code& ec) | |
537 | { | |
538 | if (d == -1) | |
539 | { | |
540 | ec = boost::asio::error::bad_descriptor; | |
541 | return -1; | |
542 | } | |
543 | ||
544 | pollfd fds; | |
545 | fds.fd = d; | |
546 | fds.events = POLLIN; | |
547 | fds.revents = 0; | |
548 | int timeout = (state & user_set_non_blocking) ? 0 : -1; | |
20effc67 TL |
549 | int result = ::poll(&fds, 1, timeout); |
550 | get_last_error(ec, result < 0); | |
7c673cae | 551 | if (result == 0) |
20effc67 TL |
552 | if (state & user_set_non_blocking) |
553 | ec = boost::asio::error::would_block; | |
7c673cae FG |
554 | return result; |
555 | } | |
556 | ||
557 | int poll_write(int d, state_type state, boost::system::error_code& ec) | |
558 | { | |
559 | if (d == -1) | |
560 | { | |
561 | ec = boost::asio::error::bad_descriptor; | |
562 | return -1; | |
563 | } | |
564 | ||
565 | pollfd fds; | |
566 | fds.fd = d; | |
567 | fds.events = POLLOUT; | |
568 | fds.revents = 0; | |
569 | int timeout = (state & user_set_non_blocking) ? 0 : -1; | |
20effc67 TL |
570 | int result = ::poll(&fds, 1, timeout); |
571 | get_last_error(ec, result < 0); | |
7c673cae | 572 | if (result == 0) |
20effc67 TL |
573 | if (state & user_set_non_blocking) |
574 | ec = boost::asio::error::would_block; | |
7c673cae FG |
575 | return result; |
576 | } | |
577 | ||
b32b8144 FG |
578 | int poll_error(int d, state_type state, boost::system::error_code& ec) |
579 | { | |
580 | if (d == -1) | |
581 | { | |
582 | ec = boost::asio::error::bad_descriptor; | |
583 | return -1; | |
584 | } | |
585 | ||
586 | pollfd fds; | |
587 | fds.fd = d; | |
588 | fds.events = POLLPRI | POLLERR | POLLHUP; | |
589 | fds.revents = 0; | |
590 | int timeout = (state & user_set_non_blocking) ? 0 : -1; | |
20effc67 TL |
591 | int result = ::poll(&fds, 1, timeout); |
592 | get_last_error(ec, result < 0); | |
b32b8144 | 593 | if (result == 0) |
20effc67 TL |
594 | if (state & user_set_non_blocking) |
595 | ec = boost::asio::error::would_block; | |
b32b8144 FG |
596 | return result; |
597 | } | |
598 | ||
7c673cae FG |
599 | } // namespace descriptor_ops |
600 | } // namespace detail | |
601 | } // namespace asio | |
602 | } // namespace boost | |
603 | ||
604 | #include <boost/asio/detail/pop_options.hpp> | |
605 | ||
606 | #endif // !defined(BOOST_ASIO_WINDOWS) | |
607 | // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) | |
608 | // && !defined(__CYGWIN__) | |
609 | ||
610 | #endif // BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP |