Clean up the handling of errors reported by the close() system call. In

particular, assume that most operating systems won't have close() fail
with EWOULDBLOCK, but if it does then set blocking mode and restart the
call. If any other error occurs we assume the descriptor is closed.
This commit is contained in:
Christopher Kohlhoff 2011-02-28 10:52:17 +11:00
parent a75fc27d25
commit 5c758bb025
9 changed files with 79 additions and 38 deletions

View File

@ -258,7 +258,8 @@ public:
* or connect operations will be cancelled immediately, and will complete
* with the asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure.
* @throws asio::system_error Thrown on failure. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*
* @note For portable behaviour with respect to graceful closure of a
* connected socket, call shutdown() before closing the socket.
@ -276,7 +277,8 @@ public:
* or connect operations will be cancelled immediately, and will complete
* with the asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any.
* @param ec Set to indicate what error occurred, if any. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*
* @par Example
* @code

View File

@ -42,8 +42,19 @@ int close(int d, state_type& state, asio::error_code& ec)
int result = 0;
if (d != -1)
{
if (state & internal_non_blocking)
errno = 0;
result = error_wrapper(::close(d), ec);
if (result != 0
&& (ec == asio::error::would_block
|| ec == asio::error::try_again))
{
// According to UNIX Network Programming Vol. 1, it is possible for
// close() to fail with EWOULDBLOCK under certain circumstances. What
// isn't clear is the state of the descriptor after this error. The one
// current OS where this behaviour is seen, Windows, says that the socket
// remains open. Therefore we'll put the descriptor back into blocking
// mode and have another attempt at closing it.
#if defined(__SYMBIAN32__)
int flags = ::fcntl(d, F_GETFL, 0);
if (flags >= 0)
@ -52,11 +63,11 @@ int close(int d, state_type& state, asio::error_code& ec)
ioctl_arg_type arg = 0;
::ioctl(d, FIONBIO, &arg);
#endif // defined(__SYMBIAN32__)
state &= ~internal_non_blocking;
}
state &= ~non_blocking;
errno = 0;
result = error_wrapper(::close(d), ec);
errno = 0;
result = error_wrapper(::close(d), ec);
}
}
if (result == 0)

View File

@ -96,8 +96,15 @@ asio::error_code reactive_descriptor_service::close(
(impl.state_ & descriptor_ops::possible_dup) == 0);
}
if (descriptor_ops::close(impl.descriptor_, impl.state_, ec) == 0)
construct(impl);
descriptor_ops::close(impl.descriptor_, impl.state_, ec);
// The descriptor is closed by the OS even if close() returns an error.
//
// (Actually, POSIX says the state of the descriptor is unspecified. On
// Linux the descriptor is apparently closed anyway; e.g. see
// http://lkml.org/lkml/2005/9/10/129
// We'll just have to assume that other OSes follow the same behaviour.)
construct(impl);
return ec;
}

View File

@ -71,8 +71,17 @@ asio::error_code reactive_socket_service_base::close(
(impl.state_ & socket_ops::possible_dup) == 0);
}
if (socket_ops::close(impl.socket_, impl.state_, true, ec) == 0)
construct(impl);
socket_ops::close(impl.socket_, impl.state_, false, ec);
// The descriptor is closed by the OS even if close() returns an error.
//
// (Actually, POSIX says the state of the descriptor is unspecified. On
// Linux the descriptor is apparently closed anyway; e.g. see
// http://lkml.org/lkml/2005/9/10/129
// We'll just have to assume that other OSes follow the same behaviour. The
// known exception is when Windows's closesocket() function fails with
// WSAEWOULDBLOCK, but this case is handled inside socket_ops::close().
construct(impl);
return ec;
}

View File

@ -277,28 +277,9 @@ int close(socket_type s, state_type& state,
int result = 0;
if (s != invalid_socket)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if ((state & non_blocking) && (state & user_set_linger))
{
ioctl_arg_type arg = 0;
::ioctlsocket(s, FIONBIO, &arg);
state &= ~non_blocking;
}
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if (state & non_blocking)
{
#if defined(__SYMBIAN32__)
int flags = ::fcntl(s, F_GETFL, 0);
if (flags >= 0)
::fcntl(s, F_SETFL, flags & ~O_NONBLOCK);
#else // defined(__SYMBIAN32__)
ioctl_arg_type arg = 0;
::ioctl(s, FIONBIO, &arg);
#endif // defined(__SYMBIAN32__)
state &= ~non_blocking;
}
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
// We don't want the destructor to block, so set the socket to linger in
// the background. If the user doesn't like this behaviour then they need
// to explicitly close the socket.
if (destruction && (state & user_set_linger))
{
::linger opt;
@ -315,6 +296,32 @@ int close(socket_type s, state_type& state,
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
result = error_wrapper(::close(s), ec);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
if (result != 0
&& (ec == asio::error::would_block
|| ec == asio::error::try_again))
{
// According to UNIX Network Programming Vol. 1, it is possible for
// close() to fail with EWOULDBLOCK under certain circumstances. What
// isn't clear is the state of the descriptor after this error. The one
// current OS where this behaviour is seen, Windows, says that the socket
// remains open. Therefore we'll put the descriptor back into blocking
// mode and have another attempt at closing it.
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
ioctl_arg_type arg = 0;
::ioctlsocket(s, FIONBIO, &arg);
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
# if defined(__SYMBIAN32__)
int flags = ::fcntl(s, F_GETFL, 0);
if (flags >= 0)
::fcntl(s, F_SETFL, flags & ~O_NONBLOCK);
# else // defined(__SYMBIAN32__)
ioctl_arg_type arg = 0;
::ioctl(s, FIONBIO, &arg);
# endif // defined(__SYMBIAN32__)
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
state &= ~non_blocking;
}
}
if (result == 0)

View File

@ -159,7 +159,8 @@ public:
* write operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @throws asio::system_error Thrown on failure.
* @throws asio::system_error Thrown on failure. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*/
void close()
{
@ -174,7 +175,8 @@ public:
* write operations will be cancelled immediately, and will complete with the
* asio::error::operation_aborted error.
*
* @param ec Set to indicate what error occurred, if any.
* @param ec Set to indicate what error occurred, if any. Note that, even if
* the function indicates an error, the underlying descriptor is closed.
*/
asio::error_code close(asio::error_code& ec)
{

View File

@ -110,7 +110,8 @@ public:
void stop()
{
stopped_ = true;
socket_.close();
asio::error_code ignored_ec;
socket_.close(ignored_ec);
deadline_.cancel();
heartbeat_timer_.cancel();
}

View File

@ -179,7 +179,8 @@ private:
// The deadline has passed. The socket is closed so that any outstanding
// asynchronous operations are cancelled. This allows the blocked
// connect(), read_line() or write_line() functions to return.
socket_.close();
asio::error_code ignored_ec;
socket_.close(ignored_ec);
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.

View File

@ -177,7 +177,8 @@ private:
{
channel_.leave(shared_from_this());
socket_.close();
asio::error_code ignored_ec;
socket_.close(ignored_ec);
input_deadline_.cancel();
non_empty_output_queue_.cancel();
output_deadline_.cancel();