Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execption while closing Mixxx #11737

Closed
JoergAtGithub opened this issue Jul 11, 2023 · 14 comments
Closed

Execption while closing Mixxx #11737

JoergAtGithub opened this issue Jul 11, 2023 · 14 comments
Labels

Comments

@JoergAtGithub
Copy link
Member

JoergAtGithub commented Jul 11, 2023

Bug Description

@m0dB This exception happens for me, after closing Mixxx by clicking on the X button on the top right of the main window.


Exception thrown: read access violation.
pWaveformWidget->**** was 0xFFFFFFFFFFFFFFE7.
	mixxx.exe!`anonymous namespace'::shouldRenderWaveform(WaveformWidgetAbstract * pWaveformWidget=0x00000186e5bddb50) Line 66	C++
 	mixxx.exe!WaveformWidgetFactory::render() Line 725	C++
 	mixxx.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<>,QtPrivate::List<>,void,void (__cdecl WaveformWidgetFactory::*)(void)>::call(void(WaveformWidgetFactory::*)() f=0x00000027c18ed3d0, WaveformWidgetFactory * o=0x00000186a17a5600, void * * arg=0x00000186f3ed3e20) Line 152	C++
 	mixxx.exe!QtPrivate::FunctionPointer<void (__cdecl WaveformWidgetFactory::*)(void)>::call<QtPrivate::List<>,void>(void(WaveformWidgetFactory::*)() f=0x00000027c18ed430, WaveformWidgetFactory * o=0x00000186a17a5600, void * * arg=0x00000186f3ed3e20) Line 186	C++
 	mixxx.exe!QtPrivate::QSlotObject<void (__cdecl WaveformWidgetFactory::*)(void),QtPrivate::List<>,void>::impl(int which=1, QtPrivate::QSlotObjectBase * this_=0x00000186d1059550, QObject * r=0x00000186a17a5600, void * * a=0x00000186f3ed3e20, bool * ret=0x0000000000000000) Line 419	C++
 	[External Code]	
 	mixxx.exe!MixxxApplication::notify(QObject * target=0x00000186a17a5600, QEvent * event=0x00000186f3ed3dd0) Line 170	C++
 	[External Code]	
 	mixxx.exe!GLSLWaveformRendererSignal::~GLSLWaveformRendererSignal() Line 39	C++
 	mixxx.exe!GLSLWaveformRendererRGBSignal::~GLSLWaveformRendererRGBSignal() Line 100	C++
 	[External Code]	
 	mixxx.exe!WaveformWidgetRenderer::~WaveformWidgetRenderer() Line 74	C++
 	mixxx.exe!WaveformWidgetAbstract::~WaveformWidgetAbstract() Line 14	C++
 	[External Code]	
 	mixxx.exe!GLSLWaveformWidget::~GLSLWaveformWidget() Line 19	C++
 	mixxx.exe!GLSLRGBWaveformWidget::~GLSLRGBWaveformWidget() Line 54	C++
 	[External Code]	
 	mixxx.exe!WWidget::~WWidget() Line 23	C++
 	mixxx.exe!WWaveformViewer::~WWaveformViewer() Line 55	C++
 	[External Code]	
 	mixxx.exe!WWidgetGroup::event(QEvent * pEvent=0x00000186e0e737c0) Line 222	C++
 	[External Code]	
 	mixxx.exe!MixxxApplication::notify(QObject * target=0x00000186ddaaa050, QEvent * event=0x00000186e0e737c0) Line 170	C++
 	[External Code]	
 	mixxx.exe!MixxxMainWindow::~MixxxMainWindow() Line 436	C++
 	mixxx.exe!`anonymous namespace'::runMixxx(MixxxApplication * pApp=0x00000027c18ffaa8, const CmdlineArgs & args={...}) Line 86	C++
 	mixxx.exe!main(int argc=1, char * * argv=0x0000018682f95ce0) Line 213	C++
 	[External Code]	

Version

2.4.0 Beta

OS

Windows 11

@m0dB
Copy link
Contributor

m0dB commented Jul 13, 2023

Does this also happen with the allshader waveforms?

@JoergAtGithub
Copy link
Member Author

Additional information:

  • This happens only with Debug builds (Target x64_off). I guess, because unhandled exceptions in Qt are ignored for other targets - not sure about this.
  • This happen for GL and GLSL waveform types, but not for non-GL and all-shaders waveform types
  • A similar exception occurs also when I try to swith the waveform type from GL or GLSL to another

@m0dB
Copy link
Contributor

m0dB commented Jul 14, 2023

It looks like the WaveformWidget is being destroyed while it is still in use in the render signal/slot. I don't understand how this is possible; I believe the following code intends avoiding this from happening:

There is this signal when the widget is destroyed:

connect(pWidget, &QWidget::destroyed, this, &WWaveformViewer::slotWidgetDead);

This sets the pointer in WWaveformViewer to null:

    void slotWidgetDead() {
       m_waveformWidget = nullptr;
    }

And the conflictive access happens through the WWaveformViewer

WaveformWidgetAbstract* pWaveformWidget = m_waveformWidgetHolders[i].m_waveformWidget;

Maybe doing this on destroyed is already too late?

I will try with a debug build to see if I can reproduce this, or run with ASAN.

@m0dB
Copy link
Contributor

m0dB commented Jul 14, 2023

Btw, when you say: (Target x64_off), what do you mean by that? What does this do?

@JoergAtGithub
Copy link
Member Author

x64_off is a CMake target for our Windows builds meaning:

  • build for AMD64 instruction set
  • compiler optimizations off (
    if(NOT OPTIMIZE STREQUAL "off")
    )
  • using the debug binaries from our VCPKG buildenv.

@m0dB
Copy link
Contributor

m0dB commented Jul 15, 2023

I tried on macos with a debug build and address sanitizer, but mixxx quits cleanly. Now, I think the macos buildenv doesn't come with a debug build of Qt (@daschuer will know) so that might make a difference.

@JoergAtGithub
Copy link
Member Author

There are two versions of the buildenv, one with Debug and Release binaries and a smaller one with Release binaries only.
If you run macos_arm64-cross-release_buildenv.sh you get the one with Release binaries only.
If you run macos_arm64-cross_buildenv.sh you get the full one inluding Debug and Release binaries.

@m0dB
Copy link
Contributor

m0dB commented Jul 15, 2023

In that case I believe I used debug binaries. But I'll double-check.

@JoergAtGithub
Copy link
Member Author

If you properly selected CMAKE_BUILD_TYPE=Debug, you would see an error, if you downloaded the wrong build type:

mixxx/CMakeLists.txt

Lines 56 to 58 in 3de2963

if(CMAKE_BUILD_TYPE MATCHES "Debug")
message(FATAL_ERROR "Did you download the Mixxx build environment using ´${CMAKE_SOURCE_DIR}/tools/macos_buildenv.bat´")
else()

@daschuer
Copy link
Member

daschuer commented Jul 15, 2023

I assume the pWaveformWidget is dangling.
This is because the widgets are deleted by the QObject tree before the factory and its VSyncThread is deleted.

I think we need to used a QPointer in WaveformWidgetHolder for m_waveformWidget.
Alternative we may call WaveformWidgetFactory::destroyWidgets() before the skin is deleted.

An other issue could be a signal on the Qt queue from the VSyncThread that arrives just after the Factory has been deleted. This can be fixed by using QObject::deleteLater() instead of WaveformWidgetFactory::destroy();

Maybe we should fix both.

@m0dB
Copy link
Contributor

m0dB commented Jul 15, 2023

An other issue could be a signal on the Qt queue from the VSyncThread that arrives just after the Factory has been deleted. This can be fixed by using QObject::deleteLater() instead of WaveformWidgetFactory::destroy();

I don't think that is true. From the QObject::~QObject documentation:
All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue.

@daschuer
Copy link
Member

Yes, you are right, I missed the fact that there is a wait() statement in the destructor. This way we can not have a race condition that would lead to the described issue.

@uklotzde
Copy link
Contributor

Qt6 reports this error (doesn't occur with Qt5):

 [Main] fatal /usr/include/qt6/QtCore/qobjectdefs_impl.h:119 ASSERT failure in WWaveformViewer: "Called object is not of the correct type (class destructor may have already run)", file /usr/include/qt6/QtCore/qobjectdefs_impl.h, line 119
#0  0x00007ffff23b6844 in __pthread_kill_implementation () at /lib64/libc.so.6
#1  0x00007ffff2365abe in raise () at /lib64/libc.so.6
#2  0x00007ffff234e87f in abort () at /lib64/libc.so.6
#3  0x00007ffff2ad782d in QtPrivate::lcNativeInterface() [clone .cold] () at /lib64/libQt6Core.so.6
#4  0x00007ffff2ad8289 in  () at /lib64/libQt6Core.so.6
#5  0x00007ffff2ad77dd in qBadAlloc() () at /lib64/libQt6Core.so.6
#6  0x0000000001219791 in QtPrivate::assertObjectType<WWaveformViewer>(QObject*) (o=0x4c7d9870) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:108
#7  QtPrivate::assertObjectType<WWaveformViewer>(QObject*) (o=0x4c7d9870) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:108
#8  QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, void (WWaveformViewer::*)()>::call(void (WWaveformViewer::*)(), WWaveformViewer*, void**)
    (arg=<optimized out>, o=0x4c7d9870, f=(void (WWaveformViewer::*)(WWaveformViewer * const)) 0x1218a80 <WWaveformViewer::slotWidgetDead()>)
    at /usr/include/qt6/QtCore/qobjectdefs_impl.h:134
#9  QtPrivate::FunctionPointer<void (WWaveformViewer::*)()>::call<QtPrivate::List<>, void>(void (WWaveformViewer::*)(), WWaveformViewer*, void**)
    (arg=<optimized out>, o=0x4c7d9870, f=(void (WWaveformViewer::*)(WWaveformViewer * const)) 0x1218a80 <WWaveformViewer::slotWidgetDead()>)
    at /usr/include/qt6/QtCore/qobjectdefs_impl.h:172
#10 QtPrivate::QSlotObject<void (WWaveformViewer::*)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)
    (which=<optimized out>, this_=<optimized out>, r=0x4c7d9870, a=<optimized out>, ret=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:384
#11 0x00007ffff2bde394 in void doActivate<false>(QObject*, int, void**) () at /lib64/libQt6Core.so.6
#12 0x00007ffff2bd4ed1 in QObject::destroyed(QObject*) () at /lib64/libQt6Core.so.6
#13 0x00007ffff621843b in QWidget::~QWidget() () at /lib64/libQt6Widgets.so.6
#14 0x000000000123d727 in WGLWidget::~WGLWidget() (this=<optimized out>, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wglwidgetqopengl.cpp:24
#15 0x000000000123c591 in allshader::WaveformWidget::~WaveformWidget() (this=<optimized out>, __in_chrg=<optimized out>)
    at /home/uk/volumes/Build/cpp/mixxx/src/waveform/widgets/allshader/waveformwidget.cpp:21
#16 0x000000000123a090 in allshader::LRRGBWaveformWidget::~LRRGBWaveformWidget() (this=0x514f89c0, __in_chrg=<optimized out>)
    at /home/uk/volumes/Build/cpp/mixxx/src/waveform/widgets/allshader/lrrgbwaveformwidget.cpp:28
#17 allshader::LRRGBWaveformWidget::~LRRGBWaveformWidget() (this=0x514f89c0, __in_chrg=<optimized out>)
    at /home/uk/volumes/Build/cpp/mixxx/src/waveform/widgets/allshader/lrrgbwaveformwidget.cpp:28
#18 0x00007ffff2bd772d in QObjectPrivate::deleteChildren() () at /lib64/libQt6Core.so.6
#19 0x00007ffff6218448 in QWidget::~QWidget() () at /lib64/libQt6Widgets.so.6
#20 0x000000000119cc6f in WWidget::~WWidget() (this=<optimized out>, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwidget.cpp:33
#21 0x0000000001215658 in WWaveformViewer::~WWaveformViewer() (this=0x4c7d9870, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/trackdroptarget.h:14
#22 WWaveformViewer::~WWaveformViewer() (this=0x4c7d9870, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwaveformviewer.cpp:55
#23 0x00007ffff2bd772d in QObjectPrivate::deleteChildren() () at /lib64/libQt6Core.so.6
#24 0x00007ffff6218448 in QWidget::~QWidget() () at /lib64/libQt6Widgets.so.6
#25 0x000000000119c773 in WWidgetGroup::~WWidgetGroup() (this=0x4c800b00, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwidgetgroup.h:17
#26 WWidgetGroup::~WWidgetGroup() (this=0x4c800b00, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwidgetgroup.h:17
#27 0x00007ffff2bd772d in QObjectPrivate::deleteChildren() () at /lib64/libQt6Core.so.6
#28 0x00007ffff6218448 in QWidget::~QWidget() () at /lib64/libQt6Widgets.so.6
#29 0x000000000119c773 in WWidgetGroup::~WWidgetGroup() (this=0x4c41fcf0, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwidgetgroup.h:17
#30 WWidgetGroup::~WWidgetGroup() (this=0x4c41fcf0, __in_chrg=<optimized out>) at /home/uk/volumes/Build/cpp/mixxx/src/widget/wwidgetgroup.h:17
#31 0x00007ffff2bcfc59 in QObject::event(QEvent*) () at /lib64/libQt6Core.so.6

@Swiftb0y
Copy link
Member

Swiftb0y commented Aug 31, 2023

My best guess would be a lifetime issue where the waveformwidget outlives the waveformviewer.
Why is ~WaveformWidget called inside ~WWaveformViewer anyways? WaveformWidget does not own any WaveformWidget instance!
Getting rid of signal connections here would probably avoid the crash, but I'm not sure of the consequences that would have.

void WWaveformViewer::setWaveformWidget(WaveformWidgetAbstract* waveformWidget) {
if (m_waveformWidget) {
QWidget* pWidget = m_waveformWidget->getWidget();
disconnect(pWidget, &QWidget::destroyed, this, &WWaveformViewer::slotWidgetDead);
}
m_waveformWidget = waveformWidget;
if (m_waveformWidget) {
QWidget* pWidget = m_waveformWidget->getWidget();
connect(pWidget, &QWidget::destroyed, this, &WWaveformViewer::slotWidgetDead);
m_waveformWidget->getWidget()->setMouseTracking(true);

The proper fix would likely require finding where the ~WaveformWidget call is coming from.

Edit: note to self. I should start rereading the thread more often before commenting to avoid looking like an idiot when I just end up repeating what has already been said. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants