Clearing a Nested QList: Risk of Memory Leak? - qt

Is it necessary to clear all the inner lists to avoid a leak?:
class Instruction
{
int opcode;
int data1;
int data2;
bool Load(QTextStream* in);
void Save(QTextStream* out) const;
};
class Interpreter
{
QList<QList<Instruction>> steps;
bool Load(QTextStream* file)
{
if(file_is_bad)
{
return false;
}
int end = steps.size();
for(int i=0; i<end; i++)
{
steps.at(i).clear();
}
steps.clear();
//now that it's clear, rebuild it from file
return true;
}
};
Or can I just call steps.clear(); and call it a day?
(And here's some more text to get past the "too much code" error.)

Igor was right. steps.clear() is enough because it destroys all inner QLists. Destroying an inner QList calls the destructor of all Instruction instants.
So as long as a single Instruction does not leak memory (e.g. by calling a new in the constructor but no delete in the destructor), QList<QList<Instruction>> will not leak as well.

Related

Usage of std::unique_ptr when moving object to worker thread in Qt

I am trying to update old code by removing class destructors. Now I got to the following code situation with a Qt master and a Qt slave (the slave is created, and moved to a second thread afterwards):
My slave was formerly written as:
serial_controller_worker::serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate, int numStopBits, bool parity, bool useParity, bool useHex)
{
this->portName = portname;
this->waitTimeout = waitTimeout;
this->baudrate = BaudRate;
this->numStopBits = numStopBits;
this->useParity = useParity;
this->parity = parity;
this->useHex = useHex;
this->serial = new QSerialPort(this);
this->storage = "";
this->delay_write = 0;
}
serial_controller_worker::~serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and was called in the master as
serial_controller_worker *newWorker = new serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex);
newWorker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, newWorker, &QObject::deleteLater);
connect(this, &Serial_port_library::newTransaction, newWorker, &serial_controller_worker::transaction);
connect(this, &Serial_port_library::connect_now, newWorker, &serial_controller_worker::connectToSerial);
connect(newWorker, &serial_controller_worker::response, this, &Serial_port_library::response_slot);
connect(newWorker, &serial_controller_worker::error_Val, this, &Serial_port_library::connectError);
workerThread.start();
emit this->connect_now();
Now I would like to transfer the slave to a constructor-only class, thus removing the destructor in the slave class. Nevertheless I still have to keep the destruction functions. Thus I created a new function for that:
void serial_controller_worker::delete_serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and created a std::unique_ptr with a custom destructor function:
struct WorkerFree
{
void operator() (serial_controller_worker *p) const { p->delete_serial_controller_worker(); }
};
class serial_controller_master{
private:
std::unique_ptr<serial_controller_worker, WorkerFree> serial_worker;
public:
serial_controller_master();
}
serial_controller_master::serial_controller_master()
{
serial_worker.reset(serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex));
serial_worker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);
}
How can I tell Qt to use my "destructor" when calling QObject::deleteLater() instead of trying to find another destructor, and how do I use the connect-calls later correctly? Currently I get errors like
error: no matching function for call to ‘Serial_port_library::connect(QThread*, void (QThread::*)(QThread::QPrivateSignal), std::unique_ptr<serial_controller_worker, WorkerFree>&, void (QObject::*)())’
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);

Swap memory pointers atomically on CUDA

I have two pointers in memory and I want to swap it atomically but atomic operation in CUDA support only int types. There is a way to do the following swap?
classA* a1 = malloc(...);
classA* a2 = malloc(...);
atomicSwap(a1,a2);
When writing device-side code...
While CUDA provides atomics, they can't cover multiple (possibly remote) memory locations at once.
To perform this swap, you will need to "protect" access to both these values with something like mutex, and have whoever wants to write values to them take a hold of the mutex for the duration of the critical section (like in C++'s host-side std::lock_guard). This can be done using CUDA's actual atomic facilities, e.g. compare-and-swap, and is the subject of this question:
Implementing a critical section in CUDA
A caveat to the above is mentioned by #RobertCrovella: If you can make do with, say, a pair of 32-bit offsets rather than a 64-bit pointer, then if you were to store them in a 64-bit aligned struct, you could use compare-and-exchange on the whole struct to implement an atomic swap of the whole struct.
... but is it really device side code?
Your code actually doesn't look like something one would run on the device: Memory allocation is usually (though not always) done from the host side before you launch your kernel and do actual work. If you could make sure these alterations only happen on the host side (think CUDA events and callbacks), and that device-side code will not be interfered with by them - you can just use your plain vanilla C++ facilities for concurrent programming (like lock_guard I mentioned above).
I managed to have the needed behaviour, it is not atomic swap but still safe. The context was a monotonic Linked List working both on CPU and GPU:
template<typename T>
union readablePointer
{
T* ptr;
unsigned long long int address;
};
template<typename T>
struct LinkedList
{
struct Node
{
T value;
readablePointer<Node> previous;
};
Node start;
Node end;
int size;
__host__ __device__ void initialize()
{
size = 0;
start.previous.ptr = nullptr;
end.previous.ptr = &start;
}
__host__ __device__ void push_back(T value)
{
Node* node = nullptr;
malloc(&node, sizeof(Node));
readablePointer<Node> nodePtr;
nodePtr.ptr = node;
nodePtr.ptr->value = value;
#ifdef __CUDA_ARCH__
nodePtr.ptr->previous.address = atomicExch(&end.previous.address, nodePtr.address);
atomicAdd(&size,1);
#else
nodePtr.ptr->previous.address = end.previous.address;
end.previous.address = nodePtr.address;
size += 1;
#endif
}
__host__ __device__ T pop_back()
{
assert(end.previous.ptr != &start);
readablePointer<Node> lastNodePtr;
lastNodePtr.ptr = nullptr;
#ifdef __CUDA_ARCH__
lastNodePtr.address = atomicExch(&end.previous.address,end.previous.ptr->previous.address);
atomicSub(&size,1);
#else
lastNodePtr.address = end.previous.address;
end.previous.address = end.previous.ptr->previous.address;
size -= 1;
#endif
T toReturn = lastNodePtr.ptr->value;
free(lastNodePtr.ptr);
return toReturn;
}
__host__ __device__ void clear()
{
while(size > 0)
{
pop_back();
}
}
};

Custom listModel does not notify the view

I have my custom list model where I put data which should be displayed on the QML view. But for some kind of reason the view in QML sometimes is updated normally, sometimes with previous data and sometimes updating is not performed.
Here is the function where I fill the model - this function is called from some other thread.
void MyScreen::fillListModel()
{
const QString SEPARATOR = " ";
myListModel->resetModel();
for (int i = 0; i < MAX_ROWS; ++i)
{
QString key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
QString val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
myListModel->addItem(key + SEPARATOR + val);
}
}
Implementation of model reset:
void BrowsingModelBase::resetModel()
{
beginResetModel();
m_items.clear();
endResetModel();
}
Implementation of addItem():
void BrowsingModelBase::addItem(const BrowsingItemModelBase &item)
{
int count = m_items.size();
beginInsertRows(QModelIndex(), count, count);
m_items.append(item);
endInsertRows();
}
Finally my QML file:
MyScreen {
Column {
id: myFlowList
y: 110
x: 220
ListView {
height:1000
spacing: 35;
model: myListModelRoot.myListModel
delegate: Text {
text: text1
}
}
}
}
The strange thing is that after loop with line
myListModel->addItem(key + SEPARATOR + val);
when I print logs with data from myListModel it is filled with proper data, but the view is usually updated with previous data. Is it possible that data change signal is stuck somewhere? Any idea what is the solution?
Assuming that you call your model's methods from another thread, with the model being fundamentally not thread-safe, you have two options:
Make certain methods of your model thread-safe, or
Explicitly invoke the methods in a thread-safe fashion.
But first, you'd gain a bit of performance by adding all the items at once, as a unit. That way, the model will emit only one signal for all of the rows, instead of one signal per row. The views will appreciate it very much.
class BrowsingModelBase {
...
};
Q_DECLARE_METATYPE(QList<BrowsingItemModelBase>)
void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
m_items.append(items);
endInsertRows();
}
You should probably also have a method called clear instead of resetModel, since to reset a model has a much more general meaning: "change it so much that it's not worth emitting individual change signals". To reset a model does not mean "clear it"! Thus:
void BrowsingModelBase::clear()
{
beginResetModel();
m_items.clear();
endResetModel();
}
Finally, following the 2nd approach of safely invoking the model's methods, fillListModel becomes as follows. See this answer for discussion of postTo.
template <typename F>
void postTo(QObject * obj, F && fun) {
if (obj->thread() != QThread::currentThread()) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, obj, std::forward<F>(fun));
} else
fun();
}
void MyScreen::fillListModel()
{
auto separator = QStringLiteral(" ");
QList<BrowserItemModelBase> items;
for (int i = 0; i < MAX_ROWS; ++i) {
auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
items << BrowserItemModelBase(key + separator + val);
}
postTo(myListModel, [this, items]{
myListModel->clear();
myListModel->addItems(items);
});
}
Alternatively, following the first approach, you can make the clear and addItems methods thread-safe:
/// This method is thread-safe.
void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
postTo(this, [this, items]{
beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
m_items.append(items);
endInsertRows();
});
}
/// This method is thread-safe.
void BrowsingModelBase::clear()
{
postTo(this, [this]{
beginResetModel();
m_items.clear();
endResetModel();
});
}
You then need no changes to fillListModel, except to make it use addItems:
void MyScreen::fillListModel()
{
auto separator = QStringLiteral(" ");
myListModel->clear();
QList<BrowserItemModelBase> items;
for (int i = 0; i < MAX_ROWS; ++i) {
auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
items << BrowserItemModelBase(key + separator + val);
}
myListModel->addItems(items);
}
The problem is most likely the fact that you are calling the fillListModel() method not from the main GUI thread. You can update the model from other threads but the beginResetModel();, endResetModel();, beginInsertRows(QModelIndex(), count, count);... methods have to be called in the main GUI thread.
One way to call these method in GUI thread (maybe not the most efficient) is to :
Create signals for each method you want to call:
signals:
//these signals are emitted from worker thread
void requestBeginResetModel();
void requestEndResetModel();
Create slots that will actually call the methods:
private slots:
//these slots execute the model reset operations in main thread
void callBeginResetModel();
void callEndResetModel();
Connect the signals and the slots:
//connect the appropriate signals
connect(this, SIGNAL(requestBeginResetModel()),
this, SLOT(callBeginResetModel()));
connect(this, SIGNAL(requestEndResetModel()),
this, SLOT(callEndResetModel()));
Your reset model will then be:
void BrowsingModelBase::resetModel()
{
emit requestBeginResetModel();
m_items.clear();
emit requestEndResetModel();
}
Finally the slots are implemented as:
void ObjectModel::callBeginResetModel()
{
beginResetModel();
}
void ObjectModel::callEndResetModel()
{
endResetModel();
}
Note that you will have to do the same for row insert methods as well. Or alternatively you could fill you model in the resetModel() method in between emitted signals.

How can I avoid infinite loop when modifying textboxes (QLineEdit) that change related item info?

I have several fields in a widget, that each can affect the behavior of an item, and changing some of them will change others.
I read somewhere that the editingFinished() signal of a line edit is triggered only by user actions - and not by code changes... Is that true ?
connect(m_lineEdit1, SIGNAL(editingFinished()), this, SLOT(m_lineEdit1Changed()));
connect(m_lineEdit2, SIGNAL(editingFinished()), this, SLOT(m_lineEdit2Changed()));
connect(this, SIGNAL(someSignal()), this, SLOT(updateData()));
void m_lineEdit1Changed()
{
changedata1();
emit someSignal();
}
void m_lineEdit2Changed()
{
changedata2();
emit someSignal();
}
void updateData()
{
m_lineEdit1.setText(fromdata);
m_lineEdit2.setText(fromdata);
}
If I change m_lineEdit1, and update the entire widget (which changes, through code, m_lineEdit2), I hit a breakpoint in m_lineEdit2Changed()
This leads to an infinite loop of updates...
What can I do to get around it ?
Blocking signals is a bit of a sledgehammer of an approach. You can use a sentinel class to explicitly prevent recursion:
#define SENTINEL_STRINGIFY(x) #x
#define SENTINEL_TOSTRING(x) SENTINEL_STRINGIFY(x)
#define SENTINEL_AT __FILE__ ":" SENTINEL_TOSTRING(__LINE__)
class Sentinel {
Q_DISABLE_COPY(Sentinel);
static QMutex m_mutex;
static QSet<QString> m_sentinels;
QString const m_sentinel;
bool const m_ok;
static bool checkAndSet(const QString & sentinel) {
QMutexLocker lock(&m_mutex);
if (m_sentinels.contains(sentinel)) return false;
m_sentinels.insert(sentinel);
return true;
}
public:
explicit Sentinel(const char * sentinel) :
m_sentinel(sentinel), m_ok(checkAndSet(m_sentinel)) {}
~Sentinel() {
if (!m_ok) return;
QMutexLocker lock(&m_mutex);
m_sentinels.remove(m_sentinel);
}
bool operator()() const { return m_ok; }
};
QMutex Sentinel::m_mutex;
QSet<QString> Sentinel::m_sentinels;
...
void Foo::m_lineEdit1Changed()
{
Sentinel s(SENTINEL_AT);
if (!s) return; // exit if this method is on the call stack
...
changedata1();
emit someSignal();
}
This is thread-safe and can be used from any thread.
A technique to avoid this problem is to use the QObject::blockSignals() function.
In your example you would do:
void updateData()
{
m_lineEdit1.blockSignals(true);
m_lineEdit1.setText(fromdata);
m_lineEdit1.setText(fromdata);
m_lineEdit1.blockSignals(false);
}
The blockSignals() call prevents the object sending any signals while you are changing the data in the line edit.

Qt: Simple DOM Model Example Memory Leak

I think that the Simple DOM Model Example in Qt has a memory leak. The following code is used to create the wrapped DomItem objects that are used to track the QDomNodes.
DomItem *DomItem::child(int i)
{
if (childItems.contains(i))
return childItems[i];
if (i >= 0 && i < domNode.childNodes().count()) {
QDomNode childNode = domNode.childNodes().item(i);
DomItem *childItem = new DomItem(childNode, i, this);
childItems[i] = childItem;
return childItem;
}
return 0;
}
I don't see how the if condition actually prevents a previously created wrapper from being overwritten in the QHash<int,DomItem*> that's used. Here is the class definition:
class DomItem
{
public:
DomItem(QDomNode &node, int row, DomItem *parent = 0);
~DomItem();
DomItem *child(int i);
DomItem *parent();
QDomNode node() const;
int row();
private:
QDomNode domNode;
QHash<int,DomItem*> childItems;
DomItem *parentItem;
int rowNumber;
};
The first line of the method, if (childItems.contains(i)) return childItems[i];, will prevent items in the hash from being overwritten.

Resources