Asio 1.11.0 standalone wrapper is not correct ... or is it me?

Given the following test program:

#include <asio.hpp> #include <cassert> int main() { asio::io_service ios1, ios2; asio::io_service::strand s2(ios2); auto test_func = wrap(s2, [&] { assert(s2.running_in_this_thread()); }); auto wrap_test_func = wrap(ios1, test_func); wrap_test_func(); ios1.run_one(); ios2.run_one(); } 

I understand that this program should not be argued.

wrap_test_func enclosed in io_service ios1 . The function that it wraps is wrapped in strand s2 (which uses ios2 ).

As I understand it, calling wrap_test_func should be equivalent to dispatch(ios1, test_func) , which then should send lambda to s2 ).

However, it seems that the shell has deployed the inner shell.

Is this the expected behavior?

+6
source share
1 answer

It turns out that it was my misunderstanding.

Here's a copy of Asio's answer:

Hi, Richard,

Yes, this behavior is intentional. However, in network TS and the last code on the main branch, this object was renamed from wrap (which meant adding a different level, as you expected) to bind_executor. This function simply fills the object with the associated executor; if he already has one, he is redefined.

If you need true packaging, you must explicitly wrap your internal handler on the external function object (or lambda) and have the external handler send () the internal handler to its "linked executor". In the form you write your own asynchronous operation, I propose to accept (documented in the network TS as part of the "requirements for asynchronous operations"):

  • Request the handler for the associated worker using get_associated_executor.

  • post () the handler of this executor if your operation completes immediately.

  • sends () a handler to this executor otherwise.

So (untested code, you may need the tip of the main branch):

  template<class Task, class Handler> void async_execute(implementation& impl, Task&& task, Handler&& handler) { ... auto ex = asio::get_associated_executor(handler get_io_context()); // this is immediate completion, so we use post() if (not impl) { post(ex, [handler = std::forward<Handler>(handler)]() mutable { promise_type promise; promise.set_exception(std::make_exception_ptr(system_error(errors::null_handle))); handler(promise.get_future()); }); return; } // this is not immediate completion, so we use dispatch() // (NOTE: assumes this->post_execute() does not run the task) // Optional. Really only needed if your io_context participates in the // async operation in some way not shown in this snippet, and not // simply as a channel for delivering the handler. auto io_work = make_work_guard(get_io_contet()); auto handler_work = make_work_guard(ex); auto impl_ptr = impl.get(); auto async_handler = [this, ex, impl_ptr, io_work, handler_work, handler = std::forward<Handler>(handler)] (detail::long_running_task_op::identifier ident, auto future) mutable { assert(impl_ptr); io_work.reset(); dispatch(ex, [handler = std::move(handler), future = std::move(future)]() mutable { handler(std::move(future)); }); assert(impl_ptr); impl_ptr->remove_op(ident); }; ... this->post_execute(); } 

Hope this helps.

Cheers Chris

+5
source

All Articles