Opened 13 years ago

Closed 11 years ago

#3307 closed Bugs (fixed)

Stream descriptor blocking state set on close

Reported by: prez@… Owned by: chris_kohlhoff
Milestone: Boost 1.40.0 Component: asio
Version: Boost Development Trunk Severity: Problem
Keywords: Cc:

Description

Say you have the following situation:

class A {

asio::posix::stream_descriptor stdin;

public:

A() : stdin(svc, dup(STDIN_FILENO)) {

stdin.async_read_some(..., &A::read);

} void read() {

do something stdin.async_read_some(..., &A::read);

}

}

A *obj;

void read() {

obj = new A;

}

void foo() {

asio::posix::stream_descriptor stdin(dup(STDIN_FILENO));

stdin.async_read_some(..., &read)

while (!obj)

sleep(1);

}

In this case, a stream descriptor is created from a dup(STDIN_FILENO) twice (assume there are technical reasons why the original stream_descriptor cannot be passed through). So the following happens:

stdin constructed stdin - data read async request (set non-blocking) stdin - data read A constructed (A::stdin constructed) A::stdin - data read async request (set non-blocking) stdin destroyed (set blocking) A::stdin - data read A::stdin - data read async request (BLOCKS!)

The problem here is that all dups of a file handle share blocking state, and three things are happening in the above.

  1. A::stdin is ASSUMED to be blocking when constructed (current blocking state is not checked). This is not a problem, but if I had tried to do a sync read/write, it would have been.
  2. Blocking state is maintained by an internal flag, so once the FD has been set non-blocking it does not try to set it non-blocking again. But worse, there is no way to 'override' this if you know for a fact that the descriptor has been set blocking out of ASIO's control. Even stream_descriptor::command with non_blocking_io just sets a flag, rather than doing an actual operation.
  3. For no apparent reason, a descriptor that ASIO knows was set non-blocking, gets set back to blocking in close() or destroy(). There is no apparent reason for this really, if the FD is about to be closed, it does not matter what it's blocking state is doing so.

I can work around this by manually calling ioctl on A::stdin.native() in the first invocation of A::read(), however this is a hack, and only required because of #2 above.

Change History (7)

comment:1 by chris_kohlhoff, 13 years ago

(In [60869]) Always call ioctl on underlying descriptor when modifying blocking mode. Refs #3307.

comment:2 by chris_kohlhoff, 13 years ago

(In [60924]) Merge from trunk.

........

r60869 | chris_kohlhoff | 2010-03-27 21:54:44 +1100 (Sat, 27 Mar 2010) | 2 lines

Always call ioctl on underlying descriptor when modifying blocking mode. Refs #3307.

........

r60882 | chris_kohlhoff | 2010-03-28 09:22:59 +1100 (Sun, 28 Mar 2010) | 12 lines

Change the resolver implementation to no longer require the typedefs InternetProtocol::resolver_query and InternetProtocol::resolver_iterator, as neither typedef is part of the documented InternetProtocol requirements.

The following typedefs are now marked as deprecated:

  • ip::icmp::resolver_query
  • ip::icmp::resolver_iterator
  • ip::tcp::resolver_query
  • ip::tcp::resolver_iterator
  • ip::udp::resolver_query
  • ip::udp::resolver_iterator

........

r60883 | chris_kohlhoff | 2010-03-28 10:04:56 +1100 (Sun, 28 Mar 2010) | 2 lines

Fix unused variable warnings.

........

r60921 | chris_kohlhoff | 2010-03-30 10:51:15 +1100 (Tue, 30 Mar 2010) | 2 lines

Document basic_resolver_query's constructor arguments.

........

r60922 | chris_kohlhoff | 2010-03-30 10:55:00 +1100 (Tue, 30 Mar 2010) | 3 lines

Work around an apparent doxygen bug to show template parameter lists on inherited member functions.

........

r60923 | chris_kohlhoff | 2010-03-30 10:57:25 +1100 (Tue, 30 Mar 2010) | 2 lines

Regenerate documentation.

........

comment:3 by Dean Michael Berris, 12 years ago

Chris, did you mean to close this issue too? Should we mark this as fixed?

comment:4 by chris_kohlhoff, 12 years ago

No, there are further API changes planned in relation to this issue.

comment:5 by chris_kohlhoff, 12 years ago

