1

In a thread which has not ever called and would never call asio::io_context.run(), must I invoke post() or dispatch() to dispatch tasks to the thread which has called asio::io_context.run()?

Is it safe to directly call asio::async_write() or asio::async_read() in a thread which has not ever called and would never call asio::io_context.run() to dispatch tasks to the thread which has called asio::io_context.run()?

sehe
  • 374,641
  • 47
  • 450
  • 633
John
  • 2,963
  • 11
  • 33
  • All of that is safe. –  Sep 16 '21 at 04:57
  • @Frank If so, why `post()` and `dispatch()` exist? I think `post()` and `dispatch()` are useless if *all of that are safe*. – John Sep 16 '21 at 06:04
  • `post()` and `dispatch()` are there for you to queue up arbitrary tasks to be executed via the context's run loop as soon as possible. Wheras `async_read()` will effectively call `post()/dispatch()` once the data is available. They do completely different things. –  Sep 16 '21 at 06:06
  • @Frank "async_read() will effectively call `post()/dispatch()` once the data is available." ***Reply:*** Could you please explain that in more detail for me? I am really confused. I think `async_read_some()\async_read_until()` would pass reading task by `post()/dispath()` to the thread which has called `io_context.run()` when `async_read_some()\async_read_until()` is called. – John Sep 16 '21 at 11:04
  • 1
    Where and how the reading happens is an implementation detail, and will be different from one platform to the next. What's *guaranteed* to be done in the run loop is the execution of the callback passed to async_read(), which only gets posted after the read is done. –  Sep 16 '21 at 11:19

1 Answers1

1

In a thread which has not ever called and would never call asio::io_context.run(), must I invoke post() or dispatch() to dispatch tasks to the thread which has called asio::io_context.run()?

That's basically how that works. Yes, it's also a "cheap" way to implement a task queue (see e.g. stackoverflow.com/questions/…) To the first part: yes. (You can replace "must" with "can")

Is it safe to directly call asio::async_write() or asio::async_read() in a thread which has not ever called and would never call asio::io_context.run() to dispatch tasks to the thread which has called asio::io_context.run()?

Yes (with caveats).

The caveats are that you're responsible for thread safety/synchronization. E.g. a tcp::socket object is not thread-safe. You should only call methods on it from one logical thread (e.g. strand) or critical section (e.g. using mutual exclusions, mutex).

The async initiators will work to get work onto the execution context (which .run()s on any number of threads). From there it is highly idiomatic that all subsequent async initiation functions happen from completion handlers, so from these threads already.

Note that none of this is magical. In fact, all the async_ initiation functions know the executor (associated with the IO object, usually) and this determines where the completion handler gets post/dispatch/defer-ed to. In some cases you want to override this (e.g. using strand.wrap() or the newer bind_executor() function).

See also When must you pass io_context to boost::asio::spawn? (C++)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • ***Which thread does the reading/writing operations?*** As per [this document](https://stackoverflow.com/questions/15568100/confused-when-boostasioio-service-run-method-blocks-unblocks), which says that : when `socket` receives data, bytes will be read and copied into `buffer`. The actual work will be done in either: 1.The initiating function, if Boost.Asio can determine that it will not block. 2.When the application explicitly run the `io_service`. ***So I think*** it's the thread which has invoked `io_context.run ()` actually does reading/writing operations. (see next comment). – John Sep 23 '21 at 01:25
  • As per the comment of *Frank*, he thinks where and how the reading happens is an implementation detail, and will be different from one platform to the next. How do you think about it? – John Sep 23 '21 at 01:26
  • @John Re _"So I think it's the thread which has invoked io_context.run ()"_ - of course, that's a given, but it tells me nothing: how many threads are running it? Is there any external synchronization? Frank is right in that it's likely often the kernel doing the actual reading/writing, but that's asking the wrong thing. Your question wasn't about that. The question was whether the `async_*` initiation needs to be posted, and the answer is: it depend! The usual rules for thread-safety apply and only you know how many other threads maybe accessing the shared object (e.g. your socket or timer). – sehe Sep 23 '21 at 08:26