Which thread async operations take place - c++

Afte reading the asio's documentation, it's clear to me that the completion handlers are called by one of the threads that called the io_service's io.run() method. However, something that's is not clear to me is which thread the read/write async methods take place. Is it the thread that I call the methods or is it in one of the threads that called the io.run() method? Or, in last case, does the library create another thread behind the scenes and performs the operation?

The I/O operation will be attempted within the initiating async_* function. If either the operation's completion condition is satisfied or an error occurs, then the operation is complete and the completion handler will be posted into the io_service. Otherwise, the operation is not complete, and it will be enqueued into the io_service, where an application thread running the io_service's function poll(), poll_one(), run(), or run_one() performs the underlying I/O operation. In both cases, the completion handler is invoked by a thread processing the io_service.
The async_write() documentation notes that the asynchronous operation may be completed immediately:
Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using boost::asio::io_service::post().
This behavior is also noted in the Requirements on Asynchronous Operations documentation:
When an asynchronous operation is complete, the handler for the operation will be invoked as if by:
Constructing a bound completion handler bch for the handler ...
Calling ios.post(bch) to schedule the handler for deferred invocation ...
This implies that the handler must not be called directly from within the initiating function, even if the asynchronous operation completes immediately.
Here is a complete example demonstrating this behavior. In it, socket1 and socket2 are connected. Initially, socket2 has no data available. However, after invoking async_write(socket1, ...), socket2 has data even though the io_service has not been ran:
#include <boost/asio.hpp>
constexpr auto noop = [](auto&& ...){};
int main()
{
using boost::asio::ip::tcp;
boost::asio::io_service io_service;
// Create all I/O objects.
tcp::acceptor acceptor{io_service, {{}, 0}};
tcp::socket socket1{io_service};
tcp::socket socket2{io_service};
// Connect sockets.
acceptor.async_accept(socket1, noop);
socket2.async_connect(acceptor.local_endpoint(), noop);
io_service.run();
io_service.reset();
// Verify socket2 has no data.
assert(0 == socket2.available());
// Initiate an asynchronous write. However, do not run
// the `io_service`.
std::string data{"example"};
async_write(socket1, boost::asio::buffer(data), noop);
// Verify socket2 has data.
assert(0 < socket2.available());
}