(In [69194]) Changes for asio version 1.5.0:

  • Added support for timeouts on socket iostreams, such as ip::tcp::iostream. A timeout is set by calling expires_at() or expires_from_now() to establish a deadline. Any socket operations which occur past the deadline will put the iostream into a bad state.
  • Added a new error() member function to socket iostreams, for retrieving the error code from the most recent system call.
  • Added a new basic_deadline_timer::cancel_one() function. This function lets you cancel a single waiting handler on a timer. Handlers are cancelled in FIFO order.
  • Added a new transfer_exactly() completion condition. This can be used to send or receive a specified number of bytes even if the total size of the buffer (or buffer sequence) is larger.
  • Added new free functions connect() and async_connect(). These operations try each endpoint in a list until the socket is successfully connected.
  • Extended the buffer_size() function so that it works for buffer sequences in addition to individual buffers.
  • Added a new buffer_copy() function that can be used to copy the raw bytes between individual buffers and buffer sequences.
  • Added new non-throwing overloads of read(), read_at(), write() and write_at() that do not require a completion condition.
  • Added friendlier compiler errors for when a completion handler does not meet the necessary type requirements. When C++0x is available (currently supported for g++ 4.5 or later, and MSVC 10), static_assert is also used to generate an informative error message. Checking may be disabled by defining BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS.
  • Made the is_loopback(), is_unspecified() and is_multicast() functions consistently available across the ip::address, ip::address_v4 and ip::address_v6 classes. Refs #3939.
  • Added new non_blocking() functions for managing the non-blocking behaviour of a socket or descriptor. The io_control() commands named non_blocking_io are now deprecated in favour of these new functions.
  • Added new native_non_blocking() functions for managing the non-blocking mode of the underlying socket or descriptor. These functions are intended to allow the encapsulation of arbitrary non-blocking system calls as asynchronous operations, in a way that is transparent to the user of the socket object. The functions have no effect on the behaviour of the synchronous operations of the socket or descriptor. Refs #3307.
  • Added the io_control() member function for socket acceptors. Refs #3297.
  • For consistency with the C++0x standard library, deprecated the native_type typedefs in favour of native_handle_type, and the native() member functions in favour of native_handle().
  • Added a release() member function to posix descriptors. This function releases ownership of the underlying native descriptor to the caller. Refs #3900.
  • Added support for sequenced packet sockets (SOCK_SEQPACKET).
  • Added a new io_service::stopped() function that can be used to determine whether the io_service has stopped (i.e. a reset() call is needed prior to any further calls to run(), run_one(), poll() or poll_one()).
  • Reduced the copying of handler function objects.
  • Added support for C++0x move construction to further reduce copying of handler objects. Move support is enabled when compiling in -std=c++0x mode on g++ 4.5 or higher, or when using MSVC10.
  • Removed the dependency on OS-provided macros for the well-known IPv4 and IPv6 addresses. This should eliminate the annoying "missing braces around initializer" warnings. Refs #3741.
  • Reduced the size of ip::basic_endpoint<> objects (such as ip::tcp::endpoint and ip::udp::endpoint).
  • Changed the reactor backends to assume that any descriptors or sockets added using assign() may have been dup()-ed, and so require explicit deregistration from the reactor. Refs #4971.
  • Changed the SSL error category to return error strings from the OpenSSL library.
  • Changed the separate compilation support such that, to use Asio's SSL capabilities, you should also include 'asio/ssl/impl/src.hpp in one source file in your program.
  • Removed the deprecated member functions named io_service(). The get_io_service() member functions should be used instead.
  • Removed the deprecated typedefs resolver_query and resolver_iterator from the ip::tcp, ip::udp and ip::icmp classes.
  • Fixed a compile error on some versions of g++ due to anonymous enums. Refs #4883.
  • Added an explicit cast to the FIONBIO constant to int to suppress a compiler warning on some platforms. Refs #5128.
  • Fixed warnings reported by g++'s -Wshadow compiler option. Refs #3905.

comment:6 by chris_kohlhoff, 12 years ago

(In [69467]) * Add support for the fork() system call. Programs that use fork must call

io_service.notify_fork() at the appropriate times. Two new examples have been added showing how to use this feature. Refs #3238, #4162.

  • 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. Refs #3307.
  • EV_ONESHOT seems to cause problems on some versions of Mac OS X, with the io_service destructor getting stuck inside the close() system call. Use EV_CLEAR instead. Refs #5021.
  • Include function name in exception what() messages.
  • Fix insufficient initialisers warning with MinGW.
  • Make the shutdown_service() member functions private.
  • Add archetypes for testing socket option functions.
  • Add missing lock in signal_set_service::cancel().
  • The signal header needs to be included in signal_set_service.hpp so that we can use constants like NSIG and SIGRTMAX.
  • Don't use Boost.Thread's convenience header. Use the header file that is specifically for the boost::thread class instead.

comment:7 by chris_kohlhoff, 11 years ago

Resolution: fixed
Status: newclosed

Merged to release branch in [72428].

Note: See TracTickets for help on using tickets.