What values of error_code can a boost timer callback receive? - c++

Like many asio callbacks asio timer callbacks take argument
const boost::system::error_code&
I am curios if it is documented what are possible values for that argument, since from my naive understanding of reading header documentation only one value beside success is possible.
void on_timeout(const boost::system::error_code& e)
{
if (e != boost::asio::error::operation_aborted)
{
// Timer was not cancelled, take necessary action.
}
}

Related

How to call a handler?

I don't understand how I could return handle in case the io_context was stopped. Minimum example:
void my_class::async_get_one_scan(
std::function<void(const boost::system::error_code& ec,
std::shared_ptr<my_chunked_packet>)> handler)
{
asio::spawn(strand_, [this, handler] (asio::yield_context yield)
{
const auto work = boost::asio::make_work_guard(io_service_);
my_chunk_buffer chunks;
while (!chunks.full()) {
std::array<uint8_t, 1000> datagram;
boost::system::error_code ec;
auto size = socket_.async_receive(asio::buffer(datagram), yield[ec]);
if (!ec)
process_datagram(datagram, size, chunks);
else {
handler(ec, nullptr);
return;
}
}
io_service_.post(std::bind(handler, boost::system::error_code, chunks.packet()));
});
}
Debug asio output:
#asio|1532525798.533266|6*7|strand#01198ff0.dispatch
#asio|1532525798.533266|>7|
#asio|1532525798.533266|>0|
#asio|1532525798.533266|0*8|socket#008e345c.async_receive
#asio|1532525798.533266|<7|
#asio|1532525798.533266|<6|
#asio|1532525799.550640|0|socket#008e34ac.close
#asio|1532525799.550640|0|socket#008e345c.close
#asio|1532525799.551616|~8|
So the last async_receive() #8 is created, after |<6| io_context.stop() is called and then I have no idea how to get the error_code from yield_context to call the handler.
question#2 is it even a correct way of async reading of chunks of data to collect the whole packet?
By definition, io_context::stop prevents the event loop from executing other handlers. So there's no way to get the exit code into the handler, because it doesn't get invoked.
You probably want to have a "soft-stop" function instead, where you stop admitting new async tasks to the io_context and optionally cancel any pending operations.
If pending operations could take too long, you will want to add a deadline timer that forces the cancellation at some threshold time interval.
The usual way to make the run loop exit is by releasing a work object. See https://www.boost.org/doc/libs/1_67_0/doc/html/boost_asio/reference/io_context__work.html

async_wait handler class member with cancellation handling

This works well:
class cStartSequence
{
void Tick()
{
// do something
}
void Wait()
{
myTimer->expires_from_now(
boost::posix_time::seconds( mySecs ) );
myTimer->async_wait(boost::bind(
&cStartSequence::Tick,
this
));
}
...
};
I want to be able to cancel the timer and have the handler do something different
void Tick( boost::system::error_code & ec )
{
if( ! ec )
// do something
else
// do something different
}
The question is how to modify the call to async_wait?
This does not compile
myTimer->async_wait(boost::bind(
&cStartSequence::Tick,
this,
boost::asio::placeholders::error
));
compiler complaint:
C:\Users\James\code\boost\v1_63\boost\bind\bind.hpp|319|error:
no match for call to
'(boost::_mfi::mf1<void, pinmed::wrs::cStartSequence, boost::system::error_code&>)
(pinmed::wrs::cStartSequence*&, const boost::system::error_code&)'
I tried some variations on the async_wait parameters, but no luck.
boost:asio v1.63, windows 10, code::blocks v16.01
Your Tick method takes a non-const reference. That's not ok (and doesn't satisfy the handler requirements). Use either
void Tick(boost::system::error_code const& ec);
Or maybe
void Tick(boost::system::error_code ec);
The first one is preferred by Asio author(s) for slightly obscurantist reasons (library-specific optimizations IIRC)
PS. For safe cancellation of deadline timers see also
cancel a deadline_timer, callback triggered anyway
Proper cleanup with a suspended coroutine

What is the advantage of class member functions to call each other in C++?

I am new to C++. I found that the following programming style is quite interesting to me. I wrote a simplified version here.
#include <iostream>
using namespace std;
class MyClass {
public :
MyClass(int id_) : id(id_) {
cout<<"I am a constructor"<<endl;
}
bool error = false;
void run() {
//do something ...
if (!error) {
read();
}
}
void read() {
//do something ...
if (!error) {
write();
}
}
void write() {
//do something ...
if (!error) {
read();
}
}
private :
int id;
};
int main() {
MyClass mc(1);
mc.run();
return 0;
}
The example here is compilable, but I didn't run it because I must go into an infinite loop. But, I hope to use this as a reference. The read() and write() are calling each other. I first encountered this programming style in boost.asio. When the server received a message in do_read(), it calls do_write() to echo the client, then it calls do_read() again at the end of the do_write().
I have two questions regarding this type of coding.
Will this cause stack overflow? Because the functions are keeping calling themselves and the function ends only an error occurs.
What is the advantage of it? Why can't I use a function to loop them orderly and break the loop whenever it encounters an error.
bool replied = true;
while (!error) {
if (replied) read();
else {
write();
replied = !replied;
}
}
Your simplified version leaves out the most important aspect: the write() and read() calls are asynchronous.
Therefore, the functions don't actually cause recursion, see this recent answer: Do "C++ boost::asio Recursive timer callback" accumulate callstack?
The "unusual" thing about async_read(...) and async_write(...) is that the functions return before the IO operation has actually been performed, let alone completed. The actual execution is done on a different schedule¹.
To signal compleion back to the "caller" the async calls typically take a completion handler, which gets called with the result of the IO operation.
In that completion handler, it's typical to see either the end of the communication channel, or the next IO operation being scheduled. This is known as asynchronous call chaining and is very prominently present in many languages that support asynchronous operations ²
It takes some getting used to, but ultimately you get used to the pattern.
With this in mind, revisit one of the boost samples and see if the penny drops:
Documentation sample Chat Client
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
}
void handle_read_header(const boost::system::error_code& error)
{
if (!error && read_msg_.decode_header())
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
boost::bind(&chat_client::handle_read_body, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void handle_read_body(const boost::system::error_code& error)
{
if (!error)
{
std::cout.write(read_msg_.body(), read_msg_.body_length());
std::cout << "\n";
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void do_write(chat_message msg)
{
bool write_in_progress = !write_msgs_.empty();
write_msgs_.push_back(msg);
if (!write_in_progress)
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
write_msgs_.pop_front();
if (!write_msgs_.empty())
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
else
{
do_close();
}
}
void do_close()
{
socket_.close();
}
Benefit Of Asynchronous Operations
Asynchronous IO are useful for a more event-based model of IO. Also they remove the first "ceiling" when scaling to large volumes of IO operations. In traditional, imperative code patterns many clients/connections would require many threads in order to be able to serve them simultaneously. In practice, though, threads fail to scale (since a typical server has a smallish number of logical CPUs) and it would mean that IO operations block each other ³.
With asynchronous IO you can often do all IO operations on a single thread, greatly improving efficiency - and thereby some aspects of the program design (because fewer threading issues need to be involved).
¹ Many choices exist, but imagine that io_service::run() is running on a separate thread, that would lead to the IO operations being actually executed, potentially resumed when required and completed on that thread
² I'd say javascript is infamous for this pattern
³ A classical example is when a remote procedure call keeps a thread occupied while waiting for e.g. a database query to complete
This is my opinion:
Regarding recursion
One way to cause a stack overflow is to have a function calling itself recursively, overflowing the call stack. A set of functions calling each other in a circular manner would be equivalent to that, so yes, your intuition is correct.
An iterative version of the algorithm, such as the loop you describe, could prevent that.
Now, another thing that can prevent a stack overflow is the presence of code that could be optimized for tail recursion. Tail recursion optimization requires a compiler implementing this feature. Most major compilers implement it. The Boost.Asio function you mention seems to be benefiting from this optimization.
Regarding code design
Now, C++ implements many programming paradigms. These paradigms are also implemented by many other programming languages. The programming paradigms relevant to what you are discussing would be:
Structured programming
Object oriented programming
From a structured programming point of view, you should try to emphasize code reuse as much as possible by diving the code in subroutines that minimize redundant code.
From an object oriented point of view, you should model classes in a way that encapsulates their logic as much as possible.
The logic you present so far seems encapsulated enough, however, you may need to review if the methods write and read should remain public, or if they should be private instead. Minimizing the number of public methods helps achieving a higher level of encapsulation.

How to make asynchronous call with timeout

I want to make an asynchronous call in C++ with timeout, meaning I want to achieve sth like that.
AsynchronousCall(function, time);
if(success)
//call finished succesfully
else
//function was not finished because of timeout
EDIT : Where function is a method that takes a lot of time and I want to interrupt it when it takes too much time.
I' ve been looking for how to achieve it and I thinki boost::asio::deadline_timer is way to go. I guess calling timer.async_wait(boost::bind(&A::fun, this, args)) is what I need, but I do not know how to find if the call was success or was aborted due to timeout.
EDIT: after the answer from ForEveR my code now looks like this.
boost::asio::io_service service;
boost::asio::deadline_timer timer(service);
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait(boost::bind(&A::CheckTimer, this, boost::asio::placeholders::error));
boost::thread bt(&A::AsynchronousMethod, this, timer, args); //asynchronous launch
void A::CheckTimer(const boost::system::error_code& error)
{
if (error != boost::asio::error::operation_aborted)
{
cout<<"ok"<<endl;
}
// timer is cancelled.
else
{
cout<<"error"<<endl;
}
}
I wanted to pass the timer by reference and cancel it in the end of asynchronous method, but I got an error that I cannot access private member declared in class ::boost::asio::basic_io_object.
Maybe using the deadline timer is not that good idea ? I would really appreciate any help. I am passing the timer to the function, because the method that calls the asynchronous method is asynchronous itself and thus I cannot have one timer for whole class or sth like that.
You should use boost::asio::placeholders::error
timer.async_wait(boost::bind(
&A::fun, this, boost::asio::placeholders::error));
A::fun(const boost::system::error_code& error)
{
// timeout, or some other shit happens
if (error != boost::asio::error::operation_aborted)
{
}
// timer is cancelled.
else
{
}
}

Chaining asynchronous Lambdas with Boost.Asio?

I find myself writing code that basically looks like this:
using boost::system::error_code;
socket.async_connect(endpoint, [&](error_code Error)
{
if (Error)
{
print_error(Error);
return;
}
// Read header
socket.async_read(socket, somebuffer, [&](error_code Error, std::size_t N)
{
if (Error)
{
print_error(Error);
return;
}
// Read actual data
socket.async_read(socket, somebuffer, [&](error_code Error, std::size_t N)
{
// Same here...
});
});
};
So basically I'm nesting callbacks in callbacks in callbacks, while the logic is simple and "linear".
Is there a more elegant way of writing this, so that the code is both local and in-order?
One elegant solution is to use coroutines. Boost.Asio supports both stackless coroutines, which introduce a small set of pseudo-keywords, and stackful coroutines, which use Boost.Coroutine.
Stackless Coroutines
Stackless coroutines introduce a set of pseudo-keywords preprocessor macros, that implement a switch statement using a technique similar to Duff's Device. The documentation covers each of the keywords in detail.
The original problem (connect->read header->read body) might look something like the following when implemented with stackless coroutines:
struct session
: boost::asio::coroutine
{
boost::asio::ip::tcp::socket socket_;
std::vector<char> buffer_;
// ...
void operator()(boost::system::error_code ec = boost::system::error_code(),
std::size_t length = 0)
{
// In this example we keep the error handling code in one place by
// hoisting it outside the coroutine. An alternative approach would be to
// check the value of ec after each yield for an asynchronous operation.
if (ec)
{
print_error(ec);
return;
}
// On reentering a coroutine, control jumps to the location of the last
// yield or fork. The argument to the "reenter" pseudo-keyword can be a
// pointer or reference to an object of type coroutine.
reenter (this)
{
// Asynchronously connect. When control resumes at the following line,
// the error and length parameters reflect the result of
// the asynchronous operation.
yield socket_.async_connect(endpoint_, *this);
// Loop until an error or shutdown occurs.
while (!shutdown_)
{
// Read header data. When control resumes at the following line,
// the error and length parameters reflect the result of
// the asynchronous operation.
buffer_.resize(fixed_header_size);
yield socket_.async_read(boost::asio::buffer(buffer_), *this);
// Received data. Extract the size of the body from the header.
std::size_t body_size = parse_header(buffer_, length);
// If there is no body size, then leave coroutine, as an invalid
// header was received.
if (!body_size) return;
// Read body data. When control resumes at the following line,
// the error and length parameters reflect the result of
// the asynchronous operation.
buffer_.resize(body_size);
yield socket_.async_read(boost::asio::buffer(buffer_), *this);
// Invoke the user callback to handle the body.
body_handler_(buffer_, length);
}
// Initiate graceful connection closure.
socket_.shutdown(tcp::socket::shutdown_both, ec);
} // end reenter
}
}
Stackful Coroutines
Stackful coroutines are created using the spawn() function. The original problem may look something like the following when implemented with stackful coroutines:
boost::asio::spawn(io_service, [&](boost::asio::yield_context yield)
{
boost::system::error_code ec;
boost::asio::ip::tcp::socket socket(io_service);
// Asynchronously connect and suspend the coroutine. The coroutine will
// be resumed automatically when the operation completes.
socket.async_connect(endpoint, yield[ec]);
if (ec)
{
print_error(ec);
return;
}
// Loop until an error or shutdown occurs.
std::vector<char> buffer;
while (!shutdown)
{
// Read header data.
buffer.resize(fixed_header_size);
std::size_t bytes_transferred = socket.async_read(
boost::asio::buffer(buffer), yield[ec]);
if (ec)
{
print_error(ec);
return;
}
// Extract the size of the body from the header.
std::size_t body_size = parse_header(buffer, bytes_transferred);
// If there is no body size, then leave coroutine, as an invalid header
// was received.
if (!body_size) return;
// Read body data.
buffer.resize(body_size);
bytes_transferred =
socket.async_read(boost::asio::buffer(buffer), yield[ec]);
if (ec)
{
print_error(ec);
return;
}
// Invoke the user callback to handle the body.
body_handler_(buffer, length);
}
// Initiate graceful connection closure.
socket.shutdown(tcp::socket::shutdown_both, ec);
});