From fe98a9aa431c1e463d698ece014c60bb99693196 Mon Sep 17 00:00:00 2001 From: DomClark Date: Thu, 24 Aug 2017 22:09:38 +0100 Subject: [PATCH] Fix some VST deadlocks/hangs --- include/MainWindow.h | 8 ++ include/RemotePlugin.h | 8 ++ plugins/vst_base/RemoteVstPlugin.cpp | 199 ++++++++++++++++++++------- plugins/vst_base/VstPlugin.cpp | 20 +-- src/gui/MainWindow.cpp | 12 +- 5 files changed, 186 insertions(+), 61 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 69b430d33..1a58f868c 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "ConfigManager.h" #include "SubWindow.h" @@ -248,4 +249,11 @@ signals: } ; +class AutoSaveThread : public QThread +{ + Q_OBJECT +public: + void run(); +} ; + #endif diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd..725f51c2c 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -1072,6 +1072,14 @@ RemotePluginBase::message RemotePluginBase::waitForMessage( const message & _wm, bool _busy_waiting ) { +#ifndef BUILD_REMOTE_PLUGIN_CLIENT + if( _busy_waiting ) + { + // No point processing events outside of the main thread + _busy_waiting = QThread::currentThread() == + QCoreApplication::instance()->thread(); + } +#endif while( !isInvalid() ) { #ifndef BUILD_REMOTE_PLUGIN_CLIENT diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 03e160a8d..74af607c5 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -44,6 +44,10 @@ #ifdef LMMS_BUILD_LINUX +#ifndef NOMINMAX +#define NOMINMAX +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -67,6 +71,7 @@ #include +#include #include @@ -113,7 +118,7 @@ class RemoteVstPlugin; RemoteVstPlugin * __plugin = NULL; -DWORD __GuiThreadID = 0; +HWND __MessageHwnd = NULL; @@ -244,8 +249,38 @@ public: pthread_mutex_unlock( &m_pluginLock ); } + inline bool isProcessing() const + { + return m_processing; + } + + inline void setProcessing( bool processing ) + { + m_processing = processing; + } + + inline void queueMessage( const message & m ) { + m_messageList.push( m ); + } + + inline bool shouldGiveIdle() const + { + return m_shouldGiveIdle; + } + + inline void setShouldGiveIdle( bool shouldGiveIdle ) + { + m_shouldGiveIdle = shouldGiveIdle; + } + + void idle(); + void processUIThreadMessages(); + static DWORD WINAPI processingThread( LPVOID _param ); - static DWORD WINAPI guiEventLoop( LPVOID _param ); + static bool setupMessageWindow(); + static DWORD WINAPI guiEventLoop(); + static LRESULT CALLBACK messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ); private: @@ -303,6 +338,10 @@ private: bool m_initialized; pthread_mutex_t m_pluginLock; + bool m_processing; + + std::queue m_messageList; + bool m_shouldGiveIdle; float * * m_inputs; @@ -348,6 +387,9 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_windowHeight( 0 ), m_initialized( false ), m_pluginLock(), + m_processing( false ), + m_messageList(), + m_shouldGiveIdle( false ), m_inputs( NULL ), m_outputs( NULL ), m_midiEvents(), @@ -1418,8 +1460,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, SHOW_CALLBACK ("amc: audioMasterIdle\n" ); // call application idle routine (this will // call effEditIdle for all open editors too) - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; case audioMasterPinConnected: @@ -1720,8 +1761,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, case audioMasterUpdateDisplay: SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); // something has changed, update 'multi-fx' display - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; #if kVstVersion > 2 @@ -1754,6 +1794,43 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, +void RemoteVstPlugin::idle() +{ + if( isProcessing() ) + { + setShouldGiveIdle( true ); + return; + } + setProcessing( true ); + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + setProcessing( false ); + // We might have received a message whilst idling + processUIThreadMessages(); +} + + + + +void RemoteVstPlugin::processUIThreadMessages() +{ + setProcessing( true ); + while( m_messageList.size() ) + { + processMessage( m_messageList.front() ); + m_messageList.pop(); + if( shouldGiveIdle() ) + { + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + } + } + setProcessing( false ); +} + + + + DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { RemoteVstPlugin * _this = static_cast( _param ); @@ -1767,7 +1844,7 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) } else { - PostThreadMessage( __GuiThreadID, + PostMessage( __MessageHwnd, WM_USER, ProcessPluginMessage, (LPARAM) new message( m ) ); @@ -1775,7 +1852,7 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) } // notify GUI thread about shutdown - PostThreadMessage( __GuiThreadID, WM_USER, ClosePlugin, 0 ); + PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 ); return 0; } @@ -1783,61 +1860,37 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) -DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +bool RemoteVstPlugin::setupMessageWindow() { - RemoteVstPlugin * _this = static_cast( _param ); - HMODULE hInst = GetModuleHandle( NULL ); if( hInst == NULL ) { - _this->debugMessage( "guiEventLoop(): can't get " + __plugin->debugMessage( "setupMessageWindow(): can't get " "module handle\n" ); - return -1; + return false; } - HWND timerWindow = CreateWindowEx( 0, "LVSL", "dummy", + __MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy", 0, 0, 0, 0, 0, NULL, NULL, hInst, NULL ); + SetWindowLongPtr( __MessageHwnd, GWLP_WNDPROC, + reinterpret_cast( RemoteVstPlugin::messageWndProc ) ); // install GUI update timer - SetTimer( timerWindow, 1000, 50, NULL ); + SetTimer( __MessageHwnd, 1000, 50, NULL ); + return true; +} + + + + +DWORD WINAPI RemoteVstPlugin::guiEventLoop() +{ MSG msg; - - bool quit = false; - while( quit == false && GetMessage( &msg, NULL, 0, 0 ) ) + while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { TranslateMessage( &msg ); DispatchMessage( &msg ); - - if( msg.message == WM_TIMER && _this->isInitialized() ) - { - // give plugin some idle-time for GUI-update - _this->pluginDispatch( effEditIdle ); - } - else if( msg.message == WM_USER ) - { - switch( msg.wParam ) - { - case ProcessPluginMessage: - { - message * m = (message *) msg.lParam; - _this->processMessage( *m ); - delete m; - break; - } - - case GiveIdle: - _this->pluginDispatch( effEditIdle ); - break; - - case ClosePlugin: - quit = true; - break; - - default: - break; - } - } } return 0; @@ -1846,6 +1899,49 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ) +{ + if( uMsg == WM_TIMER && __plugin->isInitialized() ) + { + // give plugin some idle-time for GUI-update + __plugin->idle(); + return 0; + } + else if( uMsg == WM_USER ) + { + switch( wParam ) + { + case ProcessPluginMessage: + { + message * m = (message *) lParam; + __plugin->queueMessage( *m ); + delete m; + if( !__plugin->isProcessing() ) + { + __plugin->processUIThreadMessages(); + } + return 0; + } + + case GiveIdle: + __plugin->idle(); + return 0; + + case ClosePlugin: + PostQuitMessage(0); + return 0; + + default: + break; + } + } + return DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + + + + int main( int _argc, char * * _argv ) { #ifdef SYNC_WITH_SHM_FIFO @@ -1893,7 +1989,10 @@ int main( int _argc, char * * _argv ) if( __plugin->isInitialized() ) { - __GuiThreadID = GetCurrentThreadId(); + if( RemoteVstPlugin::setupMessageWindow() == false ) + { + return -1; + } if( CreateThread( NULL, 0, RemoteVstPlugin::processingThread, __plugin, 0, NULL ) == NULL ) { @@ -1901,7 +2000,7 @@ int main( int _argc, char * * _argv ) "processingThread\n" ); return -1; } - RemoteVstPlugin::guiEventLoop( __plugin ); + RemoteVstPlugin::guiEventLoop(); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 22c9f7c20..9873d28e5 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -389,7 +389,7 @@ int VstPlugin::currentProgram() { lock(); sendMessage( message( IdVstCurrentProgram ) ); - waitForMessage( IdVstCurrentProgram ); + waitForMessage( IdVstCurrentProgram, true ); unlock(); return m_currentProgram; @@ -401,7 +401,7 @@ const QMap & VstPlugin::parameterDump() { lock(); sendMessage( IdVstGetParameterDump ); - waitForMessage( IdVstParameterDump ); + waitForMessage( IdVstParameterDump, true ); unlock(); return m_parameterDump; @@ -528,7 +528,7 @@ void VstPlugin::openPreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( ofd.selectedFiles()[0] ) ) ) ); - waitForMessage( IdLoadPresetFile ); + waitForMessage( IdLoadPresetFile, true ); unlock(); } } @@ -540,7 +540,7 @@ void VstPlugin::setProgram( int index ) { lock(); sendMessage( message( IdVstSetProgram ).addInt( index ) ); - waitForMessage( IdVstSetProgram ); + waitForMessage( IdVstSetProgram, true ); unlock(); } @@ -551,7 +551,7 @@ void VstPlugin::rotateProgram( int offset ) { lock(); sendMessage( message( IdVstRotateProgram ).addInt( offset ) ); - waitForMessage( IdVstRotateProgram ); + waitForMessage( IdVstRotateProgram, true ); unlock(); } @@ -562,7 +562,7 @@ void VstPlugin::loadProgramNames() { lock(); sendMessage( message( IdVstProgramNames ) ); - waitForMessage( IdVstProgramNames ); + waitForMessage( IdVstProgramNames, true ); unlock(); } @@ -599,7 +599,7 @@ void VstPlugin::savePreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( fns ) ) ) ); - waitForMessage( IdSavePresetFile ); + waitForMessage( IdSavePresetFile, true ); unlock(); } } @@ -611,7 +611,7 @@ void VstPlugin::setParam( int i, float f ) { lock(); sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); - //waitForMessage( IdVstSetParameter ); + //waitForMessage( IdVstSetParameter, true ); unlock(); } @@ -640,7 +640,7 @@ void VstPlugin::loadChunk( const QByteArray & _chunk ) QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ). addInt( _chunk.size() ) ); - waitForMessage( IdLoadSettingsFromFile ); + waitForMessage( IdLoadSettingsFromFile, true ); unlock(); } } @@ -659,7 +659,7 @@ QByteArray VstPlugin::saveChunk() addString( QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ) ); - waitForMessage( IdSaveSettingsToFile ); + waitForMessage( IdSaveSettingsToFile, true ); unlock(); a = tf.readAll(); } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 63b931823..f65c96066 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1541,7 +1541,9 @@ void MainWindow::autoSave() "enablerunningautosave" ).toInt() || ! Engine::getSong()->isPlaying() ) ) { - Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); + AutoSaveThread * ast = new AutoSaveThread(); + connect( ast, SIGNAL( finished() ), ast, SLOT( deleteLater() ) ); + ast->start(); autoSaveTimerReset(); // Reset timer } else @@ -1553,3 +1555,11 @@ void MainWindow::autoSave() } } } + + + + +void AutoSaveThread::run() +{ + Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); +}