Confusion about boost::asio::io_context::run - c++

I am currently working on a project where I use the MQTT protocol for communication.
There is a Session class in a dedicated file which basically just sets up the publish handler, i.e. the callback that is invoked, when this client receives a message (the handler checks if the topic matches "ZEUXX/var", then deserialized the binary content of the frame and subsequently unsubscribes the topic):
session.hpp:
class Session
{
public:
Session()
{
comobj = MQTT_NS::make_sync_client(ioc, "localhost", "1883", MQTT_NS::protocol_version::v5);
using packet_id_t = typename std::remove_reference_t<decltype(*comobj)>::packet_id_t;
// Setup client
comobj->set_client_id(clientId);
comobj->set_clean_session(true);
/* If someone sends commands to this client */
comobj->set_v5_publish_handler( // use v5 handler
[&](MQTT_NS::optional<packet_id_t> /*packet_id*/,
MQTT_NS::publish_options pubopts,
MQTT_NS::buffer topic_name,
MQTT_NS::buffer contents,
MQTT_NS::v5::properties /*props*/) {
std::cout << "[client] publish received. "
<< " dup: " << pubopts.get_dup()
<< " qos: " << pubopts.get_qos()
<< " retain: " << pubopts.get_retain() << std::endl;
std::string_view topic = std::string_view(topic_name.data(), topic_name.size());
std::cout << " -> topic: " << topic << std::endl;
else if (topic.substr(0, 9) == "ZEUXX/var")
{
std::cout << "[client] reading variable name: " << topic.substr(10, topic.size() - 9) << std::endl;
auto result = 99; // dummy variable, normally an std::variant of float, int32_t uint8_t
// obtained by deserialzing the binary content of the frame
std::cout << comobj->unsubscribe(std::string{topic});
}
return true;
});
}
void readvar(const std::string &varname)
{
comobj->publish(serialnumber + "/read", varname, MQTT_NS::qos::at_most_once);
comobj->subscribe(serialnumber + "/var/" + varname, MQTT_NS::qos::at_most_once);
}
void couple()
{
comobj->connect();
ioc.run();
}
void decouple()
{
comobj->disconnect();
std::cout << "[client] disconnected..." << std::endl;
}
private:
std::shared_ptr<
MQTT_NS::callable_overlay<
MQTT_NS::sync_client<MQTT_NS::tcp_endpoint<as::ip::tcp::socket, as::io_context::strand>>>>
comobj;
boost::asio::io_context ioc;
};
The client is based on a boost::asio::io_context object which happens to be the origin of my confusion. In my main file I have the following code.
main.cpp:
#include "session.hpp"
int main()
{
Session session;
session.couple();
session.readvar("speedcpu");
}
Essentially, this creates an instance of the class Session and the couple member invokes the boost::asio::io_context::run member. This runs the io_context object's event processing loop and blocks the main thread, i.e. the third line in the main function will never be reached.
I would like to initiate a connection (session.couple) and subsequently do my publish and subscribe commands (session.readvar). My question is: How do I do that correctly?
Conceptionally what I aim for is best expressed by the following python-code:
client.connect("localhost", 1883)
# client.loop_forever() that's what happens at the moment, the program
# doesn't continue from here
# The process loop get's started, however it does not block the program and
# one can send publish command subsequently.
client.loop_start()
while True:
client.publish("ZEUXX/read", "testread")
time.sleep(20)
Running the io_context object in a separate thread seems not to be working the way I tried it, any suggestions on how to tackle this problem? What I tried is the following:
Adaption in session.hpp
// Adapt the couple function to run io_context in a separate thread
void couple()
{
comobj->connect();
std::thread t(boost::bind(&boost::asio::io_context::run, &ioc));
t.detach();
}
Adpations in main.cpp
int main(int argc, char** argv)
{
Session session;
session.couple();
std::cout << "successfully started io context in separate thread" << std::endl;
session.readvar("speedcpu");
}
The std::cout line is now reached, i.e. the program does not get stuck in the couple member of the class by io_context.run(). However directly after this line I get an error: "The network connection was aborted by the local system".
The interesting thing about this is that when I use t.join() instead of t.detach() then there is no error, however I have the same behavior with t.join() as when I call io_context.run() directly, namely blocking the program.

Given your comment to the existing answer:
io_context.run() never return because it never runs out of work (it is being kept alive from the MQTT server). As a result, the thread gets blocked as soon as I enter the run() method and I cannot send any publish and subscribe frames anymore. That was when I thought it would be clever to run the io_context in a separate thread to not block the main thread. However, when I detach this separate thread, the connection runs into an error, if I use join however, it works fine but the main thread gets blocked again.
I'll assume you know how to get this running successfully in a separate thread. The "problem" you're facing is that since io_context doesn't run out of work, calling thread::join will block as well, since it will wait for the thread to stop executing. The simplest solution is to call io_context::stop before the thread::join. From the official docs:
This function does not block, but instead simply signals the io_context to stop. All invocations of its run() or run_one() member functions should return as soon as possible. Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately until restart() is called.
That is, calling io_context::stop will cause the io_context::run call to return ("as soon as possible") and thus make the related thread joinable.
You will also want to save the reference to the thread somewhere (possibly as an attribute of the Session class) and only call thread::join after you've done the rest of the work (e.g. called the Session::readvar) and not from within the Session::couple.

When io_context runs out of work, it returns from run().
If you don't post any work, run() will always immediately return. Any subsequent run() also immediately returns, even if new work was posted.
To re-use io_context after it completed, use io_context.reset(). In your case, better to
use a work guard (https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/executor_work_guard.html), see many of the library examples
don't even "run" the ioc in couple() if you already run it on a background thread
If you need synchronous behaviour, don't run it on a background thread.
Also keep in mind that you need to afford graceful shutdown which is strictly harder with a detached thread - after all, now you can't join() it to know when it exited.

Related

Boost::asio::strand merges multiple handlers into one

I am currently using boost 1.70 and I was trying to implement io service loop to have a custom call between each invoked handle, and I couldn't get it to work. After some examination, I gained suspicion there are multiples handles executed in one call of "run_one" function. So I wrote a test code:
#include <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/post.hpp>
#include <thread>
#include <mutex>
class StrandPost
{
private:
boost::asio::io_service service_;
boost::asio::io_service::work work_;
boost::asio::io_service::strand strand_;
std::thread module_thread_;
void Run() {
auto run_one = [this]() {
std::cout << " ---- Running one ----" << std::endl;
auto retval = service_.run_one();
return retval;
};
while (run_one());
std::cout << " ---- Ending run ----" << std::endl;
}
public:
StrandPost()
: service_()
, work_(service_)
, strand_(service_)
, module_thread_(&StrandPost::Run, this)
{}
~StrandPost() {
service_.stop();
if (module_thread_.joinable()) {
module_thread_.join();
}
}
void PlanOutput(const std::string& string) {
boost::asio::post(strand_,[string](){
std::cout << string <<std::endl;
});
// boost::asio::post(service_,[string](){
// std::cout << string <<std::endl;
// });
}
};
} // ----- end anonymous namespace -----
int main() {
StrandPost strand;
strand.PlanOutput("First message");
strand.PlanOutput("Second message");
strand.PlanOutput("Third message");
strand.PlanOutput("Fourth message");
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
And the output of that code confirmed my theory, because it was:
---- Running one ----
First message
---- Running one ----
Second message
Third message
Fourth message
---- Running one ----
---- Ending run ----
When using "io_service" directly, it works as expected, but when using "strand", after the first handle, multiple handles are executed as one.
So, the strand effectively merged several handlers into one.
My question is:
Is this bug or is this intentional? Am I doing something wrong?
If this is a bug, is it reported? Because I could not find a mention of this anywhere.
I'm fairly certain this is intentional. The strand itself is a queue of jobs, that only one thread at a time can perform.
When io_service::run_one runs, it causes the thread to run the strand ready queue. I believe the 'only one once' logic isn't passed through to the strand's processing loop. Think of it it this way, the io_service is told to do one handler, but the strand's handler runs several jobs in sequence before returning.
The best fix for your issue is, if you're going to have your own io_service in your class, is don't use the strand at all, and post directly to the io_service. Then you'll have the behavior you desire.
This is, indeed, as intended. The strand_executor_service pops all ready handlers on the same strand:
void strand_executor_service::run_ready_handlers(implementation_type& impl)
{
// Indicate that this strand is executing on the current thread.
call_stack<strand_impl>::context ctx(impl.get());
// Run all ready handlers. No lock is required since the ready queue is
// accessed only within the strand.
boost::system::error_code ec;
while (scheduler_operation* o = impl->ready_queue_.front())
{
impl->ready_queue_.pop();
o->complete(impl.get(), ec, 0);
}
}
It is quite obvious that this can have a great performance improving impact.
Well, its not that easy, since I also need to be guaranteed that handles posted for execution from a given thread will be executed in the order of posting. Preserving order between posts from different threads is irrelevant, however order of posts from a given thread must be preserved, and as far as I know, "io_service" does not guarantee this. But thanks for the answer, looking further into the boost implementation, it looks you are completely right. –
TStancek
6 hours ago
io_service does have the ordering guarantees of a strand (in fact, the strand's guarantees derive from that). In your case, there is - by definition - only one thread, so everything on the service will be in an implicit strand (see Why do I need strand per connection when using boost::asio?).
Summary
You can do without the strand for the example code in your question.
If your situation is more involved and you need the one-by-one message processing control, you would do better to have a task queue that implements this explicitly, instead of depending on implementation details.

Thread crashing when trying to join

Update
I did as recommended to create a std::vector of threads outside the scope, so I can .join() as soon as the thread has finished it's job, the problem now is that as soon as the thread is joined the program not exactly crashes, because it still runs in the background but the abort window appears. I checked if the thread was joinable and indeed it is when trying to join.
Timer.cpp:
void Timer::Start(int time, void(*lf)()) {
slaveTimer = std::thread(&Timer::RunTimer, this, time, lf);
}
void Timer::RunTimer(int seconds, void(*lf)()) {
auto time = (std::chrono::seconds)seconds;
std::this_thread::sleep_for(time);
lf();
slaveTimer.join(); //Program Crashes
}
Main.cpp
Timer timer1(10, [](){ std::cout << "Hello World" << std::endl; });
Original Post
I was trying to make coroutines with multithreading, the thing is that when I try to make the thread wait for X seconds, i then thread.detach(); but that takes a couple of milliseconds and the screen (because I’m displaying with GL) freezes. One of the possible solutions that I can think of is making the thread detach itself before executing the action, but that doesn’t seem possible, so I was wondering if there is any way to do that or something similar to solve this problem.
You cannot call join from the function which is the body of execution thread. It will give you the error:
Reference
Error Conditions :
resource_deadlock_would_occur if this->get_id() ==
std::this_thread::get_id() (deadlock detected)
you need to add additional method for instance
void Timer::stop() {
slaveTimer.join();
}
and call this method from thread which created timer1 instance
Timer timer1(10, [](){ std::cout << "Hello World" << std::endl; });
timer1.stop();
or join thread in dtor of Timer:
Timer::~Timer() {
slaveTimer.join();
}

Threading In Classes

I am creating an asynchronous class that logs strings into a file. Should I be creating the thread within the class itself? I was thinking something like this as a start function
void Async_Log::start (void)
{
std::thread thread_log(
[&]()
{
std::ofstream fout;
fout.open(fileName);
while(true)
{
if(q.size())
{
std::lock_guard<std::mutex> lock(m);
fout << q.front() << "\t At Time: " << std::clock() << std::endl;
q.pop();
}
}
fout.close();
});
}
Or would it be better to leave the threading up to main. My first concern is if threading is unique (so if I instantiate the class 2 times with two different files will the thread_log be over written or have a conflict).
There is nothing wrong to have a dedicated thread in the class, but I want to note several things:
Inside your thread you implement busy waiting for log messages. This is completely redundant and very expensive! Your thread consumes CPU even when there are no messages in the queue. What you need is blocking queue there, that would block on pop() method. You can find implementation of the blocking queue for C++ here or here.
There is need to provide possibilty to terminate your logging thread. This you can do eigher by having 'terminate' variable that you check in the loop, or by sending special 'poison pill' message to the logger.

How to make boost threads self-destruct? (C++)

I have a class with some function like:
void workerFunc(int ClassVariable)
{
boost::posix_time::seconds workTime(classVariableA);
std::cout << "Worker: running" << std::endl;
// Pretend to do something useful...
boost::this_thread::sleep(workTime);
std::cout << ClassVariable << std::endl;
std::cout << "Worker: finished" << std::endl;
}
which I want to be in threads. and some other function that I want to work like
while(1)
{
boost::thread workerThread(workerFunc(ClassVariableB));
}
so it will create thread each time it can. But what I need is for that thread to auto destruct itself when it is finished. How to do such thing?
You do not have to do anything for that. You just have to make sure that the thread really finishes (i.e. no infinite loops or such).
A thread will automatically end when it finishes running the function for which it was created.
join is a strange word, it really means wait_for which means wait for the thread to finish executing.
If you want to retain a thread for re-use, it is normally implemented by making its function loop, each time reaching a "wait" state, where it gets alerted (woken up) when something it is waiting for happens. At some point this will be a termination request, or a request that leads to its termination.

how a thread can signal when it's finished?

#include <iostream>
#include <boost/thread.hpp>
using std::endl; using std::cout;
using namespace boost;
mutex running_mutex;
struct dostuff
{
volatile bool running;
dostuff() : running(true) {}
void operator()(int x)
{
cout << "dostuff beginning " << x << endl;
this_thread::sleep(posix_time::seconds(2));
cout << "dostuff is done doing stuff" << endl;
mutex::scoped_lock running_lock(running_mutex);
running = false;
}
};
bool is_running(dostuff& doer)
{
mutex::scoped_lock running_lock(running_mutex);
return doer.running;
}
int main()
{
cout << "Begin.." << endl;
dostuff doer;
thread t(doer, 4);
if (is_running(doer)) cout << "Cool, it's running.\n";
this_thread::sleep(posix_time::seconds(3));
if (!is_running(doer)) cout << "Cool, it's done now.\n";
else cout << "still running? why\n"; // This happens! :(
return 0;
}
Why is the output of the above program:
Begin..
Cool, it's running.
dostuff beginning 4
dostuff is done doing stuff
still running? why
How can dostuff correctly flag when it is done? I do not want to sit around waiting for it, I just want to be notified when it's done.
The problem in this example is that there are two instances of dostuff, so the version being set to false in operator() is different then the one in main.
From the thread management documentation:
A new thread is launched by passing an object of a callable type that can be invoked with no parameters to the constructor. The object is then copied into internal storage, and invoked on the newly-created thread of execution. If the object must not (or cannot) be copied, then boost::ref can be used to pass in a reference to the function object. In this case, the user of Boost.Thread must ensure that the referred-to object outlives the newly-created thread of execution.
If you don't want to copy the object, use boost::ref:
thread t(boost::ref(doer), 4);
You can't assume the thread will be finished just by sleeping.
You can call join on the thread. This will wait until the thread is done and then resume flow.
For advanced notifying between threads of a certain event happening you can use boost condition.
I'm guessing your problem is actually a bug in your code. From the Boost documentation for thread:
Thread Constructor with arguments
template <class F,class A1,class A2,...>
thread(F f,A1 a1,A2 a2,...);
Preconditions:
F and each An must by copyable or movable.
Effects:
As if thread(boost::bind(f,a1,a2,...)). Consequently, f and each an are copied into internal storage for access by the new thread.
So, I think the thread is modifying its own copy of doer, and not the object whose runnable state you're checking.
The real question isn't how the dostuff thread should send the signal, but rather how the main thread should receive the signal. My favorite method is to use socketpair() to create a local socket connection and then give one socket to the child thread and the other socket to the main thread. The two threads can then use the socket-connection to communicate with each other. In your case, all you would need is for the child thread to send a byte on the socket (or just close its socket file descriptor) just before it exits, and that would be enough to break the main thread out of select() or poll() or whatever it is blocking in and let it know that the child thread has finished its task.
Note that the main thread should still call join() on the child thread's thread-ID (after it receives the child-going-away signal), to make sure that the child thread is really really dead, before freeing any resources... otherwise you risk a race condition of the main thread freeing a resource after the child thread has signalled but before the thread-cleanup routines have completed.