c++boostsynchronizationasio

Task prioritization when yielding from spawn method running on strand executor


I'd like to analyze the ordering of boost.asio calls using strand.

Consider the following scenario. First, a coroutine that uses the strand is called by boost::asio::spawn (task 1), it's running exclusively since I use strand until it gets suspended cooperatively when instruction pointer reach ***, and the scheduler that runs on the thread executes the next handler (task 2).

Task 2 uses the same strand but it's called by boost::asio::post. this call guarantee that this task get exclusiveness until finished (no yields).

So I expect that in this scenario, the remaining of task 1 won't be executed at least until task 2 finished. is my assumption correct ?

Furthermore, what if while task 2 being executed, more tasks are called on the same strand, does task 1 get priority when task 2 terminates, or there's no guarantee which task is going to take the strand (either task 2 that just return from yield or newer tasks that called)

//task1
boost::asio::spawn(strand_, [=](const auto& yield) {
  ...
  ..
  .
  auto response = sendRequest(request, yield);   //(***)
  ...
  ..
  .
}

//task 2 
boost::asio::post(strand_, [&]() {
  ... 
  ..
  .
}

//task 3
boost::asio::post(strand_, [&]() {
  ... 
  ..
  .
}


Solution

  • So I expect that in this scenario, the remaining of task 1 won't be executed at least until task 2 finished. is my assumption correct ?

    Yes

    Furthermore, what if while task 2 being executed, more tasks are called on the same strand, does task 1 get priority when task 2 terminates, or there's no guarantee which task is going to take the strand (either task 2 that just return from yield or newer tasks that called)

    If by "tasks are called" you mean "handlers are posted" (or equivalently: "async operations are initiated"), yes. All the tasks on a strand are executed in order they are posted.

    This is documented here: Order Of Handler Invocation:

    Order of handler invocation

    Given: a strand object s

    • an object a meeting completion handler requirements
    • an object a1 which is an arbitrary copy of a made by the implementation
    • an object b meeting completion handler requirements
    • an object b1 which is an arbitrary copy of b made by the implementation

    if any of the following conditions are true:

    • s.post(a) happens-before s.post(b)
    • s.post(a) happens-before s.dispatch(b), where the latter is performed outside the strand
    • s.dispatch(a) happens-before s.post(b), where the former is performed outside the strand
    • s.dispatch(a) happens-before s.dispatch(b), where both are performed outside the strand

    then asio_handler_invoke(a1, &a1) happens-before asio_handler_invoke(b1, &b1).

    In your case the happens-before is present (because you know that task1's continuation MUST have been posted before task2 could be entered).