chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.91k stars 1.21k forks source link

Make boost::asio::dispatch() decide running vs scheduling based also on the current callstack usage #675

Open ghost opened 3 years ago

ghost commented 3 years ago

@traceon commented on Feb 25, 2019, 12:44 AM UTC:

There can be recursion when using dispatch() call, which will lead to stack overflows, if scheduler always decides to run the handler instead of scheduling it.

Is this a known issue, or is there any solution/workaround for this? (Or this is intentionally left for the user to care about?)

The io_context guarantees that the handler will only be called in a thread in which
the run(), run_one(), poll() or poll_one() member functions is currently being invoked.
The handler may be executed inside this function if the guarantee can be met. 

...I am asking this, because the doc says it may be executed, but not will be executed if the guarantee can be met, assuming the executor is given a right to still postpone the call.

A couple of possible workarounds that come to my mind, if I were going to implement my own Executor, with blackjack and hookers:

This issue was moved by chriskohlhoff from boostorg/asio#207.

ghost commented 3 years ago

@youngwolf-project commented on Feb 25, 2019, 2:11 PM UTC:

I met this behavior before, and I feel it's my fault, which means I believe it's by design. But I agree that your solutions are better, I also considered to limit the call depth by my own codes.

ghost commented 3 years ago

@djarek commented on Feb 27, 2019, 11:56 PM UTC:

You should only call dispatch() if you know you won't end up with (potentially) unbounded recursion. In the context of asynchronous operations, dispatch() is only allowed in a continuation.

ghost commented 3 years ago

@traceon commented on Feb 28, 2019, 11:19 AM UTC:

In the context of asynchronous operations, dispatch() is only allowed in a continuation.

Is it mentioned anywhere in the docs?

ghost commented 3 years ago

@djarek commented on Mar 1, 2019, 12:44 PM UTC:

Here:

If an asynchonous operation completes immediately (that is, within the thread of execution calling the initiating function, and before the initiating function returns), the completion handler shall be submitted for execution as if by performing ex2.post(std::move(f), alloc2). Otherwise, the completion handler shall be submitted for execution as if by performing ex2.dispatch(std::move(f), alloc2).

ghost commented 3 years ago

@traceon commented on Mar 1, 2019, 5:43 PM UTC:

djarek thank you!

However, it doesn't look like any of dispatch() calls within the library itself is obeying that rule (especially in tests), so another question arises: what are examples of those asynchonous operations for which I would be allowed to call dispatch()?

From the wording above, any use of dispatch() is not legitimate unless it is performed as a tail call (which you can't guarantee in C++.)

Am I missing something?

ghost commented 3 years ago

@djarek commented on Mar 1, 2019, 6:10 PM UTC:

dispatch() is never invoked outside of continuations in ASIO, at least not in asynchronous operations (obviously, executor tests can call them in any context). Note that tail-call is not the same as a continuation, in simple terms a continuation is execution of a function in a (potentially different) thread of execution, after the original thread of execution called init.result.get() in the initiation function.

ghost commented 3 years ago

@traceon commented on Mar 1, 2019, 7:39 PM UTC:

Then, why asio::dispatch() doesn't consult asio_handler_is_continuation() when deciding if it is going to run or schedule?

ghost commented 3 years ago

@djarek commented on Mar 1, 2019, 10:00 PM UTC:

Because asio_handler_is_continuation is not a part of Networking TS and Executors have a different way of being informed "this is a continuation". If dispatch() checked asio_handler_is_continuation it would be a pessimization for types that don't define asio_handler_is_continuation, e.g. closure types produced by lambda expressions.

ghost commented 3 years ago

@traceon commented on Mar 3, 2019, 10:24 PM UTC:

I see. But what if you "submitting" a continuation from a loop of the function (i.e., effectively, you want the scheduler to dispatch the next iteration, without exiting the current iteration)? Looks like the same problem will happen here?