For instance, you want to send some data to a remote-partner - asynchronous.
boost::asio::async_write(_socket, boost::asio::buffer(msg.data(), msg.size()),
std::bind(&Socket::WriteHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
//Where 'this' is the class Socket
Before that, you probably have created a thread which called ioService.run(). The async_write function will take the same ioService you have used to create your socket. It puts it into the queue of your ioService to execute the write operation and the handler - on the thread your ioService runs on, as the async_ already suggests.

Related

boost::asio Strictly sequential invocation of event handlers using strand

I have a question regarding to the usage of strand in boost::asio framework.
The manuals refer the following
In the case of composed asynchronous operations, such as async_read()
or async_read_until(), if a completion handler goes through a strand,
then all intermediate handlers should also go through the same strand.
This is needed to ensure thread safe access for any objects that are
shared between the caller and the composed operation (in the case of
async_read() it's the socket, which the caller can close() to cancel
the operation). This is done by having hook functions for all
intermediate handlers which forward the calls to the customisable hook
associated with the final handler:
Let's say that we have the following example
Strand runs in a async read socket operation . Socket read the data and forwards them to a async writer socket. Two operation are in the same io_service. Is this write operation thread safe as well?Is is called implicity in the same strand? Or is it needed to explicitly call async_write in the strand
read_socket.async_read_some(my_buffer,
boost::asio::bind_executor(my_strand,
[](error_code ec, size_t length)
{
write_socket.async_write_some(boost::asio::buffer(data, size), handler);
}));
Is the async_write_some sequential executing in the following example or needs strand as well?
Yes, since you bound the completion handler to the strand executor (explicitly, as well), you know it will be invoked on the strand - which includes async_write_some.
Note you can also have an implicit default executor for the completion by constructing the socket on the strand:
tcp::socket read_socket { my_strand };
In that case you don't have to explicitly bind the handler to the strand:
read_socket.async_read_some( //
my_buffer, my_strand, [](error_code ec, size_t length) {
write_socket.async_write_some(asio::buffer(data, size), handler);
});
I prefer this style because it makes it much easier to write generic code which may or may not require strands.
Note that the quoted documentation has no relation to the question because none of the async operations are composed operations.

asio::use_future and event loop

Im browsing asio feature use_future, reading the source code.
But cannot figure out how it works. Say, if i call
auto fut = async_write(sock, buffer, use_future)
fut becomes std::future (according to source code). Now, if i call fut.get() i should able to wait async operation complete and get return value. In the use_future.hpp file i see standard for asio async_result handler resolution and so on..
But if i block on future::get() call, how the IO loop continue to work so operation can complete? Does it create a system thread?
The Asio tutorial mentions that for single-threaded applications, one may observe poor responsiveness if handlers take a long time to complete. In this case, if only one thread is processing the io_service, then one would observe a deadlock.
When boost::asio::use_future is used, the initiating operation will:
initiate the underlying operation with a completion handler that will set the value or error on a std::promise
return to the caller a std::future associated with the std::promise.
If a single thread is processing the I/O service, and it blocks on future::get(), then the completion handler that would set the std::promise will never be invoked. It is the application's responsibility to prevent this from occurring. The official futures example accomplishes this by creating an additional thread that is dedicated to processing the I/O service and waits on std::future from within a thread that is not processing the I/O service.
// We run the io_service off in its own thread so that it operates
// completely asynchronously with respect to the rest of the program.
boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
std::thread thread([&io_service](){ io_service.run(); });
std::future<std::size_t> send_length =
socket.async_send_to(..., boost::asio::use_future);
// Do other things here while the send completes.
send_length.get(); // Blocks until the send is complete. Throws any errors.
Does it create a system thread?
No. You're supposed free to decide on which thread(s) to run io_service::run

Why io_service can be used without run() for synchronous operations

I'm interested why I can use boost::asio::io_service without run() call for syncronous socket operations? For example the following piece of code is valid :
boost::asio::io_service io_service;
tcp::acceptor a_event_data(io_service, tcp::endpoint(tcp::v4(), port + 2));
// Accept a connection to event socket
socket_ptr sock_event_data(new tcp::socket(io_service));
a_event_data.accept(*sock_event_data);
boost::asio::ip::tcp::endpoint endpoint_event_data = sock_event_data->remote_endpoint();
std::cout << "Accepted a connection for Event Data socket from " << endpoint_event_data.address().to_string() << std::endl;
But there is no call for run(). In case of asynchronous connections I must use it. Is it a dummy usage of io_service here just to interface with the system?
The synchronous operation executes the I/O operations on the current thread. The asynchronous operation executes the I/O operations on one of the threads in io_service's thread pool. If you don't call run() at least once (when there is actual work to do; otherwise it will immediately return) there is no thread in the pool.
Synchronous operations on I/O objects implicitly run the io_service object for an individual operation.
The io_service functions run(), run_one(), poll() or poll_one() must be called for the io_service to perform asynchronous operations on behalf of a C++ program.
Notification that an asynchronous operation has completed is delivered by invocation of the associated handler. Handlers are invoked only by a thread that is currently calling any overload of run(), run_one(), poll() or poll_one() for the io_service.

Boost::asio::async_write, handler called only once

I am quite new in boost::asio and I have a problem. I am writting client that sends in loop some commands to server. I am sending command with boost::asio::async_write and I expect that every time I send commands handler will be called. In fact only during first sending I see that handler is called. My client looks like that:
Client::Client(boost::asio::io_service & p_ioService,
boost::asio::ip::tcp::endpoint p_endpoint)
: io_service(p_ioService), endpoint(p_endpoint), socket(p_ioService)
{
socket.connect(endpoint);
}
Client::~Client()
{
socket.close();
}
void Client::sendCommand(const string & p_command)
{
boost::asio::async_write(socket,boost::asio::buffer(p_command),
boost::bind(&Client::onSendingFinished,this, _1, _2));
io_service.run();
}
void Client::onSendingFinished(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
cout<<"Sent "<<bytes_transferred<<endl;
}
There is no other place in main.cpp where io_service.run is called. I notice that if I call
io_service.reset() after io_service.run() it works fine, handler is called every time.
How should I solve this without io_service.reset()
Thanks in advance
I do not understand the aversion to calling io_service::reset(). In this case, it is necessary to invoke prior to any subsequent calls to io_service::run():
reset() must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_service being stopped or running out of work.
It is possible that a thread returns from run() as a result of an exception being thrown, yet the io_service has neither been stopped nor ran out of work. In this case, the thread can invoke run() without calling reset().
The current Client::sendCommand() is synchronous. It is an implementation detail that it initiates an asynchronous operation, then blocks in io_service::run() waiting for the operation to complete. Unless there are multiple threads invoking commands on socket, multiple threads running the io_service, or the write operation needs to be cancellable, such as from a timeout, then it would be functionally equivalent and possible easier to implement Client::sendCommand() with a synchronous write().
void Client::sendCommand(const string & p_command)
{
boost::system::error_code ec;
std::size_t bytes_transferred =
boost::asio::write(socket, boost::asio::buffer(p_command), ec);
onSendingFinished(ec, bytes_transferred);
}
If Client::sendCommand() needs to be asynchronous, then:
The io_service should be ran from outside of Client::sendCommand(). If the io_service does not always have outstanding work, then io_service::work can be used control when run() returns. See this answer for more details as to when io_service::run() blocks and unblocks.
The underlying memory provided to async_write() as the buffer (p_command) needs to remain valid until the operation's handler, Client::onSendingFinished(), has been called. In this case, it may require making a copy of p_command in Client::sendCommand(), writing the copy to the socket, then deleting the copy from within the handler.
[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
While it is not inherently bad to call reset() every now and then, there are two typical ways to avoid having to do it.
Start a new async operation within the handler of the first one. run() only returns once all handlers have finished and thus a new async operation started in a handler is still in time to keep blocking io_service.
Use io_service::work. If you create an instance of io_service::work constructed with your io_service as parameter, than your subsequent calls to run() will not return as long as the work object remains alive. And thus you will not have to reset anything. Of course this means that either one of your handlers or another thread has to destroy the work object at some time, if you want run() to ever stop blocking.
It's quite unusual to just send messages, it's far more common to have two way communication.
If you implemented a receiver as well, then your receive code would always require a receive handler running in the io_service and you wouldn't have this problem...

Confused when boost::asio::io_service run method blocks/unblocks

Being a total beginner to Boost.Asio, I am confused with io_service::run(). I would appreciate it if someone could explain to me when this method blocks/unblocks. The documentations states:
The run() function blocks until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped.
Multiple threads may call the run() function to set up a pool of threads from which the io_service may execute handlers. All threads that are waiting in the pool are equivalent and the io_service may choose any one of them to invoke a handler.
A normal exit from the run() function implies that the io_service object is stopped (the stopped() function returns true). Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately unless there is a prior call to reset().
What does the following statement mean?
[...] no more handlers to be dispatched [...]
While trying to understand the behavior of io_service::run(), I came across this example (example 3a). Within it, I observe that io_service->run() blocks and waits for work orders.
// WorkerThread invines io_service->run()
void WorkerThread(boost::shared_ptr<boost::asio::io_service> io_service);
void CalculateFib(size_t);
boost::shared_ptr<boost::asio::io_service> io_service(
new boost::asio::io_service);
boost::shared_ptr<boost::asio::io_service::work> work(
new boost::asio::io_service::work(*io_service));
// ...
boost::thread_group worker_threads;
for(int x = 0; x < 2; ++x)
{
worker_threads.create_thread(boost::bind(&WorkerThread, io_service));
}
io_service->post( boost::bind(CalculateFib, 3));
io_service->post( boost::bind(CalculateFib, 4));
io_service->post( boost::bind(CalculateFib, 5));
work.reset();
worker_threads.join_all();
However, in the following code that I was working on, the client connects using TCP/IP and the run method blocks until data is asynchronously received.
typedef boost::asio::ip::tcp tcp;
boost::shared_ptr<boost::asio::io_service> io_service(
new boost::asio::io_service);
boost::shared_ptr<tcp::socket> socket(new tcp::socket(*io_service));
// Connect to 127.0.0.1:9100.
tcp::resolver resolver(*io_service);
tcp::resolver::query query("127.0.0.1",
boost::lexical_cast< std::string >(9100));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
socket->connect(endpoint_iterator->endpoint());
// Just blocks here until a message is received.
socket->async_receive(boost::asio::buffer(buf_client, 3000), 0,
ClientReceiveEvent);
io_service->run();
// Write response.
boost::system::error_code ignored_error;
std::cout << "Sending message \n";
boost::asio::write(*socket, boost::asio::buffer("some data"), ignored_error);
Any explanation of run() that describes its behavior in the two examples below would be appreciated.
Foundation
Lets start with a simplified example and examine the relevant Boost.Asio pieces:
void handle_async_receive(...) { ... }
void print() { ... }
...
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);
...
io_service.post(&print); // 1
socket.connect(endpoint); // 2
socket.async_receive(buffer, &handle_async_receive); // 3
io_service.post(&print); // 4
io_service.run(); // 5
What Is A Handler?
A handler is nothing more than a callback. In the example code, there are 3 handlers:
The print handler (1).
The handle_async_receive handler (3).
The print handler (4).
Even though the same print() function is used twice, each use is considered to create its own uniquely identifiable handler. Handlers can come in many shapes and sizes, ranging from basic functions like the ones above to more complex constructs such as functors generated from boost::bind() and lambdas. Regardless of the complexity, the handler still remains nothing more than a callback.
What Is Work?
Work is some processing that Boost.Asio has been requested to do on behalf of the application code. Sometimes Boost.Asio may start some of the work as soon as it has been told about it, and other times it may wait to do the work at a later point in time. Once it has finished the work, Boost.Asio will inform the application by invoking the supplied handler.
Boost.Asio guarantees that handlers will only run within a thread that is currently calling run(), run_one(), poll(), or poll_one(). These are the threads that will do work and call handlers. Therefore, in above example, print() is not invoked when it is posted into the io_service (1). Instead, it is added to the io_service and will be invoked at a later point in time. In this case, it within io_service.run() (5).
What Are Asynchronous Operations?
An asynchronous operation creates work and Boost.Asio will invoke a handler to inform the application when the work has completed. Asynchronous operations are created by calling a function that has a name with the prefix async_. These functions are also known as initiating functions.
Asynchronous operations can be decomposed into three unique steps:
Initiating, or informing, the associated io_service that works needs to be done. The async_receive operation (3) informs the io_service that it will need to asynchronously read data from the socket, then async_receive returns immediately.
Doing the actual work. In this case, when socket receives data, bytes will be read and copied into buffer. The actual work will be done in either:
The initiating function (3), if Boost.Asio can determine that it will not block.
When the application explicitly run the io_service (5).
Invoking the handle_async_receive ReadHandler. Once again, handlers are only invoked within threads running the io_service. Thus, regardless of when the work is done (3 or 5), it is guaranteed that handle_async_receive() will only be invoked within io_service.run() (5).
The separation in time and space between these three steps is known as control flow inversion. It is one of the complexities that makes asynchronous programming difficult. However, there are techniques that can help mitigate this, such as by using coroutines.
What Does io_service.run() Do?
When a thread calls io_service.run(), work and handlers will be invoked from within this thread. In the above example, io_service.run() (5) will block until either:
It has invoked and returned from both print handlers, the receive operation completes with success or failure, and its handle_async_receive handler has been invoked and returned.
The io_service is explicitly stopped via io_service::stop().
An exception is thrown from within a handler.
One potential psuedo-ish flow could be described as the following:
create io_service
create socket
add print handler to io_service (1)
wait for socket to connect (2)
add an asynchronous read work request to the io_service (3)
add print handler to io_service (4)
run the io_service (5)
is there work or handlers?
yes, there is 1 work and 2 handlers
does socket have data? no, do nothing
run print handler (1)
is there work or handlers?
yes, there is 1 work and 1 handler
does socket have data? no, do nothing
run print handler (4)
is there work or handlers?
yes, there is 1 work
does socket have data? no, continue waiting
-- socket receives data --
socket has data, read it into buffer
add handle_async_receive handler to io_service
is there work or handlers?
yes, there is 1 handler
run handle_async_receive handler (3)
is there work or handlers?
no, set io_service as stopped and return
Notice how when the read finished, it added another handler to the io_service. This subtle detail is an important feature of asynchronous programming. It allows for handlers to be chained together. For instance, if handle_async_receive did not get all the data it expected, then its implementation could post another asynchronous read operation, resulting in io_service having more work, and thus not returning from io_service.run().
Do note that when the io_service has ran out of work, the application must reset() the io_service before running it again.
Example Question and Example 3a code
Now, lets examine the two pieces of code referenced in the question.
Question Code
socket->async_receive adds work to the io_service. Thus, io_service->run() will block until the read operation completes with success or error, and ClientReceiveEvent has either finished running or throws an exception.
Example 3a Code
In hopes of making it easier to understand, here is a smaller annotated Example 3a:
void CalculateFib(std::size_t n);
int main()
{
boost::asio::io_service io_service;
boost::optional<boost::asio::io_service::work> work = // '. 1
boost::in_place(boost::ref(io_service)); // .'
boost::thread_group worker_threads; // -.
for(int x = 0; x < 2; ++x) // :
{ // '.
worker_threads.create_thread( // :- 2
boost::bind(&boost::asio::io_service::run, &io_service) // .'
); // :
} // -'
io_service.post(boost::bind(CalculateFib, 3)); // '.
io_service.post(boost::bind(CalculateFib, 4)); // :- 3
io_service.post(boost::bind(CalculateFib, 5)); // .'
work = boost::none; // 4
worker_threads.join_all(); // 5
}
At a high-level, the program will create 2 threads that will process the io_service's event loop (2). This results in a simple thread pool that will calculate Fibonacci numbers (3).
The one major difference between the Question Code and this code is that this code invokes io_service::run() (2) before actual work and handlers are added to the io_service (3). To prevent the io_service::run() from returning immediately, an io_service::work object is created (1). This object prevents the io_service from running out of work; therefore, io_service::run() will not return as a result of no work.
The overall flow is as follows:
Create and add the io_service::work object added to the io_service.
Thread pool created that invokes io_service::run(). These worker threads will not return from io_service because of the io_service::work object.
Add 3 handlers that calculate Fibonacci numbers to the io_service, and return immediately. The worker threads, not the main thread, may start running these handlers immediately.
Delete the io_service::work object.
Wait for worker threads to finish running. This will only occur once all 3 handlers have finished execution, as the io_service neither has handlers nor work.
The code could be written differently, in the same manner as the Original Code, where handlers are added to the io_service, and then the io_service event loop is processed. This removes the need to use io_service::work, and results in the following code:
int main()
{
boost::asio::io_service io_service;
io_service.post(boost::bind(CalculateFib, 3)); // '.
io_service.post(boost::bind(CalculateFib, 4)); // :- 3
io_service.post(boost::bind(CalculateFib, 5)); // .'
boost::thread_group worker_threads; // -.
for(int x = 0; x < 2; ++x) // :
{ // '.
worker_threads.create_thread( // :- 2
boost::bind(&boost::asio::io_service::run, &io_service) // .'
); // :
} // -'
worker_threads.join_all(); // 5
}
Synchronous vs. Asynchronous
Although the code in the question is using an asynchronous operation, it is effectively functioning synchronously, as it is waiting for the asynchronous operation to complete:
socket.async_receive(buffer, handler)
io_service.run();
is equivalent to:
boost::asio::error_code error;
std::size_t bytes_transferred = socket.receive(buffer, 0, error);
handler(error, bytes_transferred);
As a general rule of thumb, try to avoid mixing synchronous and asynchronous operations. Often times, it can turn a complex system into a complicated system. This answer highlights advantages of asynchronous programming, some of which are also covered in the Boost.Asio documentation.
To simplify how what run does, think of it as an employee that must process a pile of paper; it takes one sheet, does what the sheet tells, throws the sheet away and takes the next one; when he runs out of sheets, it leaves the office. On each sheet there can be any kind of instruction, even adding a new sheet to the pile.
Back to asio: you can give to an io_service work in two ways, essentially: by using post on it as in the sample you linked, or by using other objects that internally call post on the io_service, like the socket and its async_* methods.