Home > Blockchain >  AsyncWait in Lely CANopen is not behaving asynchronously?
AsyncWait in Lely CANopen is not behaving asynchronously?

Time:11-24

I'm trying to perform some tasks using fibers in the Lely CANopen stack. However, I either don't understand the model, or something is broken. What I would like to do is run multiple tasks at different rates.

An example based on the tutorial here:

class MyDriver : public canopen::FiberDriver {
 public:
  using FiberDriver::FiberDriver;

 private:
  // Configure device
  void OnConfig(std::function<void(std::error_code ec)> res) noexcept override {
    try {
      // Do some SDO configuration

      // Schedule tasks
      Defer(&MyDriver::TaskA, this);
      Defer(&MyDriver::TaskB, this);

      // Return no error
      res({});
    } catch (canopen::SdoError& e) {
      res(e.code());
    }
  }

  void TaskA() noexcept {
    while (true) {
      // Do some processing
      std::cout << "Hello from task A" << std::endl;

      // Wait
      Wait(AsyncWait(duration(std::chrono::milliseconds(1000))));
    }
  }

  void TaskB() noexcept {
    while (true) {
      // Do some processing
      std::cout << "Hello from task B" << std::endl;

      // Wait
      Wait(AsyncWait(duration(std::chrono::milliseconds(500))));
    }
  }

};

int main() {
  // A lot of set up omitted for clarity
  // Create context, event loop, CAN channel, etc.

  // Create master
  canopen::AsyncMaster master(timer, chan, "../config/master.dcf", "", 1);

  // Start NMT service by pretending to get a reset command
  master.Reset();

  // Create a driver for CAN device
  MyDriver driver(exec, master, 2);

  // Run the event loop
  loop.run();

  return 0;
}

I would expect this to print something like:

Hello from task A
Hello from task B
Hello from task B
Hello from task A
Hello from task B
Hello from task B
...

However, I get output like this:

Hello from task A
Hello from task A
Hello from task A
Hello from task A
...

So it seems that Wait(AsyncWait(d)) is not actually yielding execution. What am I doing wrong?

CodePudding user response:

(Answered here, but repeated below to make it easy to find.)

That's because you're scheduling the tasks with Defer() instead of Post().

Defer() submits a task to a "strand" executor. A strand guarantees that tasks submitted to it never run concurrently. So it will only execute task B once task A finishes. This can be useful in multithreaded applications (such as when using LoopDriver) to prevent race conditions without having to use mutexes or other explicit synchronization mechanisms.

Defer() is probably not the most obvious name for this. It originally comes from the executors in Boost.Asio and is meant to indicate that the submitted task will not run concurrently with the task that does the submitting (OnConfig() in your example). In this case, it means "defer task A until OnConfig() is complete and defer task B until task A is complete".

  • Related