From 111afa1f5859b15d6c46c6ae991c1f684a0262f3 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Sun, 7 Jul 2024 19:12:45 +1000 Subject: [PATCH 01/10] Don't call fcntl with F_SETFL if the flags aren't changing. --- asio/include/asio/detail/impl/descriptor_ops.ipp | 8 ++++---- asio/include/asio/detail/impl/socket_ops.ipp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/asio/include/asio/detail/impl/descriptor_ops.ipp b/asio/include/asio/detail/impl/descriptor_ops.ipp index c56a512e46..9954fe76bd 100644 --- a/asio/include/asio/detail/impl/descriptor_ops.ipp +++ b/asio/include/asio/detail/impl/descriptor_ops.ipp @@ -114,7 +114,7 @@ bool set_user_non_blocking(int d, state_type& state, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(d, F_SETFL, flag); + result = (flag != result) ? ::fcntl(d, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) @@ -136,7 +136,7 @@ bool set_user_non_blocking(int d, state_type& state, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(d, F_SETFL, flag); + result = (flag != result) ? ::fcntl(d, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } } @@ -184,7 +184,7 @@ bool set_internal_non_blocking(int d, state_type& state, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(d, F_SETFL, flag); + result = (flag != result) ? ::fcntl(d, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) @@ -206,7 +206,7 @@ bool set_internal_non_blocking(int d, state_type& state, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(d, F_SETFL, flag); + result = (flag != result) ? ::fcntl(d, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } } diff --git a/asio/include/asio/detail/impl/socket_ops.ipp b/asio/include/asio/detail/impl/socket_ops.ipp index 7feb7ca02d..13a3a8f233 100644 --- a/asio/include/asio/detail/impl/socket_ops.ipp +++ b/asio/include/asio/detail/impl/socket_ops.ipp @@ -393,7 +393,7 @@ bool set_user_non_blocking(socket_type s, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(s, F_SETFL, flag); + result = (flag != result) ? ::fcntl(s, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) @@ -415,7 +415,7 @@ bool set_user_non_blocking(socket_type s, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(s, F_SETFL, flag); + result = (flag != result) ? ::fcntl(s, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } } @@ -467,7 +467,7 @@ bool set_internal_non_blocking(socket_type s, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(s, F_SETFL, flag); + result = (flag != result) ? ::fcntl(s, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) @@ -489,7 +489,7 @@ bool set_internal_non_blocking(socket_type s, if (result >= 0) { int flag = (value ? (result | O_NONBLOCK) : (result & ~O_NONBLOCK)); - result = ::fcntl(s, F_SETFL, flag); + result = (flag != result) ? ::fcntl(s, F_SETFL, flag) : 0; get_last_error(ec, result < 0); } } From a37f1c424aca33c6862ab1f87f1538a8fc593967 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Sun, 7 Jul 2024 19:56:27 +1000 Subject: [PATCH 02/10] Don't use ioctl to modify blocking mode on assigned descriptors. --- .../asio/detail/impl/descriptor_ops.ipp | 61 ++++++++++--------- asio/include/asio/detail/impl/socket_ops.ipp | 61 ++++++++++--------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/asio/include/asio/detail/impl/descriptor_ops.ipp b/asio/include/asio/detail/impl/descriptor_ops.ipp index 9954fe76bd..b23453c4f6 100644 --- a/asio/include/asio/detail/impl/descriptor_ops.ipp +++ b/asio/include/asio/detail/impl/descriptor_ops.ipp @@ -69,25 +69,24 @@ int close(int d, state_type& state, asio::error_code& ec) ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = 0; -# if defined(ENOTTY) || defined(ENOTCAPABLE) - result = ::ioctl(d, FIONBIO, &arg); - get_last_error(ec, result < 0); - if (false -# if defined(ENOTTY) + if ((state & possible_dup) == 0) + { + result = ::ioctl(d, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { int flags = ::fcntl(d, F_GETFL, 0); if (flags >= 0) ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); } -# else // defined(ENOTTY) || defined(ENOTCAPABLE) - ::ioctl(d, FIONBIO, &arg); -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) state &= ~non_blocking; @@ -119,16 +118,19 @@ bool set_user_non_blocking(int d, state_type& state, } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = (value ? 1 : 0); - int result = ::ioctl(d, FIONBIO, &arg); - get_last_error(ec, result < 0); -# if defined(ENOTTY) || defined(ENOTCAPABLE) - if (false -# if defined(ENOTTY) + int result = 0; + if ((state & possible_dup) == 0) + { + result = ::ioctl(d, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { result = ::fcntl(d, F_GETFL, 0); @@ -140,7 +142,6 @@ bool set_user_non_blocking(int d, state_type& state, get_last_error(ec, result < 0); } } -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) if (result >= 0) @@ -189,16 +190,19 @@ bool set_internal_non_blocking(int d, state_type& state, } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = (value ? 1 : 0); - int result = ::ioctl(d, FIONBIO, &arg); - get_last_error(ec, result < 0); -# if defined(ENOTTY) || defined(ENOTCAPABLE) - if (false -# if defined(ENOTTY) + int result = 0; + if ((state & possible_dup) == 0) + { + result = ::ioctl(d, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { result = ::fcntl(d, F_GETFL, 0); @@ -210,7 +214,6 @@ bool set_internal_non_blocking(int d, state_type& state, get_last_error(ec, result < 0); } } -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) if (result >= 0) diff --git a/asio/include/asio/detail/impl/socket_ops.ipp b/asio/include/asio/detail/impl/socket_ops.ipp index 13a3a8f233..9f36942d86 100644 --- a/asio/include/asio/detail/impl/socket_ops.ipp +++ b/asio/include/asio/detail/impl/socket_ops.ipp @@ -339,25 +339,24 @@ int close(socket_type s, state_type& state, ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); # else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = 0; -# if defined(ENOTTY) || defined(ENOTCAPABLE) - result = ::ioctl(s, FIONBIO, &arg); - get_last_error(ec, result < 0); - if (false -# if defined(ENOTTY) + if ((state & possible_dup) == 0) + { + result = ::ioctl(s, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { int flags = ::fcntl(s, F_GETFL, 0); if (flags >= 0) ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); } -# else // defined(ENOTTY) || defined(ENOTCAPABLE) - ::ioctl(s, FIONBIO, &arg); -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) # endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) #endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__) state &= ~non_blocking; @@ -398,16 +397,19 @@ bool set_user_non_blocking(socket_type s, } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = (value ? 1 : 0); - int result = ::ioctl(s, FIONBIO, &arg); - get_last_error(ec, result < 0); -# if defined(ENOTTY) || defined(ENOTCAPABLE) - if (false -# if defined(ENOTTY) + int result = 0; + if ((state & possible_dup) == 0) + { + result = ::ioctl(s, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { result = ::fcntl(s, F_GETFL, 0); @@ -419,7 +421,6 @@ bool set_user_non_blocking(socket_type s, get_last_error(ec, result < 0); } } -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) if (result >= 0) @@ -472,16 +473,19 @@ bool set_internal_non_blocking(socket_type s, } #else // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) ioctl_arg_type arg = (value ? 1 : 0); - int result = ::ioctl(s, FIONBIO, &arg); - get_last_error(ec, result < 0); -# if defined(ENOTTY) || defined(ENOTCAPABLE) - if (false -# if defined(ENOTTY) + int result = 0; + if ((state & possible_dup) == 0) + { + result = ::ioctl(s, FIONBIO, &arg); + get_last_error(ec, result < 0); + } + if ((state & possible_dup) != 0 +# if defined(ENOTTY) || ec.value() == ENOTTY -# endif // defined(ENOTTY) -# if defined(ENOTCAPABLE) +# endif // defined(ENOTTY) +# if defined(ENOTCAPABLE) || ec.value() == ENOTCAPABLE -# endif // defined(ENOTCAPABLE) +# endif // defined(ENOTCAPABLE) ) { result = ::fcntl(s, F_GETFL, 0); @@ -493,7 +497,6 @@ bool set_internal_non_blocking(socket_type s, get_last_error(ec, result < 0); } } -# endif // defined(ENOTTY) || defined(ENOTCAPABLE) #endif // defined(__SYMBIAN32__) || defined(__EMSCRIPTEN__) if (result >= 0) From c586e957f164850ab0f6caa3ec0ce52ca035791d Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Sun, 7 Jul 2024 20:27:33 +1000 Subject: [PATCH 03/10] Don't set non-blocking mode for async_wait operations. --- .../impl/reactive_descriptor_service.ipp | 12 +++--- .../impl/reactive_socket_service_base.ipp | 10 +++-- .../detail/reactive_descriptor_service.hpp | 36 ++++++++-------- .../asio/detail/reactive_socket_service.hpp | 8 ++-- .../detail/reactive_socket_service_base.hpp | 41 +++++++++++-------- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/asio/include/asio/detail/impl/reactive_descriptor_service.ipp b/asio/include/asio/detail/impl/reactive_descriptor_service.ipp index 829aefb01b..b3a140042d 100644 --- a/asio/include/asio/detail/impl/reactive_descriptor_service.ipp +++ b/asio/include/asio/detail/impl/reactive_descriptor_service.ipp @@ -198,18 +198,20 @@ asio::error_code reactive_descriptor_service::cancel( } void reactive_descriptor_service::do_start_op(implementation_type& impl, - int op_type, reactor_op* op, bool is_continuation, bool is_non_blocking, - bool noop, void (*on_immediate)(operation* op, bool, const void*), + int op_type, reactor_op* op, bool is_continuation, + bool allow_speculative, bool noop, bool needs_non_blocking, + void (*on_immediate)(operation* op, bool, const void*), const void* immediate_arg) { if (!noop) { - if ((impl.state_ & descriptor_ops::non_blocking) || - descriptor_ops::set_internal_non_blocking( + if ((impl.state_ & descriptor_ops::non_blocking) + || !needs_non_blocking + || descriptor_ops::set_internal_non_blocking( impl.descriptor_, impl.state_, true, op->ec_)) { reactor_.start_op(op_type, impl.descriptor_, impl.reactor_data_, op, - is_continuation, is_non_blocking, on_immediate, immediate_arg); + is_continuation, allow_speculative, on_immediate, immediate_arg); return; } } diff --git a/asio/include/asio/detail/impl/reactive_socket_service_base.ipp b/asio/include/asio/detail/impl/reactive_socket_service_base.ipp index a02001b421..bbd27e0257 100644 --- a/asio/include/asio/detail/impl/reactive_socket_service_base.ipp +++ b/asio/include/asio/detail/impl/reactive_socket_service_base.ipp @@ -234,19 +234,21 @@ asio::error_code reactive_socket_service_base::do_assign( } void reactive_socket_service_base::do_start_op( - reactive_socket_service_base::base_implementation_type& impl, int op_type, - reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop, + reactive_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool allow_speculative, bool noop, bool needs_non_blocking, void (*on_immediate)(operation* op, bool, const void*), const void* immediate_arg) { if (!noop) { if ((impl.state_ & socket_ops::non_blocking) + || !needs_non_blocking || socket_ops::set_internal_non_blocking( impl.socket_, impl.state_, true, op->ec_)) { reactor_.start_op(op_type, impl.socket_, impl.reactor_data_, op, - is_continuation, is_non_blocking, on_immediate, immediate_arg); + is_continuation, allow_speculative, on_immediate, immediate_arg); return; } } @@ -263,7 +265,7 @@ void reactive_socket_service_base::do_start_accept_op( if (!peer_is_open) { do_start_op(impl, reactor::read_op, op, is_continuation, - true, false, on_immediate, immediate_arg); + true, false, true, on_immediate, immediate_arg); } else { diff --git a/asio/include/asio/detail/reactive_descriptor_service.hpp b/asio/include/asio/detail/reactive_descriptor_service.hpp index f7c7e86916..0a031786f1 100644 --- a/asio/include/asio/detail/reactive_descriptor_service.hpp +++ b/asio/include/asio/detail/reactive_descriptor_service.hpp @@ -239,7 +239,7 @@ class reactive_descriptor_service : default: p.p->ec_ = asio::error::invalid_argument; start_op(impl, reactor::read_op, p.p, - is_continuation, false, true, &io_ex, 0); + is_continuation, false, true, false, &io_ex, 0); p.v = p.p = 0; return; } @@ -252,7 +252,8 @@ class reactive_descriptor_service : &reactor_, &impl.reactor_data_, impl.descriptor_, op_type); } - start_op(impl, op_type, p.p, is_continuation, false, false, &io_ex, 0); + start_op(impl, op_type, p.p, is_continuation, + false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -326,7 +327,7 @@ class reactive_descriptor_service : start_op(impl, reactor::write_op, p.p, is_continuation, true, buffer_sequence_adapter::all_empty(buffers), &io_ex, 0); + ConstBufferSequence>::all_empty(buffers), true, &io_ex, 0); p.v = p.p = 0; } @@ -360,7 +361,7 @@ class reactive_descriptor_service : &impl, impl.descriptor_, "async_write_some(null_buffers)")); start_op(impl, reactor::write_op, p.p, - is_continuation, false, false, &io_ex, 0); + is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -435,7 +436,7 @@ class reactive_descriptor_service : start_op(impl, reactor::read_op, p.p, is_continuation, true, buffer_sequence_adapter::all_empty(buffers), &io_ex, 0); + MutableBufferSequence>::all_empty(buffers), true, &io_ex, 0); p.v = p.p = 0; } @@ -469,14 +470,15 @@ class reactive_descriptor_service : &impl, impl.descriptor_, "async_read_some(null_buffers)")); start_op(impl, reactor::read_op, p.p, - is_continuation, false, false, &io_ex, 0); + is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } private: // Start the asynchronous operation. - ASIO_DECL void do_start_op(implementation_type& impl, int op_type, - reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop, + ASIO_DECL void do_start_op(implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool allow_speculative, bool noop, bool needs_non_blocking, void (*on_immediate)(operation* op, bool, const void*), const void* immediate_arg); @@ -484,18 +486,19 @@ class reactive_descriptor_service : // immediate completion. template void start_op(implementation_type& impl, int op_type, Op* op, - bool is_continuation, bool is_non_blocking, bool noop, - const void* io_ex, ...) + bool is_continuation, bool allow_speculative, bool noop, + bool needs_non_blocking, const void* io_ex, ...) { - return do_start_op(impl, op_type, op, is_continuation, - is_non_blocking, noop, &Op::do_immediate, io_ex); + return do_start_op(impl, op_type, op, is_continuation, allow_speculative, + noop, needs_non_blocking, &Op::do_immediate, io_ex); } // Start the asynchronous operation for handlers that are not specialised for // immediate completion. template - void start_op(implementation_type& impl, int op_type, Op* op, - bool is_continuation, bool is_non_blocking, bool noop, const void*, + void start_op(implementation_type& impl, int op_type, + Op* op, bool is_continuation, bool allow_speculative, + bool noop, bool needs_non_blocking, const void*, enable_if_t< is_same< typename associated_immediate_executor< @@ -506,8 +509,9 @@ class reactive_descriptor_service : >::value >*) { - return do_start_op(impl, op_type, op, is_continuation, is_non_blocking, - noop, &reactor::call_post_immediate_completion, &reactor_); + return do_start_op(impl, op_type, op, is_continuation, + allow_speculative, noop, needs_non_blocking, + &reactor::call_post_immediate_completion, &reactor_); } // Helper class used to implement per-operation cancellation diff --git a/asio/include/asio/detail/reactive_socket_service.hpp b/asio/include/asio/detail/reactive_socket_service.hpp index c828e7f1f2..6b7963eb78 100644 --- a/asio/include/asio/detail/reactive_socket_service.hpp +++ b/asio/include/asio/detail/reactive_socket_service.hpp @@ -307,7 +307,7 @@ class reactive_socket_service : &impl, impl.socket_, "async_send_to")); start_op(impl, reactor::write_op, p.p, - is_continuation, true, false, &io_ex, 0); + is_continuation, true, false, true, &io_ex, 0); p.v = p.p = 0; } @@ -341,7 +341,7 @@ class reactive_socket_service : &impl, impl.socket_, "async_send_to(null_buffers)")); start_op(impl, reactor::write_op, p.p, - is_continuation, false, false, &io_ex, 0); + is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -432,7 +432,7 @@ class reactive_socket_service : start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - p.p, is_continuation, true, false, &io_ex, 0); + p.p, is_continuation, true, false, true, &io_ex, 0); p.v = p.p = 0; } @@ -471,7 +471,7 @@ class reactive_socket_service : start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - p.p, is_continuation, false, false, &io_ex, 0); + p.p, is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } diff --git a/asio/include/asio/detail/reactive_socket_service_base.hpp b/asio/include/asio/detail/reactive_socket_service_base.hpp index 63dcacad58..ab1f5fcf08 100644 --- a/asio/include/asio/detail/reactive_socket_service_base.hpp +++ b/asio/include/asio/detail/reactive_socket_service_base.hpp @@ -229,7 +229,7 @@ class reactive_socket_service_base default: p.p->ec_ = asio::error::invalid_argument; start_op(impl, reactor::read_op, p.p, - is_continuation, false, true, &io_ex, 0); + is_continuation, false, true, false, &io_ex, 0); p.v = p.p = 0; return; } @@ -242,7 +242,8 @@ class reactive_socket_service_base &reactor_, &impl.reactor_data_, impl.socket_, op_type); } - start_op(impl, op_type, p.p, is_continuation, false, false, &io_ex, 0); + start_op(impl, op_type, p.p, is_continuation, + false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -314,7 +315,7 @@ class reactive_socket_service_base start_op(impl, reactor::write_op, p.p, is_continuation, true, ((impl.state_ & socket_ops::stream_oriented) && buffer_sequence_adapter::all_empty(buffers)), &io_ex, 0); + ConstBufferSequence>::all_empty(buffers)), true, &io_ex, 0); p.v = p.p = 0; } @@ -347,7 +348,7 @@ class reactive_socket_service_base &impl, impl.socket_, "async_send(null_buffers)")); start_op(impl, reactor::write_op, p.p, - is_continuation, false, false, &io_ex, 0); + is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -424,7 +425,7 @@ class reactive_socket_service_base (flags & socket_base::message_out_of_band) == 0, ((impl.state_ & socket_ops::stream_oriented) && buffer_sequence_adapter::all_empty(buffers)), &io_ex, 0); + MutableBufferSequence>::all_empty(buffers)), true, &io_ex, 0); p.v = p.p = 0; } @@ -460,7 +461,7 @@ class reactive_socket_service_base start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - p.p, is_continuation, false, false, &io_ex, 0); + p.p, is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -532,7 +533,8 @@ class reactive_socket_service_base (in_flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, p.p, is_continuation, - (in_flags & socket_base::message_out_of_band) == 0, false, &io_ex, 0); + (in_flags & socket_base::message_out_of_band) == 0, + false, true, &io_ex, 0); p.v = p.p = 0; } @@ -573,7 +575,7 @@ class reactive_socket_service_base start_op(impl, (in_flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - p.p, is_continuation, false, false, &io_ex, 0); + p.p, is_continuation, false, false, false, &io_ex, 0); p.v = p.p = 0; } @@ -589,8 +591,9 @@ class reactive_socket_service_base const native_handle_type& native_socket, asio::error_code& ec); // Start the asynchronous read or write operation. - ASIO_DECL void do_start_op(base_implementation_type& impl, int op_type, - reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop, + ASIO_DECL void do_start_op(base_implementation_type& impl, + int op_type, reactor_op* op, bool is_continuation, + bool allow_speculative, bool noop, bool needs_non_blocking, void (*on_immediate)(operation* op, bool, const void*), const void* immediate_arg); @@ -598,18 +601,19 @@ class reactive_socket_service_base // immediate completion. template void start_op(base_implementation_type& impl, int op_type, Op* op, - bool is_continuation, bool is_non_blocking, bool noop, - const void* io_ex, ...) + bool is_continuation, bool allow_speculative, bool noop, + bool needs_non_blocking, const void* io_ex, ...) { - return do_start_op(impl, op_type, op, is_continuation, - is_non_blocking, noop, &Op::do_immediate, io_ex); + return do_start_op(impl, op_type, op, is_continuation, allow_speculative, + noop, needs_non_blocking, &Op::do_immediate, io_ex); } // Start the asynchronous operation for handlers that are not specialised for // immediate completion. template - void start_op(base_implementation_type& impl, int op_type, Op* op, - bool is_continuation, bool is_non_blocking, bool noop, const void*, + void start_op(base_implementation_type& impl, int op_type, + Op* op, bool is_continuation, bool allow_speculative, + bool noop, bool needs_non_blocking, const void*, enable_if_t< is_same< typename associated_immediate_executor< @@ -620,8 +624,9 @@ class reactive_socket_service_base >::value >*) { - return do_start_op(impl, op_type, op, is_continuation, is_non_blocking, - noop, &reactor::call_post_immediate_completion, &reactor_); + return do_start_op(impl, op_type, op, is_continuation, + allow_speculative, noop, needs_non_blocking, + &reactor::call_post_immediate_completion, &reactor_); } // Start the asynchronous accept operation. From 25b7372748be5924280266c58099b9fdf70a4602 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Sun, 7 Jul 2024 17:56:57 +1000 Subject: [PATCH 04/10] Move async_immediate to a public header. --- asio/include/Makefile.am | 1 + asio/include/asio.hpp | 1 + asio/include/asio/immediate.hpp | 142 ++++++++++++++++++ asio/src/Makefile.mgw | 1 + asio/src/Makefile.msc | 1 + asio/src/tests/Makefile.am | 3 + .../tests/unit/bind_immediate_executor.cpp | 25 +-- asio/src/tests/unit/immediate.cpp | 25 +++ 8 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 asio/include/asio/immediate.hpp create mode 100644 asio/src/tests/unit/immediate.cpp diff --git a/asio/include/Makefile.am b/asio/include/Makefile.am index 091abed6ab..3f282fa5c8 100644 --- a/asio/include/Makefile.am +++ b/asio/include/Makefile.am @@ -415,6 +415,7 @@ nobase_include_HEADERS = \ asio/handler_continuation_hook.hpp \ asio/high_resolution_timer.hpp \ asio.hpp \ + asio/immediate.hpp \ asio/impl/any_completion_executor.ipp \ asio/impl/any_io_executor.ipp \ asio/impl/append.hpp \ diff --git a/asio/include/asio.hpp b/asio/include/asio.hpp index 67018a575c..25cee47c31 100644 --- a/asio/include/asio.hpp +++ b/asio/include/asio.hpp @@ -105,6 +105,7 @@ #include "asio/generic/stream_protocol.hpp" #include "asio/handler_continuation_hook.hpp" #include "asio/high_resolution_timer.hpp" +#include "asio/immediate.hpp" #include "asio/io_context.hpp" #include "asio/io_context_strand.hpp" #include "asio/io_service.hpp" diff --git a/asio/include/asio/immediate.hpp b/asio/include/asio/immediate.hpp new file mode 100644 index 0000000000..4035ba53a6 --- /dev/null +++ b/asio/include/asio/immediate.hpp @@ -0,0 +1,142 @@ +// +// immediate.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMMEDIATE_HPP +#define ASIO_IMMEDIATE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/associated_immediate_executor.hpp" +#include "asio/async_result.hpp" +#include "asio/dispatch.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class initiate_immediate +{ +public: + typedef Executor executor_type; + + explicit initiate_immediate(const Executor& ex) + : ex_(ex) + { + } + + executor_type get_executor() const noexcept + { + return ex_; + } + + template + void operator()(CompletionHandler&& handler) const + { + typename associated_immediate_executor< + CompletionHandler, executor_type>::type ex = + (get_associated_immediate_executor)(handler, ex_); + (dispatch)(ex, static_cast(handler)); + } + +private: + Executor ex_; +}; + +} // namespace detail + +/// Launch a trivial asynchronous operation that completes immediately. +/** + * The async_immediate function is intended for use by composed operations, + * which can delegate to this operation in order to implement the correct + * semantics for immediate completion. + * + * @param ex The asynchronous operation's I/O executor. + * + * @param token The completion token. + * + * The completion handler is immediately submitted for execution by calling + * asio::dispatch() on the handler's associated immediate executor. + * + * If the completion handler does not have a customised associated immediate + * executor, then the handler is submitted as if by calling asio::post() + * on the supplied I/O executor. + * + * @par Completion Signature + * @code void() @endcode + */ +template > +inline auto async_immediate(const Executor& ex, + NullaryToken&& token = default_completion_token_t(), + constraint_t< + (execution::is_executor::value + && can_require::value) + || is_executor::value + > = 0) + -> decltype( + async_initiate( + declval>(), token)) +{ + return async_initiate( + detail::initiate_immediate(ex), token); +} + +/// Launch a trivial asynchronous operation that completes immediately. +/** + * The async_immediate function is intended for use by composed operations, + * which can delegate to this operation in order to implement the correct + * semantics for immediate completion. + * + * @param ex The execution context used to obtain the asynchronous operation's + * I/O executor. + * + * @param token The completion token. + * + * The completion handler is immediately submitted for execution by calling + * asio::dispatch() on the handler's associated immediate executor. + * + * If the completion handler does not have a customised associated immediate + * executor, then the handler is submitted as if by calling asio::post() + * on the I/O executor obtained from the supplied execution context. + * + * @par Completion Signature + * @code void() @endcode + */ +template > +inline auto async_immediate(ExecutionContext& ctx, + NullaryToken&& token = default_completion_token_t< + typename ExecutionContext::executor_type>(), + constraint_t< + is_convertible::value + > = 0) + -> decltype( + async_initiate( + declval>(), token)) +{ + return async_initiate( + detail::initiate_immediate< + typename ExecutionContext::executor_type>( + ctx.get_executor()), token); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMMEDIATE_HPP diff --git a/asio/src/Makefile.mgw b/asio/src/Makefile.mgw index ad418fde00..05d9e8420c 100644 --- a/asio/src/Makefile.mgw +++ b/asio/src/Makefile.mgw @@ -96,6 +96,7 @@ UNIT_TEST_EXES = \ tests/unit/generic/seq_packet_protocol.exe \ tests/unit/generic/stream_protocol.exe \ tests/unit/high_resolution_timer.exe \ + tests/unit/immediate.exe \ tests/unit/io_context.exe \ tests/unit/io_context_strand.exe \ tests/unit/ip/address.exe \ diff --git a/asio/src/Makefile.msc b/asio/src/Makefile.msc index 590895bc3d..51c1dd1dbb 100644 --- a/asio/src/Makefile.msc +++ b/asio/src/Makefile.msc @@ -190,6 +190,7 @@ UNIT_TEST_EXES = \ tests\unit\generic\seq_packet_protocol.exe \ tests\unit\generic\stream_protocol.exe \ tests\unit\high_resolution_timer.exe \ + tests\unit\immediate.exe \ tests\unit\io_context.exe \ tests\unit\io_context_strand.exe \ tests\unit\ip\address.exe \ diff --git a/asio/src/tests/Makefile.am b/asio/src/tests/Makefile.am index 4c5cee09fb..c051944e23 100644 --- a/asio/src/tests/Makefile.am +++ b/asio/src/tests/Makefile.am @@ -90,6 +90,7 @@ check_PROGRAMS = \ unit/generic/seq_packet_protocol \ unit/generic/stream_protocol \ unit/high_resolution_timer \ + unit/immediate \ unit/io_context \ unit/io_context_strand \ unit/ip/address \ @@ -304,6 +305,7 @@ TESTS = \ unit/executor_work_guard \ unit/file_base \ unit/high_resolution_timer \ + unit/immediate \ unit/io_context \ unit/io_context_strand \ unit/ip/address \ @@ -528,6 +530,7 @@ unit_generic_raw_protocol_SOURCES = unit/generic/raw_protocol.cpp unit_generic_seq_packet_protocol_SOURCES = unit/generic/seq_packet_protocol.cpp unit_generic_stream_protocol_SOURCES = unit/generic/stream_protocol.cpp unit_high_resolution_timer_SOURCES = unit/high_resolution_timer.cpp +unit_immediate_SOURCES = unit/immediate.cpp unit_io_context_SOURCES = unit/io_context.cpp unit_io_context_strand_SOURCES = unit/io_context_strand.cpp unit_ip_address_SOURCES = unit/ip/address.cpp diff --git a/asio/src/tests/unit/bind_immediate_executor.cpp b/asio/src/tests/unit/bind_immediate_executor.cpp index b1de3eba82..1a4b07d09a 100644 --- a/asio/src/tests/unit/bind_immediate_executor.cpp +++ b/asio/src/tests/unit/bind_immediate_executor.cpp @@ -17,34 +17,13 @@ #include "asio/bind_immediate_executor.hpp" #include -#include "asio/dispatch.hpp" +#include "asio/immediate.hpp" #include "asio/io_context.hpp" #include "unit_test.hpp" using namespace asio; namespace bindns = std; -struct initiate_immediate -{ - template - void operator()(Handler&& handler, io_context* ctx) const - { - typename associated_immediate_executor< - Handler, io_context::executor_type>::type ex = - get_associated_immediate_executor(handler, ctx->get_executor()); - dispatch(ex, static_cast(handler)); - } -}; - -template -ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(Token, void()) -async_immediate(io_context& ctx, Token&& token) - ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( - async_initiate(declval(), token, &ctx))) -{ - return async_initiate(initiate_immediate(), token, &ctx); -} - void increment(int* count) { ++(*count); @@ -70,7 +49,7 @@ void bind_immediate_executor_to_function_object_test() ASIO_CHECK(count == 1); - async_immediate(ioc1, + async_immediate(ioc1.get_executor(), bind_immediate_executor( ioc2.get_executor(), bind_immediate_executor( diff --git a/asio/src/tests/unit/immediate.cpp b/asio/src/tests/unit/immediate.cpp new file mode 100644 index 0000000000..0666898cf1 --- /dev/null +++ b/asio/src/tests/unit/immediate.cpp @@ -0,0 +1,25 @@ +// +// immediate.cpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/immediate.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "immediate", + ASIO_TEST_CASE(null_test) +) From 595983d164431026c5e364a65fb281b2c517951e Mon Sep 17 00:00:00 2001 From: Christian Eggers Date: Thu, 4 Jul 2024 13:28:05 +0200 Subject: [PATCH 05/10] Update operations/composed_6 exaple to also free delay_timer_. Also the delay_timer_ member should be freed before calling the user-supplied completion handler (as in the composed_7 and composed_8 examples). --- asio/src/examples/cpp11/operations/composed_6.cpp | 5 +++-- asio/src/examples/cpp14/operations/composed_6.cpp | 5 +++-- asio/src/examples/cpp20/operations/composed_6.cpp | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/asio/src/examples/cpp11/operations/composed_6.cpp b/asio/src/examples/cpp11/operations/composed_6.cpp index b948758d83..70b64502fb 100644 --- a/asio/src/examples/cpp11/operations/composed_6.cpp +++ b/asio/src/examples/cpp11/operations/composed_6.cpp @@ -138,9 +138,10 @@ struct async_write_message_initiation // We no longer have any future work coming for the I/O executor. io_work_.reset(); - // Deallocate the encoded message before calling the user-supplied - // completion handler. + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. encoded_message_.reset(); + delay_timer_.reset(); // Call the user-supplied handler with the result of the operation. handler_(error); diff --git a/asio/src/examples/cpp14/operations/composed_6.cpp b/asio/src/examples/cpp14/operations/composed_6.cpp index 82c0cd7223..b056272b76 100644 --- a/asio/src/examples/cpp14/operations/composed_6.cpp +++ b/asio/src/examples/cpp14/operations/composed_6.cpp @@ -156,9 +156,10 @@ auto async_write_messages(tcp::socket& socket, // We no longer have any future work coming for the I/O executor. io_work_.reset(); - // Deallocate the encoded message before calling the user-supplied - // completion handler. + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. encoded_message_.reset(); + delay_timer_.reset(); // Call the user-supplied handler with the result of the operation. handler_(error); diff --git a/asio/src/examples/cpp20/operations/composed_6.cpp b/asio/src/examples/cpp20/operations/composed_6.cpp index 77e2f50f93..da89886d55 100644 --- a/asio/src/examples/cpp20/operations/composed_6.cpp +++ b/asio/src/examples/cpp20/operations/composed_6.cpp @@ -161,9 +161,10 @@ auto async_write_messages(tcp::socket& socket, // We no longer have any future work coming for the I/O executor. io_work_.reset(); - // Deallocate the encoded message before calling the user-supplied - // completion handler. + // Deallocate the encoded message and delay timer before calling the + // user-supplied completion handler. encoded_message_.reset(); + delay_timer_.reset(); // Call the user-supplied handler with the result of the operation. handler_(error); From 84ecb55cfac71a1eaef0e541443f087de45c938d Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Mon, 8 Jul 2024 22:58:31 +1000 Subject: [PATCH 06/10] Fix read_until's regex support when ASIO_NO_DYNAMIC_BUFFER_V1 is defined. --- asio/include/asio/impl/read_until.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/asio/include/asio/impl/read_until.hpp b/asio/include/asio/impl/read_until.hpp index 08fe3fcd9b..354a2d2040 100644 --- a/asio/include/asio/impl/read_until.hpp +++ b/asio/include/asio/impl/read_until.hpp @@ -68,6 +68,19 @@ namespace detail } return std::make_pair(last1, false); } + +#if !defined(ASIO_NO_EXTENSIONS) +#if defined(ASIO_HAS_BOOST_REGEX) + struct regex_match_flags + { + template + operator T() const + { + return T::match_default | T::match_partial; + } + }; +#endif // !defined(ASIO_NO_EXTENSIONS) +#endif // defined(ASIO_HAS_BOOST_REGEX) } // namespace detail #if !defined(ASIO_NO_DYNAMIC_BUFFER_V1) @@ -231,19 +244,6 @@ std::size_t read_until(SyncReadStream& s, #if !defined(ASIO_NO_EXTENSIONS) #if defined(ASIO_HAS_BOOST_REGEX) -namespace detail { - -struct regex_match_flags -{ - template - operator T() const - { - return T::match_default | T::match_partial; - } -}; - -} // namespace detail - template inline std::size_t read_until(SyncReadStream& s, DynamicBuffer_v1&& buffers, const boost::basic_regex& expr, From 4928948a6e607664ee0ab5590544def762a118ad Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Tue, 9 Jul 2024 20:05:38 +1000 Subject: [PATCH 07/10] Enable SFINAE-based partial specialisation of associator<>. --- asio/include/asio/associator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asio/include/asio/associator.hpp b/asio/include/asio/associator.hpp index 7952fcc51b..fbc97694a6 100644 --- a/asio/include/asio/associator.hpp +++ b/asio/include/asio/associator.hpp @@ -23,7 +23,7 @@ namespace asio { /// Used to generically specialise associators for a type. template