Chris@16: // Chris@16: // detail/impl/descriptor_ops.ipp Chris@16: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP Chris@16: #define BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #if !defined(BOOST_ASIO_WINDOWS) \ Chris@16: && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \ Chris@16: && !defined(__CYGWIN__) Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace detail { Chris@16: namespace descriptor_ops { Chris@16: Chris@16: int open(const char* path, int flags, boost::system::error_code& ec) Chris@16: { Chris@16: errno = 0; Chris@16: int result = error_wrapper(::open(path, flags), ec); Chris@16: if (result >= 0) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: int close(int d, state_type& state, boost::system::error_code& ec) Chris@16: { Chris@16: int result = 0; Chris@16: if (d != -1) Chris@16: { Chris@16: errno = 0; Chris@16: result = error_wrapper(::close(d), ec); Chris@16: Chris@16: if (result != 0 Chris@16: && (ec == boost::asio::error::would_block Chris@16: || ec == boost::asio::error::try_again)) Chris@16: { Chris@16: // According to UNIX Network Programming Vol. 1, it is possible for Chris@16: // close() to fail with EWOULDBLOCK under certain circumstances. What Chris@16: // isn't clear is the state of the descriptor after this error. The one Chris@16: // current OS where this behaviour is seen, Windows, says that the socket Chris@16: // remains open. Therefore we'll put the descriptor back into blocking Chris@16: // mode and have another attempt at closing it. Chris@16: #if defined(__SYMBIAN32__) Chris@16: int flags = ::fcntl(d, F_GETFL, 0); Chris@16: if (flags >= 0) Chris@16: ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); Chris@16: #else // defined(__SYMBIAN32__) Chris@16: ioctl_arg_type arg = 0; Chris@16: ::ioctl(d, FIONBIO, &arg); Chris@16: #endif // defined(__SYMBIAN32__) Chris@16: state &= ~non_blocking; Chris@16: Chris@16: errno = 0; Chris@16: result = error_wrapper(::close(d), ec); Chris@16: } Chris@16: } Chris@16: Chris@16: if (result == 0) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: bool set_user_non_blocking(int d, state_type& state, Chris@16: bool value, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return false; Chris@16: } Chris@16: Chris@16: errno = 0; Chris@16: #if defined(__SYMBIAN32__) Chris@16: int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); Chris@16: if (result >= 0) Chris@16: { Chris@16: errno = 0; Chris@16: int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); Chris@16: result = error_wrapper(::fcntl(d, F_SETFL, flag), ec); Chris@16: } Chris@16: #else // defined(__SYMBIAN32__) Chris@16: ioctl_arg_type arg = (value ? 1 : 0); Chris@16: int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); Chris@16: #endif // defined(__SYMBIAN32__) Chris@16: Chris@16: if (result >= 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: if (value) Chris@16: state |= user_set_non_blocking; Chris@16: else Chris@16: { Chris@16: // Clearing the user-set non-blocking mode always overrides any Chris@16: // internally-set non-blocking flag. Any subsequent asynchronous Chris@16: // operations will need to re-enable non-blocking I/O. Chris@16: state &= ~(user_set_non_blocking | internal_non_blocking); Chris@16: } Chris@16: return true; Chris@16: } Chris@16: Chris@16: return false; Chris@16: } Chris@16: Chris@16: bool set_internal_non_blocking(int d, state_type& state, Chris@16: bool value, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return false; Chris@16: } Chris@16: Chris@16: if (!value && (state & user_set_non_blocking)) Chris@16: { Chris@16: // It does not make sense to clear the internal non-blocking flag if the Chris@16: // user still wants non-blocking behaviour. Return an error and let the Chris@16: // caller figure out whether to update the user-set non-blocking flag. Chris@16: ec = boost::asio::error::invalid_argument; Chris@16: return false; Chris@16: } Chris@16: Chris@16: errno = 0; Chris@16: #if defined(__SYMBIAN32__) Chris@16: int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); Chris@16: if (result >= 0) Chris@16: { Chris@16: errno = 0; Chris@16: int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); Chris@16: result = error_wrapper(::fcntl(d, F_SETFL, flag), ec); Chris@16: } Chris@16: #else // defined(__SYMBIAN32__) Chris@16: ioctl_arg_type arg = (value ? 1 : 0); Chris@16: int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); Chris@16: #endif // defined(__SYMBIAN32__) Chris@16: Chris@16: if (result >= 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: if (value) Chris@16: state |= internal_non_blocking; Chris@16: else Chris@16: state &= ~internal_non_blocking; Chris@16: return true; Chris@16: } Chris@16: Chris@16: return false; Chris@16: } Chris@16: Chris@16: std::size_t sync_read(int d, state_type state, buf* bufs, Chris@16: std::size_t count, bool all_empty, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // A request to read 0 bytes on a stream is a no-op. Chris@16: if (all_empty) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // Read some data. Chris@16: for (;;) Chris@16: { Chris@16: // Try to complete the operation without blocking. Chris@16: errno = 0; Chris@16: signed_size_type bytes = error_wrapper(::readv( Chris@16: d, bufs, static_cast(count)), ec); Chris@16: Chris@16: // Check if operation succeeded. Chris@16: if (bytes > 0) Chris@16: return bytes; Chris@16: Chris@16: // Check for EOF. Chris@16: if (bytes == 0) Chris@16: { Chris@16: ec = boost::asio::error::eof; Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // Operation failed. Chris@16: if ((state & user_set_non_blocking) Chris@16: || (ec != boost::asio::error::would_block Chris@16: && ec != boost::asio::error::try_again)) Chris@16: return 0; Chris@16: Chris@16: // Wait for descriptor to become ready. Chris@16: if (descriptor_ops::poll_read(d, 0, ec) < 0) Chris@16: return 0; Chris@16: } Chris@16: } Chris@16: Chris@16: bool non_blocking_read(int d, buf* bufs, std::size_t count, Chris@16: boost::system::error_code& ec, std::size_t& bytes_transferred) Chris@16: { Chris@16: for (;;) Chris@16: { Chris@16: // Read some data. Chris@16: errno = 0; Chris@16: signed_size_type bytes = error_wrapper(::readv( Chris@16: d, bufs, static_cast(count)), ec); Chris@16: Chris@16: // Check for end of stream. Chris@16: if (bytes == 0) Chris@16: { Chris@16: ec = boost::asio::error::eof; Chris@16: return true; Chris@16: } Chris@16: Chris@16: // Retry operation if interrupted by signal. Chris@16: if (ec == boost::asio::error::interrupted) Chris@16: continue; Chris@16: Chris@16: // Check if we need to run the operation again. Chris@16: if (ec == boost::asio::error::would_block Chris@16: || ec == boost::asio::error::try_again) Chris@16: return false; Chris@16: Chris@16: // Operation is complete. Chris@16: if (bytes > 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: bytes_transferred = bytes; Chris@16: } Chris@16: else Chris@16: bytes_transferred = 0; Chris@16: Chris@16: return true; Chris@16: } Chris@16: } Chris@16: Chris@16: std::size_t sync_write(int d, state_type state, const buf* bufs, Chris@16: std::size_t count, bool all_empty, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // A request to write 0 bytes on a stream is a no-op. Chris@16: if (all_empty) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return 0; Chris@16: } Chris@16: Chris@16: // Write some data. Chris@16: for (;;) Chris@16: { Chris@16: // Try to complete the operation without blocking. Chris@16: errno = 0; Chris@16: signed_size_type bytes = error_wrapper(::writev( Chris@16: d, bufs, static_cast(count)), ec); Chris@16: Chris@16: // Check if operation succeeded. Chris@16: if (bytes > 0) Chris@16: return bytes; Chris@16: Chris@16: // Operation failed. Chris@16: if ((state & user_set_non_blocking) Chris@16: || (ec != boost::asio::error::would_block Chris@16: && ec != boost::asio::error::try_again)) Chris@16: return 0; Chris@16: Chris@16: // Wait for descriptor to become ready. Chris@16: if (descriptor_ops::poll_write(d, 0, ec) < 0) Chris@16: return 0; Chris@16: } Chris@16: } Chris@16: Chris@16: bool non_blocking_write(int d, const buf* bufs, std::size_t count, Chris@16: boost::system::error_code& ec, std::size_t& bytes_transferred) Chris@16: { Chris@16: for (;;) Chris@16: { Chris@16: // Write some data. Chris@16: errno = 0; Chris@16: signed_size_type bytes = error_wrapper(::writev( Chris@16: d, bufs, static_cast(count)), ec); Chris@16: Chris@16: // Retry operation if interrupted by signal. Chris@16: if (ec == boost::asio::error::interrupted) Chris@16: continue; Chris@16: Chris@16: // Check if we need to run the operation again. Chris@16: if (ec == boost::asio::error::would_block Chris@16: || ec == boost::asio::error::try_again) Chris@16: return false; Chris@16: Chris@16: // Operation is complete. Chris@16: if (bytes >= 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: bytes_transferred = bytes; Chris@16: } Chris@16: else Chris@16: bytes_transferred = 0; Chris@16: Chris@16: return true; Chris@16: } Chris@16: } Chris@16: Chris@16: int ioctl(int d, state_type& state, long cmd, Chris@16: ioctl_arg_type* arg, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return -1; Chris@16: } Chris@16: Chris@16: errno = 0; Chris@16: int result = error_wrapper(::ioctl(d, cmd, arg), ec); Chris@16: Chris@16: if (result >= 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: Chris@16: // When updating the non-blocking mode we always perform the ioctl syscall, Chris@16: // even if the flags would otherwise indicate that the descriptor is Chris@16: // already in the correct state. This ensures that the underlying Chris@16: // descriptor is put into the state that has been requested by the user. If Chris@16: // the ioctl syscall was successful then we need to update the flags to Chris@16: // match. Chris@16: if (cmd == static_cast(FIONBIO)) Chris@16: { Chris@16: if (*arg) Chris@16: { Chris@16: state |= user_set_non_blocking; Chris@16: } Chris@16: else Chris@16: { Chris@16: // Clearing the non-blocking mode always overrides any internally-set Chris@16: // non-blocking flag. Any subsequent asynchronous operations will need Chris@16: // to re-enable non-blocking I/O. Chris@16: state &= ~(user_set_non_blocking | internal_non_blocking); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: return result; Chris@16: } Chris@16: Chris@16: int fcntl(int d, int cmd, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return -1; Chris@16: } Chris@16: Chris@16: errno = 0; Chris@16: int result = error_wrapper(::fcntl(d, cmd), ec); Chris@16: if (result != -1) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: int fcntl(int d, int cmd, long arg, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return -1; Chris@16: } Chris@16: Chris@16: errno = 0; Chris@16: int result = error_wrapper(::fcntl(d, cmd, arg), ec); Chris@16: if (result != -1) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: int poll_read(int d, state_type state, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return -1; Chris@16: } Chris@16: Chris@16: pollfd fds; Chris@16: fds.fd = d; Chris@16: fds.events = POLLIN; Chris@16: fds.revents = 0; Chris@16: int timeout = (state & user_set_non_blocking) ? 0 : -1; Chris@16: errno = 0; Chris@16: int result = error_wrapper(::poll(&fds, 1, timeout), ec); Chris@16: if (result == 0) Chris@16: ec = (state & user_set_non_blocking) Chris@16: ? boost::asio::error::would_block : boost::system::error_code(); Chris@16: else if (result > 0) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: int poll_write(int d, state_type state, boost::system::error_code& ec) Chris@16: { Chris@16: if (d == -1) Chris@16: { Chris@16: ec = boost::asio::error::bad_descriptor; Chris@16: return -1; Chris@16: } Chris@16: Chris@16: pollfd fds; Chris@16: fds.fd = d; Chris@16: fds.events = POLLOUT; Chris@16: fds.revents = 0; Chris@16: int timeout = (state & user_set_non_blocking) ? 0 : -1; Chris@16: errno = 0; Chris@16: int result = error_wrapper(::poll(&fds, 1, timeout), ec); Chris@16: if (result == 0) Chris@16: ec = (state & user_set_non_blocking) Chris@16: ? boost::asio::error::would_block : boost::system::error_code(); Chris@16: else if (result > 0) Chris@16: ec = boost::system::error_code(); Chris@16: return result; Chris@16: } Chris@16: Chris@16: } // namespace descriptor_ops Chris@16: } // namespace detail Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // !defined(BOOST_ASIO_WINDOWS) Chris@16: // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) Chris@16: // && !defined(__CYGWIN__) Chris@16: Chris@16: #endif // BOOST_ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP