QThread ASSERT failure in QMutexLocker: "QMutex pointer is misaligned",
Asked Answered
H

3

2

Im trying to create an uploader that will create new threads and in every thread I have a QNetworkAccessManager. All the uploader threads have a reference to a shared list and will split it by using start and end indexes.

The Uploader looks something like this:

 class FileUploader : public QObject {
    Q_OBJECT

public:
    explicit FileUploader(QList<FileInfoWrapper> &fileList, const int start = 0, const int offset = 0, QObject *parent = 0);


    void uploadNext();

    QString containerName;

private:
    int start_, offset_, iterator_;
    QList<FileInfoWrapper> &fileList_;
    RestFileUploader *restFileUploader;

signals:
    void progressChangedAt(int row);
    void statusChangedAt(int row);
    void finished();

public slots:
    void init();

private slots:
    void setUploadProgress(qint64 tranfered);
    void handleRequestFinished(QNetworkReply* reply);
    void handleSslErros(QNetworkReply *reply, const QList<QSslError> &errors);
    void handleNetworkError(QNetworkReply::NetworkError error);

};

Then, in the run() function I create the new RestFileUploader(this)(Pretty much a object which creates its own new QNetworkAccessManager(this) and put requests on it) so that nothing is created in the constructor(which would cause it to end up in the main thread instead?). The run function creates a request to be given to the QNetworkAccessManager and then does nothing until that signals "finished(QNetworkReply)" and then I grab the next one (and so on until the list is stepped through).

Then I create two new threads in the main application and when I start them using run() it works except that the ID is the same on both the threads. If I instead call "start()" it crashes with: QObject: Cannot create children for a parent that is in a different thread. (Parent is FileUploader(0x2580748), parent's thread is QThread(0x4fb2b8), current thread is FileUploader(0x2580748)

BUT! Just before I start step through the list, I print the threadId and they are no longer the same.

What am I doing wrong or should I just do this: http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/ ?

Edit:

I changed it and renamed run to start and made this wrapper(and I no longer call NetworkAccessManager or RestFileUploader with "this"):

FileUploader *fileUploader = new FileUploader(fileList_, start, (offset == 0 ? (fileList_.count() - start) : offset));
QThread *fileUploaderThread = new QThread;
fileUploader->moveToThread(fileUploaderThread);

connect(fileUploader, SIGNAL(progressChangedAt(int)), model_, SLOT(reportProgressChanged(int)));
connect(fileUploader, SIGNAL(statusChangedAt(int)), model_, SLOT(reportStatusChanged(int)));

fileUploaderThread->start();
QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);

When uploading one object works, since I only use one thread then. But when I have more objects that I split up, the application crashed brutally with this error message:

ASSERT failure in QMutexLocker: "QMutex pointer is misaligned", file ..\..\include/QtCore/../../../../../../ndk_buildrepos/qt-desktop/src/corelib/thread/qmutex.h, line 100
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.

Please help me

Edit:

fileuploader.cpp

#include "fileuploader.h"

FileUploader::FileUploader(QList<FileInfoWrapper> &fileList, const int start, const int offset, QObject *parent)
    : QObject(parent), start_(start), offset_(offset), iterator_(start - 1), fileList_(fileList) {
}

void FileUploader::init() {
    restFileUploader = new RestFileUploader();

    connect(restFileUploader, SIGNAL(uploadProgress(qint64)), this, SLOT(setUploadProgress(qint64)));
    connect(restFileUploader, SIGNAL(requestFinished(QNetworkReply*)), this, SLOT(handleRequestFinished(QNetworkReply*)));
    connect(restFileUploader, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(handleSslErros(QNetworkReply*,QList<QSslError>)));
    connect(restFileUploader, SIGNAL(networkError(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply::NetworkError)));

    containerName = "temp"

    qDebug() << "thread" << this->thread()->currentThreadId() << start_ << ":" << offset_;

    uploadNext();
}

void FileUploader::uploadNext() {
     qDebug() << "uploadNext" << this->thread()->currentThreadId();

    if((iterator_ + 1) < (start_ + offset_)) {
        iterator_++;

        restFileUploader->putBlob(containerName, fileList_.at(iterator_).fileName(), fileList_.at(iterator_).fileInfo().filePath());

    } else emit finished();
}

void FileUploader::setUploadProgress(qint64 tranfered) {

    fileList_[iterator_].setProgress(tranfered);

    emit progressChangedAt(iterator_);
}

void FileUploader::handleRequestFinished(QNetworkReply* reply) {

    qDebug() << "finished blob: " << iterator_ << " in thread " << this->thread()->currentThreadId();

    if(reply->error() > QNetworkReply::NoError) {
        qDebug() << reply->errorString();

        fileList_[iterator_].uploadFailed();

        emit progressChangedAt(iterator_);

    } else fileList_[iterator_].uploadFinished();

    emit statusChangedAt(iterator_);

    uploadNext();
}

void FileUploader::handleNetworkError(QNetworkReply::NetworkError error) {

    if(error > QNetworkReply::NoError) {
        fileList_[iterator_].uploadFailed();

        restFileUploader->cancelCurrentRequest();

        emit progressChangedAt(iterator_);
        emit statusChangedAt(iterator_);
    }
}

void FileUploader::handleSslErros(QNetworkReply *reply, const QList<QSslError> &errors) {

    if(reply->error() > QNetworkReply::NoError) {

        qDebug() << reply->errorString();

        fileList_[iterator_].uploadFailed();

        restFileUploader->cancelCurrentRequest();

        emit progressChangedAt(iterator_);
        emit statusChangedAt(iterator_);
    }
}

#include "restfileuploader.h"

void RestFileUploader::putBlob(const QString& container, const QString& blob, const QString& filePath) {
    QFile *uploadFile = new QFile(filePath, this); // <--- this maybe?
    uploadFile->open(QIODevice::ReadOnly); 

    QNetworkRequest request = this->createRestRequest("PUT", QString("%1/%2").arg(container, blob), uploadFile->size(), headers);

    reply_ = accessManager_->put(request, uploadFile);

    connect(reply_, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(reportUploadProgress(qint64, qint64)));
    connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(reportNetworkError(QNetworkReply::NetworkError)));

    qDebug() << this->thread()->currentThreadId();
}

void RestFileUploader::cancelCurrentRequest() {
    reply_->abort();
}

RestFileUploader::~RestFileUploader() {
    qDebug() << "RestFileUploader deleted";

    reply_->deleteLater();
}

So... 1 thread with one thing to upload == ok. 2 objects on two threads are also okey. When I try to upload 3 or more objects on the two threads it all goes to hell.

Also, could it have something to do with that the UI is reading the info of the files at the same time as I change it?

EDIT: For some reason my app now works in 4.8.0 when I compile it in visual studio. Could it have something to do with version 4.7.4?

Hoboken answered 27/2, 2012 at 0:25 Comment(7)
Surely you shouldn't be calling run() directly; that's what the new thread will do once start()ed. Yes, I know that's not working for you either (hence this is a comment, not an answer) but run() really shouldn't be called from the main thread.Somerset
I might think it has something to with that I'm not keeping all my code in run(). I might just wrap itHoboken
"Also, could it have something to do with that the UI is reading the info of the files at the same time as I change it?" Yeah, it probably would. How about using threadsafe signals/slots to send the information to the main thread (where ui events happen)?Approve
i was trying to look for information about that but cant really find any. Any good site where I coul read about it?Hoboken
Try: developer.qt.nokia.com/doc/qt-4.8/…Approve
removed the reading of the list from the gui and im not printing out anything now. I still have no idea why it crashes randomly.Hoboken
chikuba - You have a similar question that you asked today as well where I was suggesting you use signal/slot for communicating your memory: #9476545 . +1 to @Approve for the same suggestionCodding
A
4

QThread::start() is what actually starts the thread (as a different thread). QThread::run() is just a regular function, so if you call it without calling start() first, you're executing it in the main thread.

The funny thing about it is that your derived class was created in the main thread, so it "belongs" in the main thread. I'm guessing you're giving your class as the parent to something else; this is what is generating the error message when you try to do this after calling start(). Can you leave those objects parent-less?

You won't be able to create gui objects in another thread, Qt just doesn't allow it (yet?). But other objects can be created, you just can't give them a parent in a different thread.

Approve answered 27/2, 2012 at 0:41 Comment(5)
When I create the uploader I doesnt give it a parent at all. Which one would be a qui object? The NAM?Hoboken
You said new AzureBlobStorageManager(this) - does this class pass the pointer to the thread to the NAM as a parent? I'm not familiar with the specifics of NAMs I'm afraid.Approve
i was just lazy and shortened NetworkAccessManager. The AzureBlobStorageManager is just a class I've made which contains some functions and a NetworkAccessManager and handles REST calls. So you would recoment me not to use "this" at all?Hoboken
I'm not saying you can't pass this, I was just wondering if this was being passed as the argument to a constructor(QObject* parent) somewhere. I see from the documentation that QNetworkAccessManager takes only one parameter (QObject* parent) in the constructor - so since you said you gave it an argument, that is where I suspect your problem is.Approve
In your edit, you say you renamed run() to start() - this would probably not work how you want it to work. QThread::start() calls run(), with run() being the virtual function so your derived implementation gets called. If you define Derived::start() and call that, QThread::start() won't actually get called, so you won't be actually starting the thread. You want your stuff in run(), just don't call it directly (i.e. call start() instead).Approve
S
3

The error you are receiving

ASSERT failure in QMutexLocker: "QMutex pointer is misaligned"

occurs on creation of a QMutexLocker object if the QMutex object passed to the constructor is not aligned on a 2-byte boundary in RAM (in Qt 4.7.1 at least).

The QMutexLocker object uses one member variable to represent both the location of the mutex in memory and its state (whether it is locked or not). The state is represented by the least significant bit of the variable, while the least significant bit of the mutex pointer is assumed to be zero. If this bit is not zero, the ASSERT exception above is thrown.

The only reason for misalignment of the QMutex pointer I can think of is a memory leak or corruption. Check whether you destroy all memory that you allocate (especially in loops) and check all typecasts (you might have casted a pointer of a smaller type to a pointer of a bigger type, damaging memory) and null-terminated strings (which will damage memory if not properly terminated). Finally, verify the thread safety of all memory that you share across threads.

Submariner answered 14/6, 2012 at 10:21 Comment(1)
I got this because I tried to lock a mutex in an already destroyed object. So accessing already freed memory can also be a root for this error.Braud
S
0

This is probably because the order some object in your application is created. I would recommend inherit from QObject instead of QThread, and move the object to a worker thread instead (QObject::moveToThread()). Also move any code that is initialized in FileUploader to a separate slot in FileUploader, say init(). Invoke init (QMetaObject::invokeMethod()) when the thread is running (after calling start on the worker thread). E.g.:

FileUploader : public QOject
{
...
public slots:
void init() { Foo *foo = new Foo(this); }
}

// main thread
QThread worker;
FileUploader *fup = new FileUploader();
fup->moveToThread(&worker);
worker.start();
QMetaObject::invokeMethod(fup, "init", Qt::QueuedConnection); 
//
Supplicate answered 27/2, 2012 at 1:42 Comment(16)
yeah. forgot to tell that i did those changes, except the last one with the metaobject. however, is it okey to create the thread on the heap? tried this way but ofc they went out of scope and died when the function endedHoboken
Did what you said and it now works with more threads, but I can only upload one object per thread. When I come to the "uploadNext" everything crashesHoboken
yes, that will be fine. just make sure that members allocated and that are children (in object tree) of FileUploader is created in the init slot.Supplicate
Then there is probably a synchronization problem, would need to see more of the code and e.g., stack trace to say for sure.Supplicate
make uploadNext a slot, and call it the same way as my example calls init.Supplicate
but its supose to be recursive. when the finish signal is emitted, the uploadNext will be called.Hoboken
1. if RestFileUploader is a QObject, you could set the parent to this in the init slot. 2. make sure that you don't call init more then once for each object you create. 3. Are you creating new threads each time one finishes?Supplicate
Are you adding stuff to the fileList from the original thread, after the worker thread is going? That would not be threadsafe. Like cstream recommenends you could make a slot to add files to the list, and use threadsafe signals to send the pointers across the thread boundary.Approve
1. done. 2. Only one per thread. 3. Creates 2 threads in the beginning, and allocates the data over them. If I only have one thread, it runs perfectly. Its when I run 2+ threads with 3+ objects where it crashesHoboken
Nothing is added to the fileList during runtime. The only thing is happening is that the fileList.at(i).progress and status will be changed as they are being uploadedHoboken
First, it looks as if your start/offset system is susceptible to overrunning the end of the list if you aren't careful. Second, what is finished() connected to - is one thread signaling that it is done and messing up the fileList somehow?Approve
I can't see much else. You will need to run this in a debugger and see where the call that causes the assertion, originated from. Probably an invalid pointer..., when you have an overview of the call trace, make sure that the path is thread safe. Also, make sure that access to the filelist is done in a thread-safe way, e.g, by splitting up the list, and have the FileUploaders own there part of the file list instead of a reference to the whole list (no locking needed).Supplicate
How can I send a reference to a part of the list? It need to point to that list cus its connected to the modelview. Or should I just pass a piece (not ref) to the thread, and then from the main thread call the model and do the change through that? I have seriously no idea how to solve this the best way and it keeps crashing...Hoboken
You can't send a reference to parts of the list, my proposal was to split the list up, and hand it over to the thread, when the thread is done with it, merge the sub-list together again. From you last comment, that might not be the best solution. A better solution would be emit a signal from the thread back to the main thread, and update the file list there. E.g., do this in the main thread "fileList_[iterator_].uploadFailed();" and have the FileUploader class hold a const ref. to the file list (to avoid modifying the list in the threads).Supplicate
Btw, with this implementation, can I really pass "this" as a parent, since "this" is created in a different thread. I know this is the case for objects inherited from QThread, but how about QOBjects moved to a thread?Hoboken
In my example that would be fine (init func), as it will be called in the threads event-loop. QObject's that are move to a different thread should not have a parent, the QObject and its children will change thread affinity (btw. sorry for the late reply)Supplicate

© 2022 - 2024 — McMap. All rights reserved.