In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()),
this, SLOT(processMessage()));
Where lcmhandler.h is:
class LCMHandler : public QObject
{
Q_OBJECT
public:
static LCMHandler* getInstance();
LCMHandler();
~LCMHandler() {}
void handleMessage(const lcm::ReceiveBuffer* rbuf,
const std::string &chan,
const example::example_t *msg);
signals:
void messageReceived();
private:
static LCMReceiver* _instance;
};
And lcmhandler.cpp is:
LCMHandler* LCMHandler::_instance = 0;
LCMHandler::LCMHandler()
{
lcm::LCM lcm;
if(lcm.good())
{
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
while(0 == lcm.handle());
} else {
std::cerr << "LCM Error" << std::endl;
}
}
LCMHandler* LCMHandler::getInstance() {
if (!_instance) {
_instance = new LCMHandler();
}
return _instance;
}
void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf,
const std::string &chan,
const hlelcm::transponder_t *msg)
{
std::cout << "Received message on channel " << chan.c_str() << std::endl;
emit messageReceived();
}
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's processMessage(), presumably because the application gets stuck looping on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() is:
void Consumer::processMessage() {
std::cout << "Message received" << std::endl;
}
It never executes, while handleMessage(...) loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.
What is the best way to handle the incoming messages? Should I refrain from using a singleton for LCMHandler? What do I need to change to make this implementation work?
Move the contents of your LCM constructor to another function:
LCMHandler::beginCommunication()
{
lcm::LCM lcm;
if(lcm.good())
{
//QObject base class call.
moveToThread( &_myLocalQThread );
_myLocalThread.start();
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
_isActive = true;
// This is blocking, we don't want it to interfere with
// the QApplication loop
while(0 == lcm.handle());
}
else
{
std::cerr << "LCM Error" << std::endl;
}
_isActive = false;
}
Then something along these lines to allow your LCM loop to happen in another thread.
auto lcmHandler = LCMHandler::getInstance();
// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
this, &Consumer::processMessage, Qt::QueuedConnection );
// Add a std::atomic< bool > _isActive to LCMHandler
if( not lcmHandler.isActive() )
{
lcmHandler.beginCommunication();
}
And then make sure to properly close your QThread in the destructor.
LCMHandler::~LCMHandler()
{
_myLocalQThread.quit();
_myLocalQThread.wait();
}
Related
Current Scheme
I am developing a Serial Port routine that will regard current receive transfer is complete if no new data is received for 25 milli-seconds. I start the timer on the first the read_handler (Boost ASIO callback method) call. For every new read_handler call, I cancel the asynchronous operations that are waiting on the timer and create a new asynchronous operations on the timer.
Problem
The problem I am facing is that randomly my receive transfer that was suppose to be 1 transfer is being treated as 2 separate transfer as receive_timeout event (receive_timeout_handler) is being triggered (called) multiple times.
I'm not sure is this because of my incorrect implementation/usage of Boost ASIO system_timer or due to Driver issue in my USB to Serial Converter.
I'm currently using FT4232 module (contains 4 UART/Serial Port) to test my routines whereby I send data from send data (4 K.B. text file) from UART1 and receive data on UART0.
I expect that only after receiving all 4 K.B. of data, the serial port class signal main thread however sometimes this one 4 K.B. transfer is signaled 2-3 times.
Code :
class SerialPort
{
public:
SerialPort() : io(), port(io), receive_timeout_timer(io)
bool open_port(void);
bool read_async(std::int32_t read_timeout = -1)
void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred);
void receive_timeout_handler(const boost::system::error_code& error);
private:
boost::asio::io_context io;
boost::asio::serial_port port;
boost::asio::system_timer receive_timeout_timer {25};
std::array<std::byte, 8096> read_byte_buffer;
};
bool SerialPort::open_port(void)
{
try
{
this->port.open("COM3");
return true;
}
catch (const std::exception& ex)
{
}
return false;
}
bool SerialPort::read_async(std::uint32_t read_timeout)
{
try
{
this->read_byte_buffer.fill(static_cast<std::byte>(0)); //Clear Buffer
if (read_timeout not_eq -1)
{
this->read_timeout = read_timeout;//If read_timeout is not set to ignore_timeout, update the read_timeout else use old read_timeout
}
this->port.async_read_some(
boost::asio::buffer(
this->read_byte_buffer.data(),
this->read_byte_buffer.size()
),
boost::bind(
&SerialPort::read_handler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
return true;
}
catch (const std::exception& ex)
{
return false;
}
}
void SerialPort::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
std::string temporary_recieve_data;
try
{
if (error not_eq boost::system::errc::success) //Error in serial port read
{
return;
}
std::transform(this->read_byte_buffer.begin(), this->read_byte_buffer.begin() + bytes_transferred,
std::back_inserter(temporary_recieve_data), [](std::byte character) {
return static_cast<char>(character);
}
);
this->read_async(); //Again Start the read operation
this->received_data += temporary_recieve_data;
this->receive_timeout_timer.cancel(); // Cancel existing timers if any are running
this->receive_timeout_timer.expires_after(boost::asio::chrono::milliseconds(SerialPort::bulk_data_receive_complete)); // Reset timer to current timestamp + 25 milliseconds
this->receive_timeout_timer.async_wait(boost::bind(&SerialPort::receive_timeout_handler, this, boost::asio::placeholders::error));
}
catch (const std::exception& ex)
{
}
}
void SerialPort::receive_timeout_handler(const boost::system::error_code& error)
{
try
{
if (error not_eq boost::system::errc::success) //Error in serial port read
{
return;
}
// this->signal(this->port_number, SerialPortEvents::read_data, this->received_data); //Signal to main thread that data has been received
}
catch (const std::exception& ex)
{
}
}
read_timer.cancel(); // Cancel existing timers if any are running
read_timer.expires_after(
SerialPort::bulk_data_receive_complete); // Reset timer to current timestamp + 25 milliseconds
Here the cancel is redundant, because setting the expiration cancels any pending wait.
You reschedule the timer regardless of whether it ran out. Your code misses the possibility that both the read and timer could have completed successfully. In that case your main gets signaled multiple times, even though it only "nearly" exceeded 25ms idle.
You would expect to see partially duplicated data, then, because received_data isn't cleared.
To clearly see what is going on, build your code with -DBOOST_ASIO_ENABLE_HANDLER_TRACKING=1 and run the output through handler_viz.pl (see also Cancelling boost asio deadline timer safely).
Suggestions
You could probably avoid the double firing by being explicit about the flow:
To achieve that, only cancel the read from the timeout handler:
void SerialPort::receive_timeout_handler(error_code ec) {
if (!ec.failed()) {
port.cancel(ec);
std::cerr << "read canceled: " << ec.message() << std::endl;
}
}
Then you could move the signal to the read-handler, where you expect the cancellation:
void SerialPort::read_handler(error_code ec, size_t bytes_transferred) {
if (ec == asio::error::operation_aborted) {
signal(port_number, SerialPortEvents::read_data, std::move(received_data));
} else if (ec.failed()) {
std::cerr << "SerialPort read: " << ec.message() << std::endl;
} else {
copy_n(begin(read_buffer), bytes_transferred, back_inserter(received_data));
read_timer.expires_after(bulk_data_receive_complete); // reset timer
read_timer.async_wait(boost::bind(&SerialPort::receive_timeout_handler, this, ph::error));
start_async_read(); // continue reading
}
}
To be completely fool-proof, you can check that the timer wasn't actually expired even on successful read (see again Cancelling boost asio deadline timer safely).
Intuitively, I think it makes more even sense to schedule the timer from start_async_read.
ASIDE #1
Currently your code completely ignores read_timeout (even aside from the unnecessary confusion between the argument read_timeout and the member read_timeout). It is unclear to me whether you want the read_timeout override argument to "stick" for the entire chain of read operations.
If you want it to stick, change the
start_async_read(bulk_data_receive_complete); // continue reading
call to
start_async_read(); // continue reading
below. I kept it like it is because it allows for easier timing demonstrations
ASIDE #2
I've undone the exception swallowing code. Instead of just squashing all exceptions into a boolean (which you'll then check to change control flow), use the native language feature to change the control flow, retaining error information.
Full Demo
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/signals2.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
namespace ph = boost::asio::placeholders;
using boost::system::error_code;
using namespace std::chrono_literals;
enum class SerialPortEvents { read_data };
class SerialPort {
using duration = std::chrono::system_clock::duration;
static constexpr duration //
ignore_timeout = duration::min(), // e.g. -0x8000000000000000ns
bulk_data_receive_complete = 25ms;
public:
SerialPort() : io(), port(io), read_timer(io) {}
void open_port(std::string device);
void start_async_read(duration read_timeout = ignore_timeout);
void run() {
if (io.stopped())
io.restart();
io.run();
}
boost::signals2::signal<void(unsigned, SerialPortEvents, std::string)> signal;
private:
void read_handler(error_code ec, size_t bytes_transferred);
void receive_timeout_handler(error_code ec);
duration read_timeout = bulk_data_receive_complete;
asio::io_context io;
asio::serial_port port;
asio::system_timer read_timer;
std::array<char, 8096> read_buffer;
std::string received_data;
// TODO
unsigned const port_number = 0;
};
void SerialPort::open_port(std::string device) { port.open(device); }
void SerialPort::start_async_read(duration timeout_override) {
read_buffer.fill(0); // Clear Buffer (TODO redundant)
if (timeout_override != ignore_timeout)
read_timeout = timeout_override;
std::cerr << "Expiry: " << read_timeout/1.s << "s from now" << std::endl;
read_timer.expires_after(read_timeout); // reset timer
read_timer.async_wait(boost::bind(&SerialPort::receive_timeout_handler, this, ph::error));
port.async_read_some( //
boost::asio::buffer(read_buffer),
boost::bind(&SerialPort::read_handler, this, ph::error, ph::bytes_transferred));
}
void SerialPort::read_handler(error_code ec, size_t bytes_transferred) {
if (ec == asio::error::operation_aborted) {
signal(port_number, SerialPortEvents::read_data, std::move(received_data));
} else if (ec.failed()) {
std::cerr << "SerialPort read: " << ec.message() << std::endl;
} else {
copy_n(begin(read_buffer), bytes_transferred, back_inserter(received_data));
start_async_read(bulk_data_receive_complete); // continue reading
}
}
void SerialPort::receive_timeout_handler(error_code ec) {
if (!ec.failed()) {
port.cancel(ec);
std::cerr << "read canceled: " << ec.message() << std::endl;
}
}
int main(int argc, char** argv) {
SerialPort sp;
sp.open_port(argc > 1 ? argv[1] : "COM3");
int count = 0;
sp.signal.connect([&count](unsigned port, SerialPortEvents event, std::string data) {
assert(port == 0);
assert(event == SerialPortEvents::read_data);
std::cout << "data #" << ++count << ": " << std::quoted(data) << "\n----" << std::endl;
});
sp.start_async_read(10s);
sp.run();
sp.start_async_read();
sp.run();
}
Testing with
socat -d -d pty,raw,echo=0 pty,raw,echo=0
./build/sotest /dev/pts/7
And various device emulations:
for a in hello world bye world; do sleep .01; echo "$a"; done >> /dev/pts/9
for a in hello world bye world; do sleep .025; echo "$a"; done >> /dev/pts/9
for a in hello world bye world; do sleep 1.0; echo "$a"; done >> /dev/pts/9
cat /etc/dictionaries-common/words >> /dev/pts/9
You can see all the outputs match with the expectations. With the sleep .025 you can see the input split over two read operations, but never with repeated data.
Handler tracking for the various runs: 1. 2.
3. 4.
The last one (literally throwing the dictionary at it) is way too big to be useful: https://imgur.com/a/I5lHnCV
Simplifying Notes
Note that your entire SerialPort re-implements a composed read operation. You might use simplify all that to asio::async_read_until with a MatchCondition.
This has the benefit of allowing directly asio::dynamic_buffer(received_data) as well.
Here's a simpler version that doesn't use a timer, but instead updates the deadline inside the manual run() loop.
It uses a single composed read operation with a MatchCondition that checks when the connection is "idle".
Live On Coliru
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
enum class SerialPortEvents { read_data };
class SerialPort {
using Clock = std::chrono::system_clock;
using Duration = Clock::duration;
static constexpr Duration default_idle_timeout = 25ms;
public:
void open_port(std::string device);
void read_till_idle(Duration idle_timeout = default_idle_timeout);
std::function<void(unsigned, SerialPortEvents, std::string)> signal;
private:
asio::io_context io;
asio::serial_port port{io};
std::string received_data;
};
void SerialPort::open_port(std::string device) { port.open(device); }
namespace {
// Asio requires nested result_type to be MatchCondition... :(
template <typename F> struct AsMatchCondition {
using CBT = boost::asio::dynamic_string_buffer<char, std::char_traits<char>,
std::allocator<char>>::const_buffers_type;
using It = asio::buffers_iterator<CBT>;
using result_type = std::pair<It, bool>;
F _f;
AsMatchCondition(F f) : _f(std::move(f)) {}
auto operator()(It f, It l) const { return _f(f, l); }
};
}
void SerialPort::read_till_idle(Duration idle_timeout) {
if (io.stopped())
io.restart();
using T = Clock::time_point;
T start = Clock::now();
auto current_timeout = idle_timeout;
auto deadline = T::max();
auto is_idle = [&](T& new_now) { // atomic w.r.t. a new_now
new_now = Clock::now();
return new_now >= deadline;
};
auto update = [&](int invocation) {
auto previous = start;
bool idle = is_idle(start);
if (invocation > 0) {
current_timeout = default_idle_timeout; // or not, your choice
std::cerr << " [update deadline for current timeout:" << current_timeout / 1ms << "ms after "
<< (start - previous) / 1ms << "ms]" << std::endl;
}
deadline = start + current_timeout;
return idle;
};
int invocation = 0; // to avoid updating current_timeout on first invocation
auto condition = AsMatchCondition([&](auto, auto e) { return std::pair(e, update(invocation++)); });
async_read_until(port, asio::dynamic_buffer(received_data), condition,
[this](auto...) { signal(0, SerialPortEvents::read_data, std::move(received_data)); });
for (T t; !io.stopped(); io.run_for(5ms))
if (is_idle(t))
port.cancel();
}
void data_received(unsigned port, SerialPortEvents event, std::string data) {
static int count = 0;
assert(port == 0);
assert(event == SerialPortEvents::read_data);
std::cout << "data #" << ++count << ": " << std::quoted(data) << std::endl;
}
int main(int argc, char** argv) {
SerialPort sp;
sp.signal = data_received;
sp.open_port(argc > 1 ? argv[1] : "COM3");
sp.read_till_idle(3s);
}
Same local demos:
I have built a Qt (5.7) application that interfaces with the Windows Media Player COM API to parse track meta data. For some in-explicit reason my application is crashing when calling into IWMPMedia3::getAttributeCountByType.
The line where the crash keeps occurring is:
if (pMedia3Item && pMedia3Item->getAttributeCountByType(L"TrackingID", 0, &l) == S_OK) //Will eventually crash here
It does not crash on the first instance, it takes a couple of hundred loops and seems to be connected to when the call repeatedly returns 0. If I switch to an attribute that I know exists then it runs fine.
What is posted below is the object stripped right back, but it still crashes. This object is designed to run in its own QThread and all the COM symbols are defined and contained within the QThread.
The code forms part of a larger app that makes use of many other Qt modules, GUI, web engine being two of the biggest.
#include "WMPMLImport.h"
#include <QDebug>
#include "Wininet.h"
#define WMP_CLSID L"{6BF52A52-394A-11d3-B153-00C04F79FAA6}"
#define WMP_REFIID L"{D84CCA99-CCE2-11d2-9ECC-0000F8085981}"
#define WMP_PL_ALL_MUSIC L"All Music"
#define SAFE_RELEASE(ptr) if(NULL!=(ptr)){(ptr)->Release();ptr=NULL;}
#define BSTR_RELEASE(bstr) {SysFreeString(bstr);(bstr)=NULL;}
class CCoInitialize
{
public:
CCoInitialize() :
m_hr(CoInitialize(NULL))
{}
~CCoInitialize()
{
if(SUCCEEDED(m_hr)) {
qDebug() << "CCoInitialize: DTOR";
CoUninitialize();
}
}
HRESULT m_hr;
};
/**
Worker class that will step through the WMP COM interface
and extact the audio meta data contained within it,
*/
class WMPMLComHandler : public QObject
{
public:
WMPMLComHandler(WMPMLImport* t) :
m_thread(t)
{
qDebug() << "WMPMLComHandler::CTOR" << QThread::currentThreadId();
}
~WMPMLComHandler()
{
qDebug() << "WMPMLComHandler::DTOR" << QThread::currentThreadId();
}
/**
Method responsible for walking through the COM interface and extracting
all the track and playlist metadata including the artwork.
#returns Return false if the COM API was not parsed correctly.
*/
bool parse()
{
bool b = true;
CCoInitialize cCoInit;
IWMPCore* pIWMPCore = NULL;
IWMPCore3* pIWMPCore3 = NULL;
IWMPPlaylistCollection *pPlaylistCollection;
IWMPPlaylist *pMainLibplaylist;
CLSID clsID;
CLSID refID;
CLSIDFromString(WMP_CLSID, &clsID);
CLSIDFromString(WMP_REFIID, &refID);
if(SUCCEEDED(CoCreateInstance(clsID, NULL, CLSCTX_ALL, refID, (void**)&pIWMPCore)))
{
if(SUCCEEDED(pIWMPCore->get_playlistCollection(&pPlaylistCollection)))
{
if(SUCCEEDED(pIWMPCore->QueryInterface(__uuidof(IWMPCore3), reinterpret_cast<void**>(&pIWMPCore3))))
{
IWMPPlaylistArray* pPlaylistArray = NULL;
if(SUCCEEDED(pPlaylistCollection->getAll(&pPlaylistArray)))
{
long playlistCount = 0;
if(SUCCEEDED(pPlaylistArray->get_count(&playlistCount)))
{
IWMPPlaylist* pPlaylist = NULL;
for(int playlistIndex=0; playlistIndex < playlistCount; playlistIndex++)
{
if (SUCCEEDED(pPlaylistArray->item(playlistIndex, &pPlaylist)))
{
long lMediaCount = 0;
if (SUCCEEDED(pPlaylist->get_count(&lMediaCount)))
{
fetchPlaylist(pPlaylist, NULL, playlistIndex, lMediaCount);
}
SAFE_RELEASE(pPlaylist);
}
}
}
SAFE_RELEASE(pPlaylistArray);
}
SAFE_RELEASE(pIWMPCore3);
}
SAFE_RELEASE(pPlaylistCollection);
}
SAFE_RELEASE(pIWMPCore);
}
SAFE_RELEASE(pMainLibplaylist);
return b;
}
private:
void fetchPlaylist(IWMPPlaylist* pPlaylist, BSTR bstrName, unsigned int playlistIndex, long count)
{
//get the playlist items
for(long mediaIndex=0; mediaIndex<count-1; mediaIndex++)
{
IWMPMedia* pMediaItem = NULL;
if(SUCCEEDED(pPlaylist->get_item(mediaIndex, &pMediaItem)))
{
IWMPMedia3 *pMedia3Item = NULL;
if (pMediaItem->QueryInterface(__uuidof(IWMPMedia3), reinterpret_cast<void **>(&pMedia3Item)) == S_OK)
{
long l = 0;
qDebug() << "About to call method for" << mediaIndex << "time";
if (pMedia3Item && pMedia3Item->getAttributeCountByType(L"TrackingID", 0, &l) == S_OK) //Will eventually crash here
{
qDebug() << "Exited method for" << mediaIndex << "time";
}
}
SAFE_RELEASE(pMedia3Item);
SAFE_RELEASE(pMediaItem);
}
}
qDebug() << "*********COMPLETE*********";
}
WMPMLImport* m_thread;
};
//==
WMPMLImport::WMPMLImport() :
m_comHandler(NULL)
{
qDebug() << "WMPMLImporter CTOR" << QThread::currentThreadId();
}
WMPMLImport::~WMPMLImport()
{
}
/**
Reimplemented function that runs the function contents in a new thread context.
The parsing of the COM is all ran through this thread. Returning out of this
thread will end its execution.
*/
void WMPMLImport::run()
{
QMutexLocker g(&m_lock);
m_comHandler = new WMPMLComHandler(this);
g.unlock();
bool parseOk = m_comHandler->parse();
g.relock();
delete m_comHandler;
m_comHandler = NULL;
}
I have a problem, that remaining sent signals are not received after calling quit on QThread object.
The scenario contains 2 additional threads (QThread and std::thread) and the main execution thread. Let's call the QThread Q, the std::thread T and the main thread M.
In M I create Q, the Receiver-object R "living" in Q and the Sender-object S. Also a std::thread T is created executing a bunch if emits with S.
class Sender : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Sent;
Sender()
{
}
public slots:
signals:
void signal(int i);
public:
void send(int i)
{
m_Sent.emplace_back(i);
emit signal(i);
}
};
class Receiver : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Received;
Receiver()
{
}
void Connect(Sender* s)
{
connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
}
void Disconnect(Sender* s)
{
disconnect(s, &Sender::signal, this, &Receiver::slot);
}
public slots:
void slot(int i)
{
m_Received.emplace_back(i);
}
};
void main(int argc, char** argv)
{
QApplication app(argc, argv);
qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
std::cout << "Setting random seed " << random_seed << "\n";
std::srand(random_seed);
std::unique_ptr<Receiver> R(new Receiver);
std::unique_ptr<Sender> S(new Sender);
auto actions = [&S]() {
int i = 0;
std::chrono::steady_clock::time_point current =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point finish =
current + std::chrono::milliseconds(100);
while (current < finish)
{
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
current = std::chrono::steady_clock::now();
}
};
std::unique_ptr<QThread> Q(new QThread());
R->moveToThread(Q.get());
R->Connect(S.get());
Q->start();
std::thread T(actions);
T.join();
// approach 1:
QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
Q->wait(); // never returns
// approach 2:
Q->quit();
Q->wait(); // missing events
std::cout << "Sent: ";
for(auto v : S->m_Sent)
{
std::cout << v << " ";
}
std::cout << std::endl;
std::cout << "Received: ";
for(auto v : R->m_Received)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
I'm working on Windows with VS2013 and Qt 5.5.1. I tested it with kind of counter in R to track received signals. While debugging I went through all emits so all should be inserted to event loop in Q. After Q.wait() the counter for the slots do not correspond to the emitted signals. I would have expected the event loop with remaining input events was handled by Q.quit() or Q.wait() but seems not so, It's always that there is a cut of "event-stream" from a certain point onward. I tried now for 4 days going through Qt-Docu and several other stuff found by google, but no proposal worked so far.
I am not 100% sure since the documentation is not crystal clear, but what makes you think that the even loop is processing all pending events before exiting ? My assumption would be that there is a check "should I exit" at every loop and that it can discard some pending events when the exit flag is set.
In order to summarize the discussion below, I would suggest to add a new signal that you emit from wherever you want (say for instance from the std::thread once you have emitted everything you wanted) that would get into the QThread event loop queue and be connected to the QThread quit method so that the thread exits when being processed.
You can also avoid defining a new signal if you want.
Your code would look like (not tested):
Sender S = new Sender();
QThread Q = new QThread();
Receiver R = new Receiver();
R->moveToThread(Q);
connect(S, &Sender::signal, R, &Receiver::slot, Qt::QueuedConnection);
Q->start();
while(!Q.isRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::thread T([&S](){
emit S->signal(); // only an example, several other connects are used too
})
T.join();
QMetaObject::invokeMethod(Q, "quit",
Qt::QueuedConnection);
Q.wait();
Is there a way to print the signals and slots being called?
I'm experiencing a weird deadlock in Qt which happens only in particular conditions across multiple threads, and I'd like to know the order of signals/slots being called.
Of course, for slots, I write the method body, and in the worst scenario, I can manually add a print out of the method. But the body of signals is generated automatically, so it is not possible, unless I write a custom moc, which seems like an overkill for this task...
If one leverages the built-in hooks, it's possible to automatically instrument all signals, and all slots connected using the Qt 4 connect syntax. Unfortunately, QtPrivate::QSlotObject doesn't implement these hooks: slots connected using the Qt 5 syntax need to be instrumented manually (e.g. by connecting a functor to them, or adding code to them).
Signal notifications can be relied on for connected signals. Objects with no signals, and some signals of objects with other connections, will not be reported. This is presumably what you want.
Thus:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
Output:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot
I have a test class like this. What I want to do is to keep running the three timers in this object. But after I instantiate an object, some timer just keeps repeating but others will disappear after like 3 mins. Can anyone explain this for me?
class EventProcessor
{
private:
boost::asio::deadline_timer* m_Timer0;
boost::asio::deadline_timer* m_Timer1;
boost::asio::deadline_timer* m_Timer2;
boost::asio::io_service io0;
boost::asio::io_service io1;
boost::asio::io_service io2;
int TimerInterval[3];
boost::asio::deadline_timer* Timers[3];
public:
EventProcessor(int p_0, int p_1, int p_2)
{
TimerInterval[0] = p_0;
TimerInterval[1] = p_1;
TimerInterval[2] = p_2;
m_Timer0= new boost::asio::deadline_timer(io0, boost::posix_time::seconds(TimerInterval[0]));
Timers[0] = m_Timer0;
m_Timer1 = new boost::asio::deadline_timer(io1, boost::posix_time::seconds(TimerInterval[1]));
Timers[1] = m_Timer1;
m_Timer2 = new boost::asio::deadline_timer(io2, boost::posix_time::seconds(TimerInterval[2]));
Timers[2] = m_Timer2;
m_Timer0->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 0));
m_Timer1->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 1));
m_Timer2->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 2));
StartWithNewThread(0);
StartWithNewThread(1);
StartWithNewThread(2);
}
private:
void HandleExpire(const boost::system::error_code& p_ec, int p_TimerIndex)
{
if(p_ec == boost::asio::error::operation_aborted)
{
std::cout << "Timer" << p_TimerIndex << " canceled" << std::endl;
return;
}
std::cout << "Timer" << p_TimerIndex << " expired" << std::endl;
//Reset(p_OriginalTimer, TimerInterval[p_TimerIndex], p_TimerIndex);
boost::thread Thread(boost::bind(&EventProcessor::Reset, this, p_TimerIndex, TimerInterval[p_TimerIndex]));
}
void Start(int p_Index)
{
boost::asio::io_service& UnderlyingIO = Timers[p_Index]->get_io_service();
UnderlyingIO.reset();
UnderlyingIO.run();
UnderlyingIO.stop();
return;
}
void StartWithNewThread(int p_Index)
{
boost::thread Thread(boost::bind(&EventProcessor::Start, this, p_Index));
std::cout << Thread.get_id() << "<->" << "Timer" << p_Index << std::endl;
return;
}
public:
void Reset(int p_Index, int p_Seconds)
{
Timers[p_Index]->cancel();
Timers[p_Index]->expires_from_now(boost::posix_time::time_duration(0,0,p_Seconds,0));
TimerInterval[p_Index] = p_Seconds;
Timers[p_Index]->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, p_Index));
boost::asio::io_service& UnderlyingIO = Timers[p_Index]->get_io_service();
UnderlyingIO.reset();
UnderlyingIO.run();
UnderlyingIO.stop();
return;
}
};
So this is how you should do it:
#include "test.h"
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
class EventProcessor
{
private:
std::unique_ptr<boost::asio::deadline_timer> m_Timers[3];
boost::asio::io_service service;
boost::atomic<int> TimerInterval[3];
public:
EventProcessor(int time0,int time1, int time2)
{
TimerInterval[0] = time0;
TimerInterval[1] = time1;
TimerInterval[2] = time2;
for (int i = 0; i < 3; i++)
{
m_Timers[i].reset(
new boost::asio::deadline_timer(service));
}
}
~EventProcessor()
{
service.stop();
for (int i = 0; i < 3; i++)
{
m_Timers[i]->cancel();
}
}
void Run()
{
for (int i = 0; i < 3; i++)
{
m_Timers[i]->expires_from_now(boost::posix_time::seconds(TimerInterval[i]));
m_Timers[i]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
i,
_1));
}
service.run();
}
void RunAsync()
{
boost::thread(boost::bind(&EventProcessor::Run,this));
}
void Reset(int i,int seconds)
{
TimerInterval[i] = seconds;
m_Timers[i]->expires_from_now(boost::posix_time::seconds(TimerInterval[i]));
m_Timers[i]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
i,
_1));
}
private:
void HandleExpire(int p_TimerIndex, const boost::system::error_code& error)
{
if(error == boost::asio::error::operation_aborted)
{
std::cout << "Timer" << p_TimerIndex << " canceled" << std::endl;
return;
}
std::cout << "Timer" << p_TimerIndex << " expired" << std::endl;
//Reset(p_OriginalTimer, TimerInterval[p_TimerIndex], p_TimerIndex);
m_Timers[p_TimerIndex]->expires_from_now(
boost::posix_time::seconds(TimerInterval[p_TimerIndex]));
m_Timers[p_TimerIndex]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
p_TimerIndex,
_1));
}
};
int main()
{
EventProcessor ev(1,2,3);
ev.RunAsync();
getchar();
ev.Reset(2,4);
getchar();
}
Granted I don't have any of the fancy checkers to see if you are currently running or not (which you totally need if you want this to be safe to use).
You can think of boost::asio::io_service as a context in which async calls can be made. It creates a FIFO queue of messages to process, and processes them where and when you tell it to. The most common way to process these messages is boost::asio::io_service::run, which will process messages until there is nothing left to be done. "nothing left to be done" is a flexible definition: it doesn't necessarily mean there is a message to process, just that there is stuff to be done. Things like a deadline timer make sure that there is "something to be done" as long as an async_wait is going on until the handler is called. You can manually enforce that there is something to be done by creating a boost::asio::io_service::work instance. This makes it so that there is "something left to be done" for the lifetime of the work object.
The deadline timer class takes care of all the async calls for you, so you don't have to spawn all those threads. The io_service performs synchronization, which is necessary to prevent annoying control issues.
So to the problem with your code:
With all those threads controlling the io_service, it is hard to tell what is actually going wrong...I have to guess on what could possibly going wrong. I'd put my money on somewhere along the line, you call a io_service::cancel before a deadline timer times out, which will stop your loop. I solve this in my code by doing all the control (calling wait_async) in one synchronous thread (the io_service::run call) and only calling io_service::cancel when I want the code to stop.