Minimal awaitable example - c++

I wonder why the following programme crashes. How to use awaitable not with boost::asio::async_write/async_read functions.
Let's see:
#include <iostream>
#include <boost/asio/io_context.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
using boost::asio::io_context;
using boost::asio::co_spawn;
using boost::asio::awaitable;
using boost::asio::detached;
awaitable<void> task() {
std::cout << "hello" << std::endl;
return awaitable<void>{};
}
int main() {
try {
boost::asio::io_context io_context(1);
co_spawn(io_context, task(), detached);
io_context.run();
} catch(const std::exception &ex) {
std::cerr << ex.what() << std::endl;
}
return 0;
}
This results in
hello
(Segmentation fault)
What is wrong about it?
UPD: self-solved.
I should just use co_return. Thus it must be the following:
awaitable<void> task() {
std::cout << "hello" << std::endl;
co_return;
}

digging into the sources of boost::asio::awaitable, I figured out that I just should make use of co_return keyword. Surprisingly, it is not shipped with boost. It is enabled either by -fcoroutines flag or -std=c++20. Unexpectedly. Having said that, it is solved.

Related

Throwing Clear Exception in a Multithreaded Context with a proram STOP

I have a program that throws in another thread as follows:
#include <iostream>
#include <stdexcept>
#include <thread>
void op()
{
std::cout << "OP HELLO WORLD" << std::endl;
throw(std::runtime_error("OP ERROR"));
};
int main(int argc, char* argv[])
{
//throw(std::runtime_error("MAIN ERROR"));
std::thread t(op);
t.join();
std::cout << "MAIN HELLO WORLD" << std::endl;
};
I want my program to print a clear error message and stop. I know I can use std::abort or std::terminate, however the error message is not clear and does not contain the log "OP ERROR".
Could you please help me code a program that when fails exits and logs a clear error message. std::future does not work too when I tried a try, catch block?
Are you looking for something like this?
#include <iostream>
#include <stdexcept>
#include <thread>
#include <future>
void op()
{
std::cout << "OP HELLO WORLD" << std::endl;
throw(std::runtime_error("OP ERROR. File: " __FILE__ ", Line: "+ std::to_string(__LINE__)));
};
int main(int argc, char* argv[])
{
//throw(std::runtime_error("MAIN ERROR"));
auto res = std::async(op);
try {
res.get();
} catch(const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return -1;
}
std::cout << "MAIN HELLO WORLD" << std::endl;
}
If you want to output errors for all threads without returning exceptions to main thread, you can setup your own version of terminate().
#include <iostream>
#include <cstdlib>
#include <exception>
#include <thread>
void op()
{
std::cout << "OP HELLO WORLD" << std::endl;
throw(std::runtime_error("OP ERROR"));
}
void my_terminate()
{
auto eptr = std::current_exception();
try {
if (eptr) {
std::rethrow_exception(eptr);
}
} catch(const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
std::abort();
}
int main(int argc, char* argv[])
{
std::set_terminate(my_terminate);
//throw(std::runtime_error("MAIN ERROR"));
std::thread t(op);
t.join();
std::cout << "MAIN HELLO WORLD" << std::endl;
}
For some reason MSVC uses per-thread handlers, and for it to work you need to call set_termnate() in every thread separatelly.
To avoid the call to std::terminate, make sure exceptions don't "leak" outside the thread function.
void op()
{
try {
std::cout << "OP HELLO WORLD" << std::endl;
throw std::runtime_error("OP ERROR");
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
exit(1);
}
}
If you want to automatically see the exception's file and line number, in C++ it is not generally possible.
If you want to catch all exceptions in a single place in the main thread, you need to catch them and pass them to the main thread. There's a useful wrapper utility std::packaged_task which gives back a future on which you can wait for the result or an exception.
#include <iostream>
#include <stdexcept>
#include <thread>
#include <future>
void op()
{
std::cout << "OP HELLO WORLD" << std::endl;
throw std::runtime_error("OP ERROR");
}
int main(int argc, char* argv[])
{
try {
std::packaged_task<void()> task{&op};
auto result = task.get_future();
std::thread t{std::move(task)};
t.join();
result.get();
std::cout << "MAIN HELLO WORLD" << std::endl;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
}

Why `std::invalid_argument` is not caught with no-rtti in macOS M1 environment?

Today I met a weird behaviour in catching exceptions in C++, could anyone clarify it to me? Code snippe
#include <iostream>
#include <string>
#include <exception>
int main() {
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) {
std::cerr << "std::invalid_argument" << std::endl;
} catch (const std::out_of_range&) {
std::cerr << "std::out_of_range" << std::endl;
} catch (const std::exception&) {
std::cerr << "Caught by ancestor" << std::endl;
} catch (...) {
auto ptr = std::current_exception();
auto type = __cxxabiv1::__cxa_current_exception_type();
std::cerr << type->name() << std::endl;
std::cerr << "..." << std::endl;
}
return 0;
}
Writes to output
St16invalid_argument
...
Environment details
C++ 14, disabled RTTI
Clang 13.1.6 arm64-apple-darwin-21.6.0
macOS Monterey 12.6
I expect exception to be caught on the very first catch block
Upd. Even simplest catch doesn't work for me on the environment
try {
std::stod("notanumber");
} catch (const std::invalid_argument&) { // not caught
std::cerr << "std::invalid_argument" << std::endl;
}

Use of Boost.Coroutine with Boost.ASIO causes assertion

I have boost::asio::io_context which is running in several threads (via ctx.run()) and I have several asynchronous objects that are used inside boost.coroutines, which are running via boost::asio::spawn. And I have the following assertion:
Assertion failed: ! is_running(), file C:\boost\boost_1_68_0\msvc_x86\include\boost-1_68\boost\coroutine\detail\push_coroutine_impl.hpp, line 258
I'm providing a minimal example which causes the same error. Please, help me: what am I doing wrong?
Boost version is 1.68
[Update]: there is a fix in the code (forget to lock io_context), but it doesn't affect assertion anyway.
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
template<class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::system::error_code))
asyncDo(boost::asio::io_context& ctx, CompletionToken&& token)
{
using CompletionType = boost::asio::async_completion<CompletionToken, void(boost::system::error_code)>;
CompletionType completion{ token };
ctx.post(
[handler{ completion.completion_handler }](){
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
return;
});
return completion.result.get();
}
void coroFunc(boost::asio::io_context& ctx, boost::asio::yield_context yield)
{
for (;;) {
std::cerr << std::this_thread::get_id() << '\n';
asyncDo(ctx, yield);
std::cerr << std::this_thread::get_id() << '\n';
}
}
int main(int, char* [])
{
boost::asio::io_context ctx;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(ctx.get_executor());
boost::asio::spawn(ctx, std::bind(coroFunc, std::ref(ctx), std::placeholders::_1));
std::thread([&ctx]() { ctx.run(); }).detach();
std::thread([&ctx]() { ctx.run(); }).detach();
std::cin.get();
work.reset();
return 0;
}
After several hours of googling and trying I've got the solution that works for me (at least at my tests). The main idea is to replace posting to ctx with posting to associated executor:
auto executor = boost::asio::get_associated_executor(completion.completion_handler, ctx);
auto allocator = boost::asio::get_associated_allocator(completion.completion_handler);
executor.post(
[handler = HandlerType{ std::move(completion.completion_handler) }](){
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
return;
},
allocator);
Whole code:
#include <iostream>
#include <stdexcept>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
template<class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::system::error_code))
asyncDo(boost::asio::io_context& ctx, CompletionToken&& token)
{
using CompletionType = boost::asio::async_completion<CompletionToken, void(boost::system::error_code)>;
using HandlerType = typename CompletionType::completion_handler_type;
CompletionType completion{ token };
auto executor = boost::asio::get_associated_executor(completion.completion_handler, ctx);
auto allocator = boost::asio::get_associated_allocator(completion.completion_handler);
executor.post(
[handler = HandlerType{ std::move(completion.completion_handler) }](){
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
return;
},
allocator);
return completion.result.get();
}
void coroFunc(boost::asio::io_context& ctx, boost::asio::yield_context yield)
{
for (;;) {
try {
std::cerr << "(0): " << std::this_thread::get_id() << '\n';
asyncDo(ctx, yield);
std::cerr << "(1): " << std::this_thread::get_id() << '\n';
} catch (std::exception const& e) {
std::cerr << "e: " << e.what() << '\n';
}
}
}
int main(int, char* [])
{
boost::asio::io_context ctx;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(ctx.get_executor());
boost::asio::spawn(ctx, std::bind(coroFunc, std::ref(ctx), std::placeholders::_1));
std::thread([&ctx]() {
for (;;) {
try {
ctx.run();
break;
} catch (std::exception const& e) {
std::cerr << "e: " << e.what() << '\n';
}
}
}).detach();
std::thread([&ctx]() {
for (;;) {
try {
ctx.run();
break;
} catch (std::exception const& e) {
std::cerr << "e: " << e.what() << '\n';
}
}
}).detach();
std::cin.get();
work.reset();
return 0;
}

Getting started with MongoDB C++ driver in Windows

Trying to set up a simple MongoDB database connection in Windows 7, using the C++ driver. I'm using Visual C++ compiler 19 for x86, 32-bit MongoDB 3.0.6, Boost 1_59_0, Mongo legacy 1.0.5 C++ driver.
Driver compiles OK using the command
scons --cpppath=d:\boost_1_59_0 --libpath=d:\boost_1_59_0\stage\lib --msvc-host-arch=x86 install
Program is
#include <cstdlib>
#include <iostream>
using namespace std;
#include <WinSock2.h>
#include <windows.h>
#include "mongo/client/dbclient.h"
void run() {
mongo::DBClientConnection c;
c.connect("localhost");
}
int main() {
try {
run();
std::cout << "connected ok" << std::endl;
} catch( const mongo::DBException &e ) {
std::cout << "caught " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}
Program compiles using
cl /EHsc /I"c:\mongo-cxx-driver-legacy-1.0.5\build\install\include" /I"d:\boost_1_59_0" /DSTATIC_LIBMONGOCLIENT mdb.cpp c:\mongo-cxx-driver-legacy-1.0.5\build\install\lib\libmongoclient-s.lib /link /LIBPATH:"D:\boost_1_59_0\stage\lib" ws2_32.lib
But when I run the program, get the error message
caught can't connect couldn't initialize connection to localhost, address is invalid
The server is running OK as I can access it through the shell, add records etc.
This is my first time programming MongoDB and I'm kind of stuck. Any suggestions?
OK, problem solved (thanks to stevepowell.ca/mongo-db-1.html). Here's the answer for anyone else who runs into this problem:
Windows needs to initialize the client before setting up the connection.
#include <cstdlib>
#include <iostream>
#include <WinSock2.h>
#include <windows.h>
#include <memory>
#include "mongo/client/dbclient.h"
using namespace mongo;
using namespace std;
void run() {
mongo::client::initialize(); // this line is new
mongo::DBClientConnection c;
c.connect("localhost");
}
int main() {
try {
run();
std::cout << "connected ok" << std::endl;
} catch( const mongo::DBException &e ) {
std::cout << "caught " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}
I wish this had been in the tutorial!
Onwards and upwards.

Year is out of valid range when passing pos_infin as timeout to timed_wait

The following code reproduces the error:
#include <iostream>
#include "boost/thread.hpp"
#include "boost/date_time/posix_time/ptime.hpp"
int main()
{
boost::condition_variable_any cv;
boost::timed_mutex m;
try {
{
boost::timed_mutex::scoped_timed_lock guard(m);
cv.timed_wait(guard, boost::posix_time::ptime(
boost::posix_time::pos_infin));
}
}
catch(std::exception & e) {
std::cout << "Error : " << e.what() << std::endl;
}
std::cout << "Done" << std::endl;
return 0;
}
On my system, using Visual Studio 2005 and Boost 1.43, this produces the following output:
Error : Year is out of valid range: 1400..10000
Done
I would expect it to deadlock, waiting for the condition variable to be notified for all eternity. This does not seem to be documented anywhere, and also I would expect timed_wait to accept any valid ptime. Am I doing anything wrong? Is this a bug, or are infinite timeouts just not intended?
Use boost::posix_time::max_date_time and it'll work as expected.