Good day everyone, I discovered a bug in my code, that is:
I have a list of pointers to QLocalSocket and in the destructor I close and delete them with the following piece of code
for ( int i = 0; i < localSocketsList.size(); i++ )
{
if ( localSocketsList.at(i) != NULL )
{
localSocketsList.at(i)->close();
localSocketsList.at(i)->deleteLater();
}
}
The bug was that I previously connected a slot with the disconnected() signal of the sockets, and the slot delete them as well with the code:
QMutableListIterator<QLocalSocket *> iterator(localSocketsList);
while( iterator.hasNext() )
{
QLocalSocket * currentLocalSocket = iterator.next();
if ( currentLocalSocket -> state() == QLocalSocket::UnconnectedState )
{
currentLocalSocket -> deleteLater();
iterator.remove();
}
}
Ok right now you can see the error, I try to delete a pointer twice and I have a crash. BUT, it took me a while to detect this error because I did not observe a crash in Windows 10, only in Windows 7.
The question is: is there some difference in the deleteLater() function of Qt5.7 depending of the operative system? Shouldn't this problem appear in all platforms, since it is a c++ run time error?
Maybe is something depending of how Qt schedules the job (I mean, finishing the for cycle before sending the signals)? In that case is the schedule of the jobs depending of the OS? Shouldn't this be almost "random"?
Thanks everybody
It is valid to invoke deleteLater multiple times before the control returns to the event loop:
#include <QtCore>
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
auto obj = new QObject;
obj->deleteLater();
obj->deleteLater();
connect(obj, &QObject::destroyed, []{ qApp->quit(); });
return app.exec();
}
Thus, it seems to me that your problem isn't in invoking deleteLater multiple times, but iterating over a collection of objects that have been destroyed. The localSocketList is not aware of the sockets being deleted and contains dangling pointers.
There's a simple remedy for that - use a list that is aware of objects being deleted. For simplicity, the list below is explicitly shared, i.e. any of its copies refer to the same object (those are also JavaScript semantics, if you're familiar with them).
// https://github.com/KubaO/stackoverflown/tree/master/questions/qobject-pointer-list-43986348
#include <QtCore>
class PointerListData : public QObject, public QSharedData {
Q_OBJECT
public:
QVector<QObject*> list;
void removed() { list.removeAll(sender()); }
void connect(QObject* obj) {
QObject::connect(obj, &QObject::destroyed, this, &PointerListData::removed);
}
void disconnect(QObject* obj) {
QObject::disconnect(obj, &QObject::destroyed, this, &PointerListData::removed);
}
};
template <typename T> class PointerList {
protected:
QExplicitlySharedDataPointer<PointerListData> d;
public:
PointerList() : d(new PointerListData) {}
PointerList(const PointerList &other) : d(other.d) {}
PointerList(PointerList && other) : d(std::move(other.d)) {}
void append(T* obj) {
auto connect = !contains(obj);
d->list.append(obj);
if (connect)
d->connect(obj);
}
PointerList & operator<<(T* obj) {
append(obj);
return *this;
}
int removeAll(T* obj) {
auto n = d->list.removeAll(obj);
if (n)
d->disconnect(obj);
return n;
}
bool contains(T* obj) const {
return d->list.contains(obj);
}
void clear() {
for (auto obj : d->list)
d->disconnect(obj);
d->list.clear();
}
void moveToThread(QThread* thread) { d->moveToThread(thread); }
bool isEmpty() const { return d->list.isEmpty(); }
int size() const { return d->list.size(); }
using iterator = T**;
using const_iterator = const T**;
iterator begin() { return iterator(d->list.data()); }
iterator end() { return iterator(d->list.data() + d->list.size()); }
const_iterator begin() const { return const_iterator(d->list.constData()); }
const_iterator end() const { return const_iterator(d->list.constData() + d->list.size()); }
constexpr const PointerList& crange() const noexcept { return *this; }
// see http://stackoverflow.com/q/15518894/1329652
};
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
PointerList<QMimeData> list;
{
QMimeData a;
QMimeData b;
list << &a << &b;
auto list2 = list;
Q_ASSERT(list2.size() == 2);
for (auto obj : list.crange())
qDebug() << obj;
}
Q_ASSERT(list.isEmpty());
}
#include "main.moc"
Related
I am trying to create a base class to manage a slice-based workload.
My approach was to create a base abstract class that handles the initialization/termination of the work and inherit from that class in specific classes that only specify the actual work and timings.
I also added the functionality in the base class to reinitialize the workload if a set number of errors occur.
This works as expected in a simple example (given below) and with most workloads that I have but when I try to use this with a specific workload (reading a serial port that's written to by an arduino) it completely messes up the stream read from arduino.
I suspect there is some problem with my approach but I couldn't figure it out...
Here is my code:
sliceWork.h
#pragma once
#include <future>
using namespace ::std;
class sliceWork
{
int sliceIntervalMilliSeconds;
int failureCounter;
int maxFailsBeforeRestart;
char* label = NULL;
promise<void> workPromise;
thread* workerThread = NULL;
virtual void init() = 0;
virtual bool oneSliceWork() = 0;
void work(future<void> future);
public:
sliceWork(int sliceInterval, int maxFails, const char* label);
~sliceWork();
void initWork();
void signalTerminate();
};
sliceWork.cpp
#include <string.h>
#include "sliceWork.h"
sliceWork::sliceWork(int interval, int maxFails, const char* workLabel)
{
sliceIntervalMilliSeconds = interval;
maxFailsBeforeRestart = maxFails;
label = new char[strlen(workLabel) + 1];
strcpy(label, workLabel);
}
sliceWork::~sliceWork()
{
if (workerThread != NULL && workerThread->joinable())
workerThread->join();
printf("destructor %s\n", label);
delete label;
delete workerThread;
}
void sliceWork::initWork()
{
failureCounter = 0;
init();
printf("Init work %s finished!\n", label);
future<void> futureWorker = workPromise.get_future();
workerThread = new thread(&sliceWork::work, this, move(futureWorker));
}
void sliceWork::work(future<void> future)
{
using namespace ::std::chrono;
steady_clock::time_point t0 = steady_clock::now();
while (future.wait_for(chrono::milliseconds(1)) == future_status::timeout)
{
if (duration_cast<chrono::milliseconds>(steady_clock::now() - t0).count()
> sliceIntervalMilliSeconds)
{
if (!oneSliceWork())
{
if (++failureCounter > maxFailsBeforeRestart
&& maxFailsBeforeRestart > 0)
{
init();
failureCounter = 0;
}
}
t0 = steady_clock::now();
}
}
printf("work terminated for %s!\n", label);
}
void sliceWork::signalTerminate()
{
printf("request terminate for work %s...\n", label);
workPromise.set_value();
}
And here is an example of using it that works as expected:
main.cpp
#include <string.h>
#include "sliceWork.h"
class A : public sliceWork
{
void init() {
printf("Init A...\n");
}
bool oneSliceWork() {
printf("Working A...\n");
return true;
}
public:
A(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
class B : public sliceWork
{
void init() {
printf("Init B...\n");
}
bool oneSliceWork() {
printf("Working B...\n");
return true;
}
public:
B(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
class C : public sliceWork
{
void init() {
printf("Init C...\n");
}
bool oneSliceWork() {
printf("Working C...\n");
return false;
}
public:
C(int slice, int max, const char* label)
: sliceWork(slice, max, label)
{
}
};
int main()
{
{
A a(1000, 1000, "A");
a.initWork();
B b(2000, 1000, "B" );
b.initWork();
C c(700, 2, "C" );
c.initWork();
printf("Initializations finished!\n");
::std::this_thread::sleep_for(::std::chrono::seconds(7));
a.signalTerminate();
::std::this_thread::sleep_for(::std::chrono::seconds(5));
b.signalTerminate();
::std::this_thread::sleep_for(::std::chrono::seconds(4));
c.signalTerminate();
}
getchar();
return 0;
}
So, I want to ask if this approach is prone to error because the way I implemented the functionality.
Application is written in C++11 and targets an Raspberry PI 3b+ running the Raspberry's flavor of Debian 11 (Raspbian), if that is relevant.
Since C++11 we use keyword nullptr instead of NULL macro. Moreover, std::thread is movable, so it is much better to use it as value rather than pointer:
class sliceWork{
///...
std::thread workerThread;
///...
~sliceWork(){
///...
if (workerThread.joinable())
workerThread.join();
///...
};
///...
void initWork(){
///...
workerThread = thread{[this](){
work(workPromise.get_future());
}};
///...
};
};
I used a lambda to initialize the thread instead of your original code; it has better minimally performance, while more readable IMO.
If you can use C++17, then I strongly recommend using std::string_view over old null-terminated strings; otherwise just use std::string. Also using constructor member initializer list is always recommended:
#include <string>
class sliceWork{
///...
std::string_view label;
///...
sliceWork(int interval, int maxFails, std::string_view workLabel):
sliceIntervalMilliSeconds {interval},
maxFailsBeforeRestart {maxFails},
label {workLabel}
{};
///...
};
If you can use C++20 however, std::jthread is of huge advantages over std::thread. Because now that you already don't delete the label or workerThread and the destructor of std::jthread automatically joins, you can totally drop the destructor of sliceWork; the default compiler-provided destructor will do!! Plus, you can even get rid of the workPromise:
class sliceWork{
///...
std::string_view label;
///...
std::jthread workerThread;
///...
//std::promise<void> workPromise;//we don't need this
///...
//~sliceWork()=default;
///...
void signalTerminate(){
///...
workerThread.request_stop();
}
///...
void initWork(){
///...
workerThread = jthread{[this](std::stop_token stop_token){
work(std::move(stop_token));
}};
///...
};
///...
void work(std::stop_token stoken){
///...
for(int ticks{0}; !stoken.stop_requested(); sleep_for(chrono::milliseconds(1)), ++ticks) {
if (ticks > sliceIntervalMilliSeconds) {
///...
}; //if
///...
}; //for
///...
};//work
};
One final word: almost never use printf; it has lots of caveats. In C++ we use std::cout. In multi_threaded applications, end the std::cout instructions with << std::endl; this flushes the buffer and helps the output to be more readable.
We need to suppress qDebug() messages when tracing is disabled.
I know the qInstallMessageHandler() an we are using it but we need an additional trace method.
class MyDebug : public QDebug {
public:
explicit MyDebug();
MyDebug(const QDebug &o);
MyDebug(const MyDebug &o);
private:
std::shared_ptr<QString> null_string;
static void doDeleteLater(QString *obj);
};
MyDebug::MyDebug()
: null_string(new QString(), &MyDebug::doDeleteLater), QDebug(null_string.get()) {
qCritical("construct");
}
void MyDebug::doDeleteLater(QString *obj) {
qCritical("delete");
}
MyDebug::MyDebug(const QDebug &o)
: QDebug(o) {
qCritical("called1");
}
MyDebug::MyDebug(const MyDebug &o)
: null_string(o.null_string), QDebug(null_string.get()) {
qCritical("called2");
}
MyDebug Application::trace(const uchar verbosity) const {
MyDebug d = this->logLevel > 6 ? MyDebug(qDebug()) : MyDebug();
// QDebug default verbosity is 2 - Range 0-7
d.setVerbosity(qMin((uchar)7, verbosity));
return d;
}
Now using the Application::trace() results in errors
Application::trace() << QString("test");
...
construct
QTextStream: No device
delete
I thought it has to do with the lifetime of null_string when MyDebug is copied, but the copy constructor is never called.
Using a heap constructed QString* as QDebug device is working.
Install a custom handler like:
#include <QtGlobal>
QtMessageHandler DEFAULT_MSG_HANDLER = 0;
void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (type == QtDebugMsg) {
// Ignores debug.
return;
}
// Redirects anything else to the default handler.
(*DEFAULT_MSG_HANDLER)(type, context, msg);
}
int main(int argc, char **argv)
{
DEFAULT_MSG_HANDLER = qInstallMessageHandler(myMessageHandler);
QApplication app(argc, argv);
// ...
return app.exec();
}
I have written a multi-threaded app in Qt/C++11 , Windows.
The idea was to have and recycle some strings from a pool, using smart pointers.
Here is stringpool.cpp:
#include "stringpool.h"
QMutex StringPool::m_mutex;
int StringPool::m_counter;
std::stack<StringPool::pointer_type<QString>> StringPool::m_pool;
StringPool::pointer_type<QString> StringPool::getString()
{
QMutexLocker lock(&m_mutex);
if (m_pool.empty())
{
add();
}
auto inst = std::move(m_pool.top());
m_pool.pop();
return inst;
}
void StringPool::add(bool useLock, QString * ptr)
{
if(useLock)
m_mutex.lock();
if (ptr == nullptr)
{
ptr = new QString();
ptr->append(QString("pomo_hacs_%1").arg(++m_counter));
}
StringPool::pointer_type<QString> inst(ptr, [this](QString * ptr) { add(true, ptr); });
m_pool.push(std::move(inst));
if(useLock)
m_mutex.unlock();
}
And here is stringpool.h:
#pragma once
#include <QMutex>
#include <QString>
#include <functional>
#include <memory>
#include <stack>
class StringPool
{
public:
template <typename T> using pointer_type = std::unique_ptr<T, std::function<void(T*)>>;
//
StringPool() = default;
pointer_type<QString> getString();
private:
void add(bool useLock = false, QString * ptr = nullptr);
//
static QMutex m_mutex;
static int m_counter;
static std::stack<pointer_type<QString>> m_pool;
};
And here is the test app:
#include <QtCore>
#include "stringpool.h"
static StringPool Pool;
class Tester : public QThread
{
public:
void run() override
{
for(int i = 0; i < 20; i++)
{
{
auto str = Pool.getString();
fprintf(stderr, "Thread %p : %s \n", QThread::currentThreadId(), str->toUtf8().data());
msleep(rand() % 500);
}
}
fprintf(stderr, "Thread %p : FINITA! \n", QThread::currentThreadId());
}
};
#define MAX_TASKS_NBR 3
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Tester tester[MAX_TASKS_NBR];
for(auto i = 0; i < MAX_TASKS_NBR; i++)
tester[i].start();
for(auto i = 0; i < MAX_TASKS_NBR; i++)
tester[i].wait();
//
return 0;
}
It compiles ok, it runs and produces the following result:
Well, the idea is that the app runs (apparently) OK.
But immediately after it finishes, I have this error:
Does anyone have an idea how can I fix this?
The reason for this error has to do with the smart pointer and not the multithreading.
You define pointer_type as an alias for unique_ptr with a custom deleter
template <typename T> using pointer_type = std::unique_ptr<T, std::function<void(T*)>>;
You create strings with custom deleters
void StringPool::add(bool useLock, QString * ptr)
{
if (ptr == nullptr)
{
ptr = new QString();
ptr->append(QString("pomo_hacs_%1").arg(++m_counter));
}
StringPool::pointer_type<QString> inst(ptr, [this](QString * ptr) { add(true, ptr); }); // here
m_pool.push(std::move(inst));
}
At the end of the program, m_pool goes out of scope and runs its destructor.
Consider the path of execution...m_pool will try to destroy all its members. For each member, the custom deleter. The custom deleter calls add. add pushes the pointer to the stack.
Logically this is an infinite loop. But it's more likely to create some kind of undefined behavior by breaking the consistency of the data structure. (i.e. The stack shouldn't be pushing new members while it is being destructed). An exception might occur due to function stack overflow or literal stack overflow (heh) when there is not enough memory to add to the stack data structure. Since the exception occurs in a destructor unhandled, it ends the program immediately. But it could also very likely be a seg fault due to the pushing while destructing.
Fixes:
I already didn't like your add function.
StringPool::pointer_type<QString> StringPool::getString()
{
QMutexLocker lock(&m_mutex);
if (m_pool.empty())
{
auto ptr = new QString(QString("pomo_hacs_%1").arg(++m_counter));
return pointer_type<QString>(ptr, [this](QString* ptr) { reclaim(ptr); });
}
auto inst = std::move(m_pool.top());
m_pool.pop();
return inst;
}
void StringPool::reclaim(QString* ptr)
{
QMutexLocker lock(&m_mutex);
if (m_teardown)
delete ptr;
else
m_pool.emplace(ptr, [this](QString* ptr) { reclaim(ptr); });
}
StringPool::~StringPool()
{
QMutexLocker lock(&m_mutex);
m_teardown = true;
}
StringPool was a static class but with this fix it must now be a singleton class.
It might be tempting to pull m_teardown out of the critical section, but it is shared data, so doing will open the door for race conditions. As a premature optimization, you could make m_teardown an std::atomic<bool> and perform a read check before entering the critical section (can skip the critical section if false) but this requires 1) you check the value again in the critical section and 2) you change from true to false exactly once.
I have an object which contains a thread which indirectly accesses this object like so:
#include <iostream>
#include <thread>
#include <atomic>
class A;
class Manager
{
public:
Manager(void) = default;
void StartA(void)
{
a = std::make_unique<A>(*this);
}
void StopA(void)
{
a = nullptr;
}
A& GetA(void)
{
return *a;
}
private:
std::unique_ptr<A> a;
};
class A
{
public:
A(Manager& manager)
: manager{manager},
shouldwork{true},
thread{[&]{ this->Run(); }}
{
}
~A(void)
{
shouldwork = false;
thread.join();
}
private:
Manager& manager;
std::atomic<bool> shouldwork;
std::thread thread;
void Run(void)
{
while (shouldwork)
{
// Here goes a lot of code which calls manager.GetA().
auto& a = manager.GetA();
}
}
};
int main(int argc, char* argv[])
try
{
Manager man;
man.StartA();
man.StopA();
}
catch (std::exception& e)
{
std::cerr << "Exception caught: " << e.what() << '\n';
}
catch (...)
{
std::cerr << "Unknown exception.\n";
}
The problem is that when one thread calls Manager::StopA and enters destructor of A, the thread inside A segfaults at Manager::GetA. How can I fix this?
In StopA() you set a = nullptr;, this in turn destroys the a object and all further access to its members result in undefined behaviour (a likely cause the segmentation fault).
Simply moving the a = nullptr; to the destructor of the Manager could resolve this problem. Even better, allow the RAII mechanism of the std::unique_ptr to destroy the a object when the destructor of the Manager runs (i.e. remove the line of code completely).
With active object implementations, careful control of the member variables is important, especially the "stop variable/control" (here the shouldwork = false;). Allow the manager to access the variable directly or via a method to stop the active object before its destruction.
Some of the code here looks out of place or obscure, e.g. a = std::make_unique<A>(*this);. A redesign could help simplify some of the code. The Manager class could be removed.
class A
{
public:
A(): shouldwork{true}, thread{[&]{ this->Run(); }}
{
}
void StopA()
{
shouldwork = false;
thread.join();
}
private:
std::atomic<bool> shouldwork;
std::thread thread;
void Run(void)
{
while (shouldwork)
{
// code...
}
}
};
The code is modelled along the lines of std::thread, were the stopping of the tread is more controlled before an attempt is made to join it. The destructor is left empty in this case, to mimic the termination (calling std::terminate) result, as is the case with the standard thread library. Threads must be explicitly joined (or detached) before destruction.
Re-introducing the Manager, the code could look as follows;
class A
{
public:
A() : shouldwork{true}, thread{[&]{ this->Run(); }} {}
void StopA() { shouldwork = false; thread.join(); }
private:
void Run();
std::atomic<bool> shouldwork;
std::thread thread;
};
class Manager
{
public:
Manager() = default;
void StartA(void)
{
a = std::make_unique<A>();
}
void StopA(void)
{
a->StopA();
}
A& GetA(void)
{
return *a;
}
private:
std::unique_ptr<A> a;
};
void A::Run()
{
while (shouldwork)
{
// Here goes a lot of code which calls manager.GetA().
auto& a = manager.GetA();
}
}
And your main remains as it is.
I was thinking about a C++ class that manages my TCP connections (on Linux). Upon destruction the connection should be closed similar to this:
TCPConnection::~TCPConnection()
{
close(_socket);
}
The problem is that when putting an object of this class into e.g. a vector the connection will also be closed like this even though I would still like to use the connection. How can I solve this? Is this a bad idea in general?
What you want to implement is a design pattern called RAII, so once your TCPConnection is instatiated it aquires resources, once it is destroyed it releases resources. If it was destroyed then it means that programmer intention was to stop using those resources. If you need to still use them, then you must prolong lifetime of your object. You can use typedef std::shared_ptr<TCPConnection> TCPConnectionPtr, then you can put your TCPConnectionPtr instances in many places and your connection will be closed only once all those instances are destroyed.
example code (http://coliru.stacked-crooked.com/a/581a856ee32890d2):
#include <iostream>
#include <vector>
#include <memory>
class TCPConnection {
int inst;
static int inst_count;
public:
TCPConnection() { inst=inst_count++; std::cout << "TCPConnection created: " << inst << std::endl; }
~TCPConnection() { std::cout << "TCPConnection destroyed:" << inst << std::endl; }
};
int TCPConnection::inst_count;
// version if If c++11 is available, can be also implemented with boost::shared_ptr
// Removing individual TCPConnection from vector will also decrement its shared_ptr
// usage count and if it is zero then will destroy also such connections.
typedef std::shared_ptr<TCPConnection> TCPConnectionPtr;
typedef std::vector<TCPConnectionPtr> TCPConnectionPtrVec;
void fun1() {
TCPConnectionPtrVec vec;
vec.push_back(TCPConnectionPtr(new TCPConnection()));
}
// version for pre c++11 compiler, but I would recomend using boost::shared_ptr
// Class TCPConnectionsVecWrapper is a helper to make sure connections are safely freed.
class TCPConnectionsVecWrapper {
// No copying allowed
TCPConnectionsVecWrapper( const TCPConnectionsVecWrapper& );
TCPConnectionsVecWrapper& operator=( const TCPConnectionsVecWrapper& );
typedef std::vector<TCPConnection*> TCPConnectionPtrsVec;
TCPConnectionPtrsVec vec;
public:
TCPConnectionsVecWrapper() {}
~TCPConnectionsVecWrapper() {
for (TCPConnectionPtrsVec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr) delete *itr;
}
TCPConnection* createConnection() {
vec.push_back(new TCPConnection());
return vec.back();
}
void remove(int index) {
delete vec[index];
vec.erase(vec.begin() + index);
}
TCPConnection* get(int index) { return vec[index]; }
const TCPConnection* get(int index) const { return vec[index]; }
std::size_t size() const { return vec.size(); }
};
void fun2() {
// All TCPConnection will get deleted once tcpConnectionsWrapper is out of scope
TCPConnectionsVecWrapper conns;
TCPConnection* con1 = conns.createConnection();
(void)con1; // unused
TCPConnection* con2 = conns.createConnection();
(void)con2; // unused
for ( size_t i = 0; i < conns.size(); ++i ) {
TCPConnection* con = conns.get(i);
(void)con; // unused
}
conns.remove(0);
}
int main(int argc, char** argv){
fun1();
fun2();
}
Store the TCPConnection as a pointer in a std::vector<TCPConnection*> rather than an an instance. Then when you need to tidy up you can just delete the pointer.
Alternatively, if you've got access to std::shared_ptr you can store that in the vector instead, and when nothing else is referencing each connection then the connection will be deleted.