raiseQThreadAffinityException cores java instead of throwing an exception
Describe the bug When trying to warn about thread affinity, QTJambi sometimes cores the JVM
To Reproduce In our not very complicated code, it reporoduces often at random moments
Expected behavior It should correctly return java exception even if one of thread objects is corrupted
Screenshots Stacktrace of the exception, it happens at different moments, but stacktrace is always the same:
vcruntime140.dll!__RTtypeid(void * inptr) Строка 166 C++ QtJambi6.dll!qtjambi_from_qobject() + 44 байт Нет данных QtJambi6.dll!JavaException::raiseQThreadAffinityException() + 90 байт Нет данных QtJambi6.dll!00007ff81c27645b() Нет данных Qt6Core.dll!QInternal::activateCallbacks(QInternal::Callback cb, void * * parameters) Строка 4254 C++ Qt6Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Строка 1054 C++ [Внедренный фрейм] Qt6Core.dll!QCoreApplication::sendEvent(QObject *) Строка 1475 C++ Qt6Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Строка 1832 C++ Qt6Gui.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Строка 80 C++ Qt6Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Строка 476 C++ Qt6Gui.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Строка 73 C++ [Внедренный фрейм] Qt6Core.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag>) Строка 139 C++ Qt6Core.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Строка 230 C++ Qt6Core.dll!QCoreApplication::exec() Строка 1382 C++ [Внешний код]
Faulty code:
void JavaException::raiseQThreadAffinityException(JNIEnv* env, const char *message QTJAMBI_STACKTRACEINFO_DECL , jobject t1, QThread* t2, QThread* t3){
jstring jmessage = message ? env->NewStringUTF(message) : nullptr;
check(env);
jthrowable t = Java::QtJambi::QThreadAffinityException::newInstance(env,jmessage, t1,
qtjambi_cast<jobject>(env, t2),
qtjambi_cast<jobject>(env, t3)
); // <-- on this line one of qtjambi_cast throws c++ exception that is not catched and so it crashes jvm
raiseJavaException( QTJAMBI_STACKTRACEINFO_DECL_USE );
}
System (please complete the following information):
- OS: Windows
- Java version 11 Azul
- QtJambi version 6.2.5
- Qt version 6.2.5
**Additional information I assume this happens because our code is actively using threads so some of them are destroyed before raiseQThreadAffinityException is called
Perhaps the fact that it just shows this in logs all the time the app is running is related: Exception thrown at 0x0000026D234FB7E7 in java.exe: 0xC0000005: Access violation while reading at address 0x0000000000000008. Thread 0x21e8 exited with code 20115 (0x4e93). Thread 0x5e54 exited with code 20115 (0x4e93). Thread 0x6380 exited with code 20115 (0x4e93). Thread 0x12b8 exited with code 20115 (0x4e93). Thread 0x5940 exited with code 20115 (0x4e93). Thread 0x41a8 exited with code 20115 (0x4e93). Thread 0x4138 exited with code 20115 (0x4e93). Thread 0x3628 exited with code 20115 (0x4e93). Thread 0x2eb4 exited with code 20115 (0x4e93). Thread 0x45ac exited with code 20115 (0x4e93). Thread 0x102c exited with code 20115 (0x4e93). Thread 0x2fb4 exited with code 20115 (0x4e93). Exception thrown at 0x0000026D1C561B18 in java.exe: 0xC0000005: Access violation while reading at address 0x0000026D115B0008. Thread 0x109c exited with code 20115 (0x4e93). Thread 0x210c exited with code 20115 (0x4e93). Thread 0x6264 exited with code 20115 (0x4e93). Thread 0x6080 exited with code 20115 (0x4e93). Thread 0x5098 exited with code 20115 (0x4e93). Thread 0x170c exited with code 20115 (0x4e93).
Is it possible for you to attach .pdb files for qt jambi libs? Without them it's very hard to debug, since I can't use sources and repository code always fails to compile on my visual studio
It also fails like this:
[External code]
vcruntime140.dll!__RTtypeid(void * inptr) Line 166
to d:\a01\_work\4\s\src\vctools\crt\vcruntime\src\eh\rtti.cpp(166)
QtJambi6.dll!qtjambi_from_qobject() + 44 bytes
QtJambi6.dll!JavaException::raiseQThreadAffinityException() + 90 bytes
QtJambi6.dll!00007ff87cac645b()
Qt6Core.dll!QInternal::activateCallbacks(QInternal::Callback cb, void * * parameters) Line 4254
to C:\Users\qt\work\qt\qtbase\src\corelib\global\qglobal.cpp(4254)
Qt6Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1054
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qcoreapplication.cpp(1054)
[Injected Frame] Qt6Core.dll!QCoreApplication::sendEvent(QObject *) Line 1475
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qcoreapplication.cpp(1475)
Qt6Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1832
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qcoreapplication.cpp(1832)
Qt6Gui.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 80
to C:\Users\qt\work\qt\qtbase\src\gui\platform\windows\qwindowsguieventdispatcher.cpp(80)
Qt6Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 476
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp(476)
Qt6Gui.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 73
to C:\Users\qt\work\qt\qtbase\src\gui\platform\windows\qwindowsguieventdispatcher.cpp(73)
[Injected frame] Qt6Core.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag>) Line 139
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qeventloop.cpp(139)
Qt6Core.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 230
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qeventloop.cpp(230)
Qt6Core.dll!QCoreApplication::exec() Line 1382
to C:\Users\qt\work\qt\qtbase\src\corelib\kernel\qcoreapplication.cpp(1382)
[External code]
Thanks for the bug report. I'll look at it on Monday.
As a first impression: It looks like, one of the QThreads is a dangling pointer. Thus typeid(*pointer) crashes. Could you show me the Java code causing this situation?
Thus typeid(*pointer) crashes. Could you show me the Java code causing this situation?
Unfortunately there is no a single point of failure, it happens at random moments of interacting with qml components using mouse, sometimes even that is not required and it just crashes right after showing the qml ui.
@omix I believe it would be easy to find exact cause if you could provide .pdb files for msvc_2019 windows build of qtjambi 6.2.5, then I would be able to see what exactly causes it in the debugger
Please try to run your program with JVM argument -Dio.qt.disable-thread-affinity-check=true. This disables the thread check causing the crash. I assume, then the program will crash due to a thread-affine event call. But however, maybe this crash may at least give you more information. Hopefully.
@omix with flag for disabling jambi thread checks, app runs just fine and doesn't crash at all. When I enbled qtjambi thread checks again, I managed to reproduce the crash in a few seconds after the app had started. I suspect jambi is wrong with its' thread check here.
Can you give me example code to reproduce it?
@omix unfortunately I can't send you soruces right now because they have too9 much company code, but I am trying to reproduce the situation in some demo. Could you please in parallel create .pdb files for qtjamvi v6.2.5? Sources always fail to compile for me for msvc2019 compiler so I can't make them myself.
So far I can tell that it's thrown in https://github.com/OmixVisualization/qtjambi/blob/b0447ef9a6db97c16acbc4c0e28e1df97ce1d6c2/src/cpp/qtjambi/qtjambi_core.cpp#L6745, qtjambi_thread_affine_event_notify method, although I am not sure yet how that happens
I already identified the line where to expect the crash. Actually, the crash is not caused by the exception but shortly before creating the exception. The exception is actually a Java exception to be caught in Java. However, there is an error with real time type information of one of the thread pointers. It seems one of it is dangling. Well, but that makes no sense because QThread::currentThread() does not return a dangling pointer and also a QObject is always associated to a QThread object, which is therefore never deleted before the last QObject instance is deleted.
Well, there could be one scenario I did not check until now: the QThread is created and deleted in native code (e.g. by QML engine). There is a QObject associated to this thread that survives the deletion of the QThread object. This might be caused by bringing the object to Java or creating it in Java. then, it is managed by Java GC and could survive native QThread.
Sources always fail to compile for me for msvc2019 compiler so I can't make them myself.
What doesn't work with compiling? I actually uploaded two bug fixes today for compile issues other people reported recently.
Compile works now.. I'll try to generate .pdbs
Use QtJambi debug dll's. Therefore, start your program with -Dio.qt.debug=debug.
I am already running with -Dio.qt.debug=debug. It didn't add any new information. Vs says "dll was built without symbols information"
I am not familiar with VS. Do you have Qt Creator?
I've managed to make qt debug flag work, for that you also need to build -debug verion of all jars, forked to a new issue #67
The pre-configured setting qtjambi.configuration=debug_and_release compiles both debug and release binaries. However, I think you need Qt as debug built as it is shipped by Qt installer.
Could you send me any example code leading to this bug? I would like to solve this for the next release I am currently preparing.
@omix I still can't provide you with our internal IB code, however I have found that even when I use qtjambi 6.2.5 built locally in release mode, it almost never crashes. After that I checked and found that latest 6.2.5 jars from Maven are dated by July 5th, whilethis git repo has a lot of bugfixes after that date: Bugfix Issue #64 Fixed crash of deployer when using self-compiled Qt Peter Droste 8/15/2022 11:25 AM Bugfix Issue #64 Fixed crash of generator when docs are not available Peter Droste 8/15/2022 10:53 AM Bugfix Issue #64 Peter Droste 7/27/2022 1:48 PM Bugfix Issue #62 Peter Droste 7/27/2022 10:47 AM Patch version 6.3.2a Bugfix Issue #63 Peter Droste 7/26/2022 11:41 AM Patch version 6.3.2 Peter Droste 7/6/2022 10:16 PM Patch version 6.3.2 Bugfix Issue #57 Bugfix Issue #56 Bugfix Issue #55 Peter Droste 7/6/2022 10:03 PM
Perhaps one of them also fixed the issue we are seeing now. Could you please repost 6.2.5 jars to Maven?
I don't think these small bugfixes deal with your issue. However, I inserted a blindfix. I removed the Java convertion for the threads in raiseQThreadAffinityException.
Do you have an idea what kind of object is used from the wrong thread? Is it QWidget instance?
@omix I think it's some qobject from non ui thread passed to QML js function. Unfortunately, it never repoduced for me on debug/release build I made for 6.2.5, only when using jars from maven
Just to understand, you create a qobject in a thread pass it to QML where you perform actions on the object? Does the thread have an event loop?
That's a guess, I don't know for sure. Will you upload new 6.2.X qtjambi build?
Yes, but that takes a while. I am performing last tests. Then I'll continue with release steps. Should be ready until weekend. Hopefully.
@omix this code immediatley crashes when you call dispose() on a running thread. Is this the same behavior as in qt?
import io.qt.core.QThread;
import io.qt.widgets.QApplication;
import io.qt.widgets.QPushButton;
import io.qt.widgets.QVBoxLayout;
import io.qt.widgets.QWidget;
public class AffinityTest {
public static void main(String[] args) {
QApplication.initialize(args);
new MainWin().show();
QApplication.exec();
QApplication.shutdown();
}
private static class MainWin extends QWidget {
QThread testThread = new QThread("Bg thread");
public MainWin() {
QVBoxLayout layout = new QVBoxLayout(this);
setLayout(layout);
QPushButton test = new QPushButton("Test thread affinity");
layout.addWidget(test);
//test.clicked.connect(this::testClicked); not needed
testThread.setDaemon(true);
testThread.start();
testThread.dispose(); // crashes right here
}
}
}
Oh, this program even crashes in C++.
Never delete a running thread (dispose() right after start()). Use quit() to exit the thread's event loop. Use join() to make sure the thread finished.
hey @omix I see that you've uploaded new 5.15 build, will you also push 6.2 today/on the weekend?
Yes, it takes a day to upload. I'm just finishing the last artifacts. Then, give the maven database a couple of hours.