gRPC assertion failed when stopping async helloworld server - c++

I'm trying to shutdown properly a gRPC server.
I use the provided async helloworld from gRPC source.
The file is here: https://github.com/grpc/grpc/blob/master/examples/cpp/helloworld/greeter_async_server.cc
I have edited the main like the following:
#include "greeter_async_server.cc"
#include <thread>
#include <iostream>
void stop_task(ServerImpl* server) {
int delay = 10000;
std::cout << "Server will stop after " << delay / 1000 << " seconds" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
std::cout << "Wait finished" << std::endl;
std::cout << "Stoping server..." << std::endl;
server->Shutdown();
std::cout << "Stop sent" << std::endl;
}
void server_task(ServerImpl* server) {
server->Run();
}
int main(int argc, char** argv) {
ServerImpl server;
std::thread server_thread(server_task, &server);
std::thread stop_thread(stop_task, &server);
stop_thread.join();
server_thread.join();
std::cout << "Server stopped" << std::endl;
return 0;
}
void ServerImpl::Shutdown()
{
server->Shutdown();
// Always shutdown the completion queue after the server.
cq->Shutdown();
}
Server will stop after 10 seconds
Server listening on 0.0.0.0:50051
Wait finished
Stoping server...
E0825 15:08:30.182000000 34960 greeter_async_server.cc:156] assertion failed: ok
Sortie de TestGreeterAsyncServer.exe (processus 37008). Code : -1073740791.
Appuyez sur une touche pour fermer cette fenêtre. . .
I don't understand why the server crashes on assertion failed.
The assertion is at line 156:
GPR_ASSERT(ok);
Anyone has an idea?
Thank you in advance!

You're trying to delete an object (server) that's been created on the stack.
That said, the example code does not showcase any way to cleanly stop the server once it's been started. It is even said in a comment above the Run() method:
There is no shutdown handling in this code.
This question provides good pointers on how to accomplish your goal.
Here is why the assertion is triggered:
cq_->Next(&tag, &ok); is a blocking call, until either
An error occurs (which means ok == false) ;
A task is about to be processed (which means ok == true) ;
cq_->Shutdown() has been called (which also means ok == false).
You can read about all the different scenarios in CompletionQueue::Next() documentation. Since you're shutting down, ok is false which triggers the assertion's failure.

Here is the solution.
I have edited the greeter_async_server.cc like this:
private:
// Adding attrbutes
bool is_shutting_down = false;
std::mutex mutex;
void HandleRpcs() {
new CallData(&service_, cq_.get());
void* tag;
bool ok;
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));
// Here, added exit condition
mutex.lock();
if (true == is_shutting_down) {
mutex.unlock();
std::cout << "stop reading" << std::endl;
break;
}
else mutex.unlock();
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
}
public:
// Adding Shutdown public method
void Shutdown() {
mutex.lock();
is_shutting_down = true;
mutex.unlock();
const std::chrono::milliseconds WaitDuration = std::chrono::milliseconds(50);
const std::chrono::time_point<std::chrono::system_clock> Deadline = std::chrono::system_clock::now() + WaitDuration;
server_->Shutdown(Deadline);
cq_->Shutdown();
}

Related

Simple client/server using C++/boost socket works under Windows but fails under Linux

I'm trying to write a very simple client/server app with boost::socket. I need a server to run and a single client to connect, send data, disconnect and possibly reconnect later and repeat.
The code reduced to the minimum is here:
Server app:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class TheServer
{
public:
TheServer(int port) : m_port(port)
{
m_pIOService = new boost::asio::io_service;
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
listenForNewConnection();
}
~TheServer()
{
m_bContinueReading = false;
m_pIOService->stop();
m_pThread->join();
delete m_pThread;
delete m_pSocket;
delete m_pAcceptor;
delete m_pIOService;
}
void listenForNewConnection()
{
if (m_pSocket)
delete m_pSocket;
if (m_pAcceptor)
delete m_pAcceptor;
// start new acceptor operation
m_pSocket = new tcp::socket(*m_pIOService);
m_pAcceptor = new tcp::acceptor(*m_pIOService, tcp::endpoint(tcp::v4(), m_port));
std::cout << "Starting async_accept" << std::endl;
m_pAcceptor->async_accept(*m_pSocket,
boost::bind<void>(&TheServer::readSession, this, boost::asio::placeholders::error));
}
void readSession(boost::system::error_code error)
{
if (!error)
{
std::cout << "Connection established" << std::endl;
while ( m_bContinueReading )
{
static unsigned char buffer[1000];
boost::system::error_code error;
size_t length = m_pSocket->read_some(boost::asio::buffer(&buffer, 1000), error);
if (!error && length != 0)
{
std::cout << "Received " << buffer << std::endl;
}
else
{
std::cout << "Received error, connection likely closed by peer" << std::endl;
break;
}
}
std::cout << "Connection closed" << std::endl;
listenForNewConnection();
}
else
{
std::cout << "Connection error" << std::endl;
}
std::cout << "Ending readSession" << std::endl;
}
void run()
{
while (m_bContinueReading)
m_pIOService->run_one();
std::cout << "Exiting run thread" << std::endl;
}
bool m_bContinueReading = true;
boost::asio::io_service* m_pIOService = NULL;
tcp::socket* m_pSocket = NULL;
tcp::acceptor* m_pAcceptor = NULL;
boost::thread* m_pThread = NULL;
int m_port;
};
int main(int argc, char* argv[])
{
TheServer* server = new TheServer(1900);
std::cout << "Press Enter to quit" << std::endl;
std::string sGot;
getline(std::cin, sGot);
delete server;
return 0;
}
Client app:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
int main(int argc, char* argv[])
{
std::cout << std::endl;
std::cout << "Starting client" << std::endl;
using boost::asio::ip::tcp;
boost::asio::io_service* m_pIOService = NULL;
tcp::socket* m_pSocket = NULL;
try
{
m_pIOService = new boost::asio::io_service;
std::stringstream sPort;
sPort << 1900;
tcp::resolver resolver(*m_pIOService);
tcp::resolver::query query(tcp::v4(), "localhost", sPort.str());
tcp::resolver::iterator iterator = resolver.resolve(query);
m_pSocket = new tcp::socket(*m_pIOService);
m_pSocket->connect(*iterator);
std::cout << "Client conected" << std::endl;
std::string hello = "Hello World";
boost::asio::write( *m_pSocket, boost::asio::buffer(hello.data(), hello.size()) );
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
hello += "(2)";
boost::asio::write(*m_pSocket, boost::asio::buffer(hello.data(), hello.size()));
}
catch (std::exception& e)
{
delete m_pSocket;
m_pSocket = NULL;
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Note that I use non-blocking async_accept to be able to cleanly stop the server when Enter is pressed.
Under Windows, it works perfectly fine, I run the server, it outputs:
Starting async_accept
Press Enter to quit
For each client app run, it outpts:
Starting client
Client conected
and server app outputs:
Connection established
Received Hello World
Received Hello World(2)
Received error, connection likely closed by peer
Connection closed
Starting async_accept
Ending readSession
Then when I press Enter in server app console, it outputs Exiting run thread and cleanly stops.
Now, when I compile this same code under Linux, the client outputs the same as under Windows, but nothing happens on the server side...
Any idea what's wrong?
There are many questionable elements.
There is a classical data race on m_bContinueReading. You write from another thread, but the other thread may never see the change because of the data race.
The second race condition is likely your problem:
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
listenForNewConnection();
Here the run thread may complete before you ever post the first work. You can use a work-guard to prevent this. In your specific code you would already fix it by reordering the lines:
listenForNewConnection();
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
I would not do this, because I would not have those statements in my constructor body. See below for the work guard solution
There is a lot of raw pointer handling and new/delete going on, which merely invites errors.
You use the buffer assuming that it is NUL-terminated. This is especially unwarranted because you use read_some which will read partial messages as they arrive on the wire.
You use a static buffer while the code may have different instances of the class. This is very false optimization. Instead, prevent all the allocations! Combining with the previous item:
char buffer[1000];
while (m_bContinueReading) {
size_t length = m_Socket.read_some(asio::buffer(&buffer, 1000), ec);
std::cout << "Received " << length << " (" << quoted(std::string(buffer, length)) << "), "
<< ec.message() << std::endl;
if (ec.failed())
break;
}
You start a new acceptor always, where there is no need: a single acceptor can accept as many connections as you wish. In fact, the method shown runs into the problems
that lingering connections can prevent the new acceptor from binding to the same port. You could also alleviate that with
m_Acceptor.set_option(tcp::acceptor::reuse_address(true));
the destroyed acceptor may have backlogged connections, which are discarded
Typically you want to support concurrent connection, so you can split of a "readSession" and immediately accept the next connection. Now, strangely your code seems to expect clients to be connected until the server is prompted to shutdown (from the console) but after that you somehow start listening to new connections (even though you know the service will be stopping, and m_bContinueReading will remain false).
In the grand scheme of things, you don't want to destroy the acceptor unless something invalidated it. In practice this is rare (e.g. on Linux the acceptor will happily survive disabling/re-enabling the network adaptor).
you have spurious explicit template arguments (bind<void>). This is an anti-pattern and may lead to subtle problems
similar with the buffer (just say asio::buffer(buffer) and no longer have correctness concerns. In fact, don't use C-style arrays:
std::array<char, 1000> buffer;
size_t n = m_Socket.read_some(asio::buffer(buffer), ec);
std::cout << "Received " << n << " " << quoted(std::string(buffer.data(), n))
<< " (" << ec.message() << ")" << std::endl;
Instead of running a manual run_one() loop (where you forget to handle exceptions), consider "just" letting the service run(). Then you can .cancel() the acceptor to let the service run out of work.
In fact, this subtlety isn't required in your code, since your code already forces "ungraceful" shutdown anyways:
m_IOService.stop(); // nuclear option
m_Thread.join();
More gentle would be e.g.
m_Acceptor.cancel();
m_Socket.cancel();
m_Thread.join();
In which case you can respond to the completion error_code == error::operation_aborted to stop the session/accept loop.
Technically, you may be able to do away with the boolean flag altogether.
I keep it because it allows us to handle multiple session-per-thread in
"fire-and-forget" manner.
In the client you have many of the same problems, and also a gotcha where
you only look at the first resolver result (assuming there was one),
ignoring the rest. You can use asio::connect instead of
m_Socket.connect to try all resolved entries
Addressing the majority of these issues, simplifying the code:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/optional.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using namespace std::chrono_literals;
using boost::system::error_code;
class TheServer {
public:
TheServer(int port) : m_port(port) {
m_Acceptor.set_option(tcp::acceptor::reuse_address(true));
do_accept();
}
~TheServer() {
m_shutdownRequested = true;
m_Work.reset(); // release the work-guard
m_Acceptor.cancel();
m_Thread.join();
}
private:
void do_accept() {
std::cout << "Starting async_accept" << std::endl;
m_Acceptor.async_accept( //
m_Socket, boost::bind(&TheServer::on_accept, this, asio::placeholders::error));
}
void on_accept(error_code ec) {
if (!ec) {
std::cout << "Connection established " << m_Socket.remote_endpoint() << std::endl;
// leave session running in the background:
std::thread(&TheServer::read_session_thread, this, std::move(m_Socket)).detach();
do_accept(); // and immediately accept new connection(s)
} else {
std::cout << "Connection error (" << ec.message() << ")" << std::endl;
std::cout << "Ending readSession" << std::endl;
}
}
void read_session_thread(tcp::socket sock) {
std::array<char, 1000> buffer;
for (error_code ec;;) {
size_t n = sock.read_some(asio::buffer(buffer), ec);
std::cout << "Received " << n << " " << quoted(std::string(buffer.data(), n)) << " ("
<< ec.message() << ")" << std::endl;
if (ec.failed() || m_shutdownRequested)
break;
}
std::cout << "Connection closed" << std::endl;
}
void thread_func() {
// http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.effect_of_exceptions_thrown_from_handlers
for (;;) {
try {
m_IOService.run();
break; // exited normally
} catch (std::exception const& e) {
std::cerr << "[eventloop] error: " << e.what();
} catch (...) {
std::cerr << "[eventloop] unexpected error";
}
}
std::cout << "Exiting service thread" << std::endl;
}
std::atomic_bool m_shutdownRequested{false};
uint16_t m_port;
asio::io_service m_IOService;
boost::optional<asio::io_service::work> m_Work{m_IOService};
tcp::socket m_Socket{m_IOService};
tcp::acceptor m_Acceptor{m_IOService, tcp::endpoint{tcp::v4(), m_port}};
std::thread m_Thread{boost::bind(&TheServer::thread_func, this)};
};
constexpr uint16_t s_port = 1900;
void run_server() {
TheServer server(s_port);
std::cout << "Press Enter to quit" << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
void run_client() {
std::cout << std::endl;
std::cout << "Starting client" << std::endl;
using asio::ip::tcp;
try {
asio::io_service m_IOService;
tcp::resolver resolver(m_IOService);
auto iterator = resolver.resolve("localhost", std::to_string(s_port));
tcp::socket m_Socket(m_IOService);
connect(m_Socket, iterator);
std::cout << "Client connected" << std::endl;
std::string hello = "Hello World";
write(m_Socket, asio::buffer(hello));
std::this_thread::sleep_for(100ms);
hello += "(2)";
write(m_Socket, asio::buffer(hello));
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
int main(int argc, char**) {
if (argc>1)
run_server();
else
run_client();
}

asio::io_context run in thread, asio::steady_timer::async_wait doesn't work

I have tried to create a server with asio, when i try to integrate a timer behind the event handler from client.
asio::io_context m_asioContext;
std::thread m_threadContext;
void print()
{
std::cout << "Hello, world!" << std::endl;
SendTimer();
}
void SendTimer()
{
asio::steady_timer timer(m_asioContext, asio::chrono::seconds(2));
timer.async_wait(boost::bind(&server_interface::print, this));
}
bool Start()
{
try
{
// Issue a task to the asio context - This is important
// as it will prime the context with "work", and stop it
// from exiting immediately. Since this is a server, we
// want it primed ready to handle clients trying to
// connect.
WaitForClientConnection();
std::cout << "[SERVER] Started!azazaz\n";
// Launch the asio context in its own thread
m_threadContext = std::thread([this]() { m_asioContext.run(); });
}
catch (std::exception& e)
{
// Something prohibited the server from listening
std::cerr << "[SERVER] Exception: " << e.what() << "\n";
return false;
}
std::cout << "[SERVER] Started!\n";
return true;
}
void Update(size_t nMaxMessages = -1, bool bWait = false)
{
if (bWait) m_qMessagesIn.wait();
// Process as many messages as you can up to the value
// specified
size_t nMessageCount = 0;
while (nMessageCount < nMaxMessages && !m_qMessagesIn.empty())
{
// Grab the front message
auto msg = m_qMessagesIn.pop_front();
// Pass to message handler
OnMessage(msg.remote, msg.msg);
nMessageCount++;
}
Update(nMaxMessages, bWait);
}
Server call
CustomServer server(60000);
server.Start();
asio::io_context io;
server.Update(-1, true);
It'seem that the timer could not run correctly. Just like the infinitive loop. I really newbie with asio. So I wonder how we could keep multi event with only a thread.
Thanks for your answer.

boost asio - io_service don't wait connection into threads

I want to create a server async with multi threads.
When I create a thread_group and waiting for some connections in asynchronous way. My program don't wait and termine immediatly.
void Server::configServer() {
_ip = boost::asio::ip::address_v4::from_string("127.0.0.1");
boost::asio::ip::tcp::resolver resolver(_io_service);
_endpoint = *resolver.resolve({tcp::v4(), _port});
std::cout << "Server address: " << _ip.to_string() << ":" << _port << std::endl;
_acceptor.close();
_acceptor.open(_endpoint.protocol());
_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
_acceptor.bind(_endpoint);
_acceptor.listen();
for (int i = 0; i < 8; ++i) {
_threads.create_thread(boost::bind(&boost::asio::io_service::run, &_io_service));
}
_threads.join_all();
std::cout << "Server is set up" << std::endl;
run();
}
void Server::run() {
Connection::pointer newConnection = Connection::create(_acceptor.get_io_service());
std::cout << "Server is running" << std::endl;
_acceptor.async_accept(newConnection->socket(),
boost::bind(&Server::handleAccept, this, newConnection,
boost::asio::placeholders::error));
}
void Server::handleAccept(Connection::pointer newConnection, const boost::system::error_code& error) {
if (!error) {
std::cout << "Reçu un client!" << std::endl;
newConnection->start();
run();
}
}
Can you tell me what am I doing wrong ?
run works as long as there are any pending tasks/handlers to be processed.
In your case you started run, then first async_ method was called. So run ends immediately due to no handlers to be called.
You should init some asynchronous task, then invoke run or use object called work guard. You didn't specify which version of Boost you use, but there are two options:
in olders io_service/io_context::work (ref)
current, executor_work_guard (ref)
In your class you can add executor_work_guard as additional member variable:
class Server {
boost::asio::io_context _io_service;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard;
Server() : ...., guard(boost::asio::make_work_guard(_io_service)) {
}
};
with this approach, run doesn't return even if there are no handlers to be processed.

C++ Wait But Allow Events To Fire

Building a SignalR C++ client using Visual Studio 2013, I am starting with the working sample code from NuGet Package Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop, source here
Reviewing the library source it seems to me the event handling processes are based on the Concurrency Runtime (pplx::task) which relies on C++11 features
void chat(const utility::string_t& name)
{
signalr::hub_connection connection{ U("https://testsite") };
auto proxy = connection.create_hub_proxy(U("ChatHub"));
proxy.on(U("broadcastMessage"), [](const web::json::value& m)
{
ucout << std::endl << m.at(0).as_string() << U(" wrote:") << m.at(1).as_string() << std::endl << U("Enter your message: ");
});
connection.start()
.then([proxy, name]()
{
for (;;)
{
utility::string_t message;
std::getline(ucin, message);
if (message == U(":q"))
{
break;
}
send_message(proxy, name, message);
}
})
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
return connection.stop();
})
.then([](pplx::task<void> stop_task)
{
try
{
stop_task.get();
ucout << U("connection stopped successfully") << std::endl;
}
catch (const std::exception &e)
{
ucout << U("exception when starting or stopping connection: ") << e.what() << std::endl;
}
}).get();
}
I want to eliminate the "user input" component; and instead quit loop when a particular "broadcastMessage" has been received.
If I replace the for loop with a sleep statement, the broadcastMessage event stops firing.
If I use the for loop without the getline, set bComplete to true when done, it works the way I want but causes high CPU usage (obviously)
for (;;)
{
if (bComplete) break;
}
Ideally I want connection to start, and then just wait until the broadcastMessage events signals to close the connection.
In addition the "chat" function shouldn't return until connection has closed.
I can see in your answer that you've already discovered Windows event objects; however, if you were looking for a C++11 platform-independent solution, consider std::condition_variable!
unsigned int accountAmount;
std::mutex mx;
std::condition_variable cv;
void depositMoney()
{
// go to the bank etc...
// wait in line...
{
std::unique_lock<std::mutex> lock(mx);
std::cout << "Depositing money" << std::endl;
accountAmount += 5000;
}
// Notify others we're finished
cv.notify_all();
}
void withdrawMoney()
{
std::unique_lock<std::mutex> lock(mx);
// Wait until we know the money is there
cv.wait(lock);
std::cout << "Withdrawing money" << std::endl;
accountAmount -= 2000;
}
int main()
{
accountAmount = 0;
std::thread deposit(&depositMoney);
std::thread withdraw(&withdrawMoney);
deposit.join();
withdraw.join();
std::cout << "All transactions processed. Final amount: " << accountAmount << std::endl;
return 0;
}
In this example we make two threads: one to deposit money into the account and one to withdraw money. Because it's possible for the thread to withdraw the money to run first, especially because there's more processing involved with depositMoney(), we need to wait until we know the money is there. We lock our thread before accessing the money, and then tell the condition_variable what we are waiting for. The condition_variable will unlock the thread, and once the money has been deposited and notify_all() is called we'll be re-awoken to finish processing our logic.
Note that it's possible to do the exact same using the Windows event objects. Instead of std::condition_variable::wait() and std::condition_variable::notify_all() you'd use SetEvent() and WaitForSingleObject(). This is platform-independent though.
I got this working using WinAPI WaitForSingleObject:
HANDLE hEvent;
void chat(const utility::string_t& name)
{
signalr::hub_connection connection{ U("https://testsite") };
auto proxy = connection.create_hub_proxy(U("ChatHub"));
proxy.on(U("broadcastMessage"), [](const web::json::value& m)
{
ucout << std::endl << m.at(0).as_string() << U(" wrote:") << m.at(1).as_string() << std::endl;
if (m.at(1).as_string() == L"quit")
{
SetEvent(hEvent);
}
});
hEvent = CreateEvent(0, TRUE, FALSE, 0);
connection.start()
.then([proxy, name]()
{
WaitForSingleObject(hEvent, INFINITE);
})
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
return connection.stop();
})
.then([](pplx::task<void> stop_task)
{
try
{
stop_task.get();
ucout << U("connection stopped successfully") << std::endl;
}
catch (const std::exception &e)
{
ucout << U("exception when starting or stopping connection: ") << e.what() << std::endl;
}`enter code here`
}).get();
}

boost::asio::deadline_timer doesn't wake up (stress scenario)

I'm using a deadline_timer as an asynchronous event and I'm running into a situation where, after some time, the thread waiting on the event never seems to be woken up (despite more calls to cancel()). I've been able to reproduce this using some sample code that I've pasted below; it's not exactly consistent but I have seen what I think is the same issue I'm experiencing.
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
int num_events = 0;
auto waiter = [&timer, &num_events](boost::asio::yield_context context) {
while (true) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 500000; ++i) {
timer.cancel();
std::cout << i << std::endl;
}
Am I doing something here that's unsupported and inadvertently hitting some race condition? The error code from the wait() never looks troublesome (even on the very last time it's woken up before it never seems to again). EDIT: I've also noticed the original bug on 3 different platforms (Windows, Mac and Linux) but the above test I've been using to reproduce has been on Windows.
The deadline_timer object is not threadsafe.
You're canceling it from another thread than the one that's posting the async_wait. This means the calls can race.
I'm not sure how this can completely inhibit the callback, in your sample. It seems to me that the program should /just/ quit because the tight loop to 500000 finishes quickly (doing many redundant cancels that never get processed, because the coroutine would e.g. not even have posted the new async_wait).
So maybe you mean, "why don't I get 500000 events".
UPDATE
After the comment, here's a trivial transformation that shows how you are gonna be fine calling members on the timer from within an actor. Note: this critically hinges on the idea that the io_service is run from a single thread only!
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
boost::asio::io_service io_service;
boost::asio::deadline_timer timer(io_service);
timer.expires_at(boost::posix_time::pos_infin);
boost::atomic_bool shutdown(false);
int num_events = 0;
auto waiter = [&timer, &num_events, &shutdown](boost::asio::yield_context context) {
while (!shutdown) {
std::cout << "waiting on event" << std::endl;
boost::system::error_code e;
timer.async_wait(context[e]);
std::cout << "got event (" << e.message() << ")" << std::endl;
++num_events;
}
};
boost::asio::spawn(io_service, std::move(waiter));
boost::thread thread(boost::bind(&boost::asio::io_service::run, &io_service));
for (auto i = 0; i < 5000; ++i) {
io_service.post([&timer, i]{
std::cout << i << std::endl;
timer.cancel();
});
}
io_service.post([&]{
shutdown = true;
timer.cancel();
});
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
Also, it looks like you just wanted to signal a background task. As given you can simplify the program like:
See it Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
using boost::thread;
using boost::asio::io_service;
int main() {
io_service svc;
int num_events = 0;
auto work = boost::make_shared<io_service::work>(svc); // keep svc running
boost::thread thread(boost::bind(&io_service::run, &svc));
for (auto i = 0; i < 500000; ++i) {
svc.post([&num_events,i]{
std::cout << "got event (" << i << ")" << std::endl;
++num_events;
});
}
work.reset();
thread.join();
std::cout << "Check: " << num_events << " events counted\n";
}
This does print all 500000 events:
got event (0)
got event (1)
got event (3)
...
got event (499998)
got event (499999)
Check: 500000 events counted