Modify

Ticket #3900 (closed Feature Requests: fixed)

Opened 4 years ago

Last modified 11 months ago

Request for support of "release" member function or assign without ownership transfer to asio socket

Reported by: artyomtnk@… Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost 1.42.0 Severity: Problem
Keywords: Cc:

Description

boost::asio socket has assign member function allowing to transfer native socket to the asio socket. However, there is no option to release such handler or assign without ownership transfer.

Please add one of these options.

Rationale: null_buffer allow easy integration of 3rd part libraries to ASIO main loop for reactor style operations. However, today it is impossible to use it with 3rd part libraries that do not transfer on the socket ownership.

Let's assume I have 3rd part library that gives me a file descriptor to put it in select loop for asynchronous operations.

If I assign file descriptor to the socket, I would not be able to destroy socket object because it would close the connection. and I accidentally may close other valid socket.

   Thread A
   --------
   foo f=foo_open() // fd=4
   sock.assign(foo_native(f));
   ...
   foo_close(f);   // fd = 4 closed

   Thread B
   ---------
   some_fd = open() // some_fd = 4

   Thread A
   --------
   ~sock()    /// fd=4 closed!!!! thread B can't work

Can you please provide one (or both) of the following APIs:

   // Transfer socket ownership to user and reset socket object.
   native_type release(); 

   // Attach an existing native socket to the socket 
   // (assign do not transfer ownership)
   void attach(const protocol_type & protocol,
               const native_type & native_socket);

   boost::system::error_code attach(
               const protocol_type & protocol,
               const native_type & native_socket,
               boost::system::error_code & ec);

Attachments

Change History

comment:1 Changed 4 years ago by Maxim Yanchenko <Maxim.Yanchenko@…>

I second this. Currently I use dup() as a workaround, but having a separate function would be more straightforward.

comment:2 Changed 4 years ago by chris_kohlhoff

The reason I have not included a socket.release() function is that it gives an incorrect illusion of portability. On Windows, adding a socket to an io_service irreversibly binds the socket to the underlying I/O completion port. The lack of release() models that restriction.

A lot more thought is required to determine if there is a satisfactory design solution. I could, however, easily add a release() function to posix::stream_descriptor. Does that cover your use cases?

comment:3 Changed 4 years ago by chris_kohlhoff

  • Milestone changed from Boost 1.43.0 to To Be Determined

comment:4 Changed 4 years ago by Maxim Yanchenko <Maxim.Yanchenko@…>

In my particular case it's a third-party library that exposes its internal socket. I use Boost.Asio to read from this socket to a null buffer to determine if it's time to ask this library to read from the socket. The problem I have is double closing of the socket during shutdown - both library and asio try to close the socket. Currently I just dup the socket from the library before giving it to asio - but it's a workaround, I'd like to have something more straightforward, like a parameter for attach() like "don't close in dtor".

comment:5 Changed 4 years ago by chris_kohlhoff

If you only target non-Windows platforms then posix::stream_descriptor could instead be used to wrap the file descriptor. Is that feasible?

(I ask because adding release() to posix::stream_descriptor is doable.)

comment:6 Changed 4 years ago by Maxim Yanchenko <Maxim.Yanchenko@…>

I need to test this approach in my code, but even if it doesn't work for me I believe it's still worth having posix::stream_descriptor::release() anyway. Let's see what the original submitter thinks.

comment:7 follow-up: ↓ 8 Changed 4 years ago by anonymous

A lot more thought is required to determine if there is a satisfactory design solution. I could, however, easily add a release() function to posix::stream_descriptor. Does that cover your use cases?

Defiantly not. Because AFAIK posix::stream_descriptor covers only Unix domain sockets and I need general TCP and UDP sockets, (and Unix as well).

On Windows, adding a socket to an io_service irreversibly binds the socket to the underlying I/O completion port.

However, if you use BOOST_ASIO_DISABLE_IOCP it would not be a problem. So, maybe it is possible to make release() fail in case IOCP is used and socket can't be released like cancel() under XP and below.

2nd question, at least all known to me Unix APIs: epoll, kqueue, /dev/poll provide and option to deassign socket from the underlying file descriptor. Shouldn't be something like that in Windows for IOCP?

comment:8 in reply to: ↑ 7 Changed 4 years ago by chris_kohlhoff

Replying to anonymous:

Defiantly not. Because AFAIK posix::stream_descriptor covers only Unix domain sockets and I need general TCP and UDP sockets, (and Unix as well).

I think you're confusing it with asio::local::stream_protocol::socket. If you're using it with null_buffers, posix::stream_descriptor basically works with anything that is selectable. That means sockets (TCP, UDP, UNIX), pipes, and a lot more.

However, if you use BOOST_ASIO_DISABLE_IOCP it would not be a problem. So, maybe it is possible to make release() fail in case IOCP is used and socket can't be released like cancel() under XP and below.

As I said, there's still a portability issue. Adding release() isn't a satisfactory solution in my opinion. At least with cancel() we know that the OSes that don't support it are slowly fading away.

2nd question, at least all known to me Unix APIs: epoll, kqueue, /dev/poll provide and option to deassign socket from the underlying file descriptor. Shouldn't be something like that in Windows for IOCP?

There should be, but unfortunately there isn't.

comment:9 Changed 4 years ago by artyomtnk@…

I think you're confusing it with asio::local::stream_protocol::socket. If you're using it with null_buffers, posix::stream_descriptor basically works with anything that is selectable. That means sockets (TCP, UDP, UNIX), pipes, and a lot more.

If so, then yes, it would be helpful and for Windows I would just have to Duplicate socket before assigning it.

BTW: Maybe it is possible to provide assign member function I suggested before that would associate the socket but it would also tell not to close it in dtor and thus:

If system is POSIX or Windows with disable IOCP then, do not call close(), otherwise, when using IOCP, attach() would duplicate the socket befor calling to assign.

comment:10 Changed 3 years ago by chris_kohlhoff

(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:11 Changed 3 years ago by chris_kohlhoff

  • Status changed from new to closed
  • Resolution set to fixed

Applied to release branch in [72428]. (N.B. posix descriptors only.)

comment:12 Changed 3 years ago by anonymous

Another use case for this that presumably would be portable to windows as well is having a tcp::socket object, exchanging some messages over it, and then deciding to turn it into an SSL socket (I believe the SMTP protocol does things like this).

In this case, the operator=(basic_socket && s) is ideal, however, it would be nice to have this for current generation compilers too. maybe with a signature like:

basic_socket& operator=(basic_socket& s);

comment:13 Changed 12 months ago by countforall@…

  • Status changed from closed to reopened
  • Resolution fixed deleted

Hi,

I feel this problem is still existing and closed without any solution. Currently i am assign native socket after duplicating the original socket(Windows). And it has been working for last few months without any issues. But found socket issues in duplication in one of the customer machine. If i remove duplication and use the original socket, it is working fine.but i would lost the connection after boost socket destruction.

Please add a release function to remove the native socket from boost socket or add a flag to to tell the destructor not to close the native socket.

Thanks mahe

comment:14 Changed 11 months ago by chris_kohlhoff

  • Status changed from reopened to closed
  • Resolution set to fixed

Sounds like your customer has a fault Layered Service Provider installed.

I'm restoring this bug to its fixed state to record that it was delivered in a previous release.

View

Add a comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
The resolution will be deleted. Next status will be 'reopened'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.