Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: vaychick от Май 23, 2011, 16:26



Название: Работа с dll во втором потоке
Отправлено: vaychick от Май 23, 2011, 16:26
Здравствуйте, есть ф-я которая в цикле вызывает ф-и динамически подгружаемой dll, задача была вынести эту ф-ю в отдельный поток - вынес, но есть проблема при обращении к функциям dll приложение вылетает с SIGSERV - Segmentation fault. Причем если убрать вызовы этих dll ф-й все работает нормально.

Собственно вопрос, какие есть подводные камни при использовании dll во втором потоке, 2 дня бъюсь и никак не могу найти причину вылета программы. Причем без вызова dll поток нормально стартует, работает и завершается.

Та же самая dll с теми-же ф-ми работала стабильно в главном потоке.
Реализовал второй поток след. образом: Внутри наследника QThread, создается объект который работает с dll, он переносится во второй поток методом moveToThread, затем стартует цикл обработки событий, а ф-я которая крутит цикл, это слот, который вызывается после посылкой сигнала извне.

исходный код не привожу, так как из контекста его трудно достать, если нужно будет выложу.
Могу кратко описать логику в псевдокоде

Код:
class thread : public QThread {
    Worker * worker;
public:
    void init() {
         worker = new Worker;
     }
    void start() {
        worker->moveToThread(this);
        QThread::start();
    }
    void run() {
        exec();
    }
};

class Worker : public QObject {
    someMember;
    someMember2;
    someMember3;
    typedef dllFoo * dllFooProto;
    QLibrary dll;
public:
    Worker() {
    dll.load();
    dllFoo = (dllFooProto)dll.resolve("dllFoo");
}
public slots:
    void foo() {
        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }
};

dll пробовал грузить и в конструкторе (main thread) и внутри foo()

если закоментировать вызов dllFoo и самому модифицировать someMember-ры, то все нормально, создается впечатление что dll что-то внутри себя делает, что конфликтует с потоками, пробовал обрамлять вызов dllFoo в mutex.lock() \ unlock(), эффекта нет.

Примечательно то, что ф-и из dll не используются в основном потоке, и свойства someMember так-же не используются, мотивов для блокировок я не вижу, приложение просто вылетает после вызова ф-й dll. Причем dll обращается к внешнему устройству и более никак не связано с приложением.

читал тут http://habrahabr.ru/blogs/qt_software/115830/ (http://habrahabr.ru/blogs/qt_software/115830/) и сдесь на форуме и документацию Qt, поэтому кажеться какбудто поток организован верно, хотя я могу ошибаться.
Кажется будто причина в dll, такое может быть? если да, то какие меры можно предпренять?


Название: Re: Работа с dll во втором потоке
Отправлено: twp от Май 23, 2011, 17:14
по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток


Название: Re: Работа с dll во втором потоке
Отправлено: xokc от Май 23, 2011, 20:14
Честно говоря ни разу не имел проблем с загрузкой DLL из не основного потока. Видимо проблема именно в Вашей dll.


Название: Re: Работа с dll во втором потоке
Отправлено: vaychick от Май 24, 2011, 08:13
по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток

Но ведь она часть объекта Worker. Я пробовал и другой вариант, непосредственно в foo() подгружать dll:

void foo() {
    QLibrary dll2("dllPath");
    dll2.load();
    if(!(dllFoo = (dllFooProto)dll2.resolve("dllFoo")))
        qDebug() << "error";

        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }

добавлю так-же что Worker не имеет предка, но наследуется от абстрактного класса, поэтому мне кажется, что должно переноситься нормально методом moveToThread

Цитировать
Честно говоря ни разу не имел проблем с загрузкой DLL из не основного потока. Видимо проблема именно в Вашей dll.
Тоже начинаю об этом думать, но dllFoo просто обращается к устройству и считывает из него поток байтов в someMember, какие коллизии могут создаться из-за многопоточности не понимаю


Название: Re: Работа с dll во втором потоке
Отправлено: twp от Май 24, 2011, 09:33
по ходу QLibrary создается в главном потоке и не переносится (как Worker) в рабочий поток

Но ведь она часть объекта Worker. Я пробовал и другой вариант, непосредственно в foo() подгружать dll:

void foo() {
    QLibrary dll2("dllPath");
    dll2.load();
    if(!(dllFoo = (dllFooProto)dll2.resolve("dllFoo")))
        qDebug() << "error";

        while(1) {
            QCoreApplication::processEvents();
            if(isExit) return;//Внешнее событие устанавливает флаг для выхода из цикла
            Sleep(1000);
           
            dllFoo(someMember, &someMember2, &someMember3);//Ф-я модифицирует содержимое someMember2 и someMember3
        }
    }

добавлю так-же что Worker не имеет предка, но наследуется от абстрактного класса, поэтому мне кажется, что должно переноситься нормально методом moveToThread
согласно документации moveToThread переносит объект и его детей, но поскольку в конструктор QLibrary не был передан Worker как родитель, то dll не был перенесен в поток вместе с Worker и остался в главной потоке. Кроме того, в потоке используется Sleep. Может проблема не в нем, но не понятно почему не используется QThread::msleep, QThread::sleep или QThread::usleep.


Название: Re: Работа с dll во втором потоке
Отправлено: Igors от Май 24, 2011, 09:42
Не вижу причем тут moveToThread - хоть бы и вообще ничего не переносили, слот должен работать. Разумеется Вы убедились что в главной нитке dllFoo работает нормально. Я бы начал интересоваться

- отключить весь остальной код слота (оставить только вызов dllFoo)
- какой адрес возвращает resolve (сравнить его с возвращаемым в главной)
- стеком краша - вошла ли она в dllFoo и где рухнула

Если это Вындоуз - убедиться что dll создана как "Multithreaded (Debug) DLL"  


Название: Re: Работа с dll во втором потоке
Отправлено: SASA от Май 24, 2011, 10:05
А когда происходит соединение со слотом void foo()? Может надо точно указать тип соединения?


Название: Re: Работа с dll во втором потоке
Отправлено: vaychick от Май 24, 2011, 13:36
Цитировать
согласно документации moveToThread переносит объект и его детей, но поскольку в конструктор QLibrary не был передан Worker как родитель, то dll не был перенесен в поток вместе с Worker и остался в главной потоке. Кроме того, в потоке используется Sleep. Может проблема не в нем, но не понятно почему не используется QThread::msleep, QThread::sleep или QThread::usleep.

Спасибо за информацию насчет переноса свойств объекта, но сейчас тестирую когда dll грузится прям в слоте.
Я не использовал QThread::sleep потому, что Worker не унаследован от QThread, и "не знает" что он свойство класса thread, поэтому использую системныю ф-ю sleep, без вызова dllFoo цикл крутиться нормально.

Цитировать
Не вижу причем тут moveToThread - хоть бы и вообще ничего не переносили, слот должен работать. Разумеется Вы убедились что в главной нитке dllFoo работает нормально.
Да, в главном потоке, точнее вообзе без потоков, работало нормально, пока не начал переделывать.

Цитировать
- отключить весь остальной код слота (оставить только вызов dllFoo)
- какой адрес возвращает resolve (сравнить его с возвращаемым в главной)
- стеком краша - вошла ли она в dllFoo и где рухнула

Реальный код, который сейчас тестирую
Код:
    void ReaderCOM::pollReader() {
    QLibrary dll2("Mr915ApiV10");
    dll2.load();
    if(!(gen2MultiTagIdentify = (APIGen2MultiTagIdentify)dll2.resolve("Gen2MultiTagIdentify")))
        qDebug() << "Gen2MultiTagIdentify";
    if(!(clearIDBuffer = (APIClearIDBuffer)dll2.resolve("ClearIDBuffer")))
        qDebug() << "ClearIDBuffer";

    while(1) {
        qDebug() << "Gen2MultiTagIdentify:" << QString::number((int)gen2MultiTagIdentify, 16);//Вот тут вылет на 3-й итерации
        clearIDBuffer(hReader, readerAddr);
        gen2MultiTagIdentify(hReader, &count, tags, readerAddr);
    }
}

вывод:

Код:
Gen2MultiTagIdentify: "1b621b50" 
Gen2MultiTagIdentify: "1b621b50"
Gen2MultiTagIdentify: "?"

"?" - наверное потому, что на третьей итерации вылетает
Отладчик показывает на это место SIGSERV

Код:
QTextStream &QTextStream::operator<<(const QString &string)
{
    Q_D(QTextStream);
    CHECK_VALID_STREAM(*this);//Вот тут
    d->putString(string);
    return *this;
}

я выводил адреса gen2MultiTagIdentify в конструкторе(основной поток) и в цикле, перед вызовом qDebug() << "Gen2MultiTagIdentify:", они совпадают
Притом вылет происходит на 3-й итерации цикла, а не на первой.
Притом если ничего ны выводить в лог - вылетает все равно


Название: Re: Работа с dll во втором потоке
Отправлено: vaychick от Май 24, 2011, 13:38
Код:
А когда происходит соединение со слотом void foo()? Может надо точно указать тип соединения?
слот вызывается 1 раз из основного потока сигналом, соединение - Qt::QueuedConnection, повторюсь, без ф-й dll все работает и не падает


Название: Re: Работа с dll во втором потоке
Отправлено: Igors от Май 24, 2011, 15:58
Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора). Затем углубляться в изучение dll - в первую очередь какие сигналы она испускает, и могут ли они навредить если посланы из др. нитки


Название: Re: Работа с dll во втором потоке
Отправлено: blood_shadow от Май 24, 2011, 17:04
Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора)
а с помощью mingw в среде Creator'а как такое сделать?


Название: Re: Работа с dll во втором потоке
Отправлено: vaychick от Май 25, 2011, 08:16
Цитировать
Для начала проверить на битую кучу (инструмент зависит от платформы/компилятора). Затем углубляться в изучение dll - в первую очередь какие сигналы она испускает, и могут ли они навредить если посланы из др. нитки

Я честно говоря не в курсе, что за сигналы в DLL, но хочу сказать что dll сторонняя и скомпилирована Microsoft Visual C++ 6.0 DLL, как показал PEid


Название: Re: Работа с dll во втором потоке
Отправлено: Igors от Май 25, 2011, 13:54
а с помощью mingw в среде Creator'а как такое сделать?
Никогда не пользовался этой IDE, поэтому здесь подсказать не могу.

Я честно говоря не в курсе, что за сигналы в DLL, но хочу сказать что dll сторонняя и скомпилирована Microsoft Visual C++ 6.0 DLL, как показал PEid
Надо убедиться что начиная от вызова ф-ции dll и до краша управление нигде не отдается Вам. Если таких callback'ов нет, то пора сделать вывод что это проблема конкретной dll и сосредлточиться на поиске ее новой версии (или переносе вызовов в главную нитку)


Название: Re: Работа с dll во втором потоке
Отправлено: vaychick от Май 25, 2011, 16:02
Цитировать
Надо убедиться что начиная от вызова ф-ции dll и до краша управление нигде не отдается Вам. Если таких callback'ов нет, то пора сделать вывод что это проблема конкретной dll и сосредлточиться на поиске ее новой версии (или переносе вызовов в главную нитку)
На данный момент приложение падает, через несколько инструкций после того, как вызов ф-и из dll отработает, dll весит 63 килобайта и работает с COM портом, начал задумываться о том, чтобы реализовать самому эти ф-и, но пока не могу найти точной спецификации