@@ -168,11 +168,11 @@ struct BridgeRtClientControl : public CarlaRingBufferControl<SmallStackBuffer> { | |||||
return jackbridge_sem_post(&data->sem.client); | return jackbridge_sem_post(&data->sem.client); | ||||
} | } | ||||
bool waitForServer(const uint secs) noexcept | |||||
bool waitForServer(const uint secs, bool* const timedOut) noexcept | |||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(data != nullptr, false); | CARLA_SAFE_ASSERT_RETURN(data != nullptr, false); | ||||
return jackbridge_sem_timedwait(&data->sem.server, secs); | |||||
return jackbridge_sem_timedwait(&data->sem.server, secs, timedOut); | |||||
} | } | ||||
PluginBridgeRtClientOpcode readOpcode() noexcept | PluginBridgeRtClientOpcode readOpcode() noexcept | ||||
@@ -270,8 +270,8 @@ struct BridgeNonRtServerControl : public CarlaRingBufferControl<HugeStackBuffer> | |||||
BridgeNonRtServerControl() noexcept | BridgeNonRtServerControl() noexcept | ||||
: mutex(), | : mutex(), | ||||
filename(), | |||||
data(nullptr) | |||||
filename(), | |||||
data(nullptr) | |||||
{ | { | ||||
carla_zeroChar(shm, 64); | carla_zeroChar(shm, 64); | ||||
jackbridge_shm_init(shm); | jackbridge_shm_init(shm); | ||||
@@ -1178,10 +1178,19 @@ public: | |||||
protected: | protected: | ||||
void run() override | void run() override | ||||
{ | { | ||||
bool timedOut, quitReceived = false; | |||||
for (; ! shouldThreadExit();) | for (; ! shouldThreadExit();) | ||||
{ | { | ||||
if (! fShmRtClientControl.waitForServer(5)) | |||||
if (! fShmRtClientControl.waitForServer(5, &timedOut)) | |||||
{ | { | ||||
/* | |||||
* As a special case we ignore timeouts for plugin bridges. | |||||
* Some big Windows plugins (Kontakt, FL Studio VST) can time out when initializing or doing UI stuff. | |||||
* If any other error happens the plugin bridge is stopped. | |||||
*/ | |||||
if (timedOut) continue; | |||||
carla_stderr2("Bridge timed-out, final post..."); | carla_stderr2("Bridge timed-out, final post..."); | ||||
fShmRtClientControl.postClient(); | fShmRtClientControl.postClient(); | ||||
carla_stderr2("Bridge timed-out, done."); | carla_stderr2("Bridge timed-out, done."); | ||||
@@ -1400,6 +1409,7 @@ protected: | |||||
} | } | ||||
case kPluginBridgeRtClientQuit: | case kPluginBridgeRtClientQuit: | ||||
quitReceived = true; | |||||
signalThreadShouldExit(); | signalThreadShouldExit(); | ||||
break; | break; | ||||
} | } | ||||
@@ -1410,6 +1420,18 @@ protected: | |||||
} | } | ||||
callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr); | callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr); | ||||
if (! quitReceived) | |||||
{ | |||||
const char* const message("Plugin bridge error, process thread has stopped"); | |||||
const std::size_t messageSize(std::strlen(message)); | |||||
const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); | |||||
fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerError); | |||||
fShmNonRtServerControl.writeUInt(messageSize); | |||||
fShmNonRtServerControl.writeCustomData(message, messageSize); | |||||
fShmNonRtServerControl.commitWrite(); | |||||
} | |||||
} | } | ||||
// called from process thread above | // called from process thread above | ||||
@@ -242,13 +242,13 @@ struct BridgeRtClientControl : public CarlaRingBufferControl<SmallStackBuffer> { | |||||
setRingBuffer(nullptr, false); | setRingBuffer(nullptr, false); | ||||
} | } | ||||
bool waitForClient(const uint secs) noexcept | |||||
bool waitForClient(const uint secs, bool* const timedOut) noexcept | |||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(data != nullptr, false); | CARLA_SAFE_ASSERT_RETURN(data != nullptr, false); | ||||
jackbridge_sem_post(&data->sem.server); | jackbridge_sem_post(&data->sem.server); | ||||
return jackbridge_sem_timedwait(&data->sem.client, secs); | |||||
return jackbridge_sem_timedwait(&data->sem.client, secs, timedOut); | |||||
} | } | ||||
void writeOpcode(const PluginBridgeRtClientOpcode opcode) noexcept | void writeOpcode(const PluginBridgeRtClientOpcode opcode) noexcept | ||||
@@ -741,6 +741,7 @@ public: | |||||
fInitError(false), | fInitError(false), | ||||
fSaved(false), | fSaved(false), | ||||
fTimedOut(false), | fTimedOut(false), | ||||
fTimedError(false), | |||||
fLastPongCounter(-1), | fLastPongCounter(-1), | ||||
fBridgeBinary(), | fBridgeBinary(), | ||||
fBridgeThread(engine, this), | fBridgeThread(engine, this), | ||||
@@ -786,7 +787,7 @@ public: | |||||
fShmRtClientControl.commitWrite(); | fShmRtClientControl.commitWrite(); | ||||
if (! fTimedOut) | if (! fTimedOut) | ||||
fShmRtClientControl.waitForClient(3); | |||||
waitForClient("stopping", 3); | |||||
} | } | ||||
fBridgeThread.stopThread(3000); | fBridgeThread.stopThread(3000); | ||||
@@ -1317,6 +1318,8 @@ public: | |||||
void activate() noexcept override | void activate() noexcept override | ||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(! fTimedError,); | |||||
{ | { | ||||
const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | ||||
@@ -1324,18 +1327,17 @@ public: | |||||
fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
} | } | ||||
bool timedOut = true; | |||||
fTimedOut = false; | |||||
try { | try { | ||||
timedOut = waitForClient(1); | |||||
waitForClient("activate", 2); | |||||
} CARLA_SAFE_EXCEPTION("activate - waitForClient"); | } CARLA_SAFE_EXCEPTION("activate - waitForClient"); | ||||
if (! timedOut) | |||||
fTimedOut = false; | |||||
} | } | ||||
void deactivate() noexcept override | void deactivate() noexcept override | ||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(! fTimedError,); | |||||
{ | { | ||||
const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | ||||
@@ -1343,14 +1345,11 @@ public: | |||||
fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
} | } | ||||
bool timedOut = true; | |||||
fTimedOut = false; | |||||
try { | try { | ||||
timedOut = waitForClient(1); | |||||
waitForClient("deactivate", 2); | |||||
} CARLA_SAFE_EXCEPTION("deactivate - waitForClient"); | } CARLA_SAFE_EXCEPTION("deactivate - waitForClient"); | ||||
if (! timedOut) | |||||
fTimedOut = false; | |||||
} | } | ||||
void process(const float** const audioIn, float** const audioOut, const float** const cvIn, float** const cvOut, const uint32_t frames) override | void process(const float** const audioIn, float** const audioOut, const float** const cvIn, float** const cvOut, const uint32_t frames) override | ||||
@@ -1358,7 +1357,7 @@ public: | |||||
// -------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------- | ||||
// Check if active | // Check if active | ||||
if (fTimedOut || ! pData->active) | |||||
if (fTimedOut || fTimedError || ! pData->active) | |||||
{ | { | ||||
// disable any output sound | // disable any output sound | ||||
for (uint32_t i=0; i < pData->audioOut.count; ++i) | for (uint32_t i=0; i < pData->audioOut.count; ++i) | ||||
@@ -1599,6 +1598,7 @@ public: | |||||
bool processSingle(const float** const audioIn, float** const audioOut, const float** const cvIn, float** const cvOut, const uint32_t frames) | bool processSingle(const float** const audioIn, float** const audioOut, const float** const cvIn, float** const cvOut, const uint32_t frames) | ||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(! fTimedError, false); | |||||
CARLA_SAFE_ASSERT_RETURN(frames > 0, false); | CARLA_SAFE_ASSERT_RETURN(frames > 0, false); | ||||
if (pData->audioIn.count > 0) | if (pData->audioIn.count > 0) | ||||
@@ -1673,10 +1673,12 @@ public: | |||||
fShmRtClientControl.commitWrite(); | fShmRtClientControl.commitWrite(); | ||||
} | } | ||||
if (! waitForClient(2)) | |||||
waitForClient("process", 1); | |||||
if (fTimedOut) | |||||
{ | { | ||||
pData->singleMutex.unlock(); | pData->singleMutex.unlock(); | ||||
return true; | |||||
return false; | |||||
} | } | ||||
for (uint32_t i=0; i < fInfo.aOuts; ++i) | for (uint32_t i=0; i < fInfo.aOuts; ++i) | ||||
@@ -1763,7 +1765,7 @@ public: | |||||
fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
} | } | ||||
fShmRtClientControl.waitForClient(1); | |||||
waitForClient("buffersize", 1); | |||||
} | } | ||||
void sampleRateChanged(const double newSampleRate) override | void sampleRateChanged(const double newSampleRate) override | ||||
@@ -1775,7 +1777,7 @@ public: | |||||
fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
} | } | ||||
fShmRtClientControl.waitForClient(1); | |||||
waitForClient("samplerate", 1); | |||||
} | } | ||||
void offlineModeChanged(const bool isOffline) override | void offlineModeChanged(const bool isOffline) override | ||||
@@ -1786,7 +1788,7 @@ public: | |||||
fShmNonRtClientControl.commitWrite(); | fShmNonRtClientControl.commitWrite(); | ||||
} | } | ||||
fShmRtClientControl.waitForClient(1); | |||||
waitForClient("offline", 1); | |||||
} | } | ||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
@@ -2273,10 +2275,20 @@ public: | |||||
carla_zeroChar(error, errorSize+1); | carla_zeroChar(error, errorSize+1); | ||||
fShmNonRtServerControl.readCustomData(error, errorSize); | fShmNonRtServerControl.readCustomData(error, errorSize); | ||||
pData->engine->setLastError(error); | |||||
if (fInitiated) | |||||
{ | |||||
pData->engine->callback(ENGINE_CALLBACK_ERROR, pData->id, 0, 0, 0.0f, error); | |||||
fInitError = true; | |||||
fInitiated = true; | |||||
// just in case | |||||
pData->engine->setLastError(error); | |||||
fInitError = true; | |||||
} | |||||
else | |||||
{ | |||||
pData->engine->setLastError(error); | |||||
fInitError = true; | |||||
fInitiated = true; | |||||
} | |||||
} break; | } break; | ||||
} | } | ||||
} | } | ||||
@@ -2453,6 +2465,7 @@ private: | |||||
bool fInitError; | bool fInitError; | ||||
bool fSaved; | bool fSaved; | ||||
bool fTimedOut; | bool fTimedOut; | ||||
bool fTimedError; | |||||
int32_t fLastPongCounter; | int32_t fLastPongCounter; | ||||
@@ -2505,21 +2518,26 @@ private: | |||||
fShmRtClientControl.commitWrite(); | fShmRtClientControl.commitWrite(); | ||||
waitForClient(); | |||||
waitForClient("resize-pool"); | |||||
} | } | ||||
bool waitForClient(const uint secs = 5) | |||||
void waitForClient(const char* const action, const uint secs = 5) | |||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(! fTimedOut, false); | |||||
CARLA_SAFE_ASSERT_RETURN(! fTimedOut,); | |||||
CARLA_SAFE_ASSERT_RETURN(! fTimedError,); | |||||
if (! fShmRtClientControl.waitForClient(secs)) | |||||
if (fShmRtClientControl.waitForClient(secs, &fTimedOut)) | |||||
return; | |||||
if (fTimedOut) | |||||
{ | { | ||||
carla_stderr("waitForClient() timeout here"); | |||||
fTimedOut = true; | |||||
return false; | |||||
carla_stderr("waitForClient(%s) timeout here", action); | |||||
} | |||||
else | |||||
{ | |||||
fTimedError = true; | |||||
carla_stderr("waitForClient(%s) error while waiting", action); | |||||
} | } | ||||
return true; | |||||
} | } | ||||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginBridge) | CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginBridge) | ||||
@@ -393,7 +393,7 @@ __cdecl bool jackbridge_set_property_change_callback(jack_client_t* client, Jack | |||||
__cdecl bool jackbridge_sem_init(void* sem) noexcept; | __cdecl bool jackbridge_sem_init(void* sem) noexcept; | ||||
__cdecl void jackbridge_sem_destroy(void* sem) noexcept; | __cdecl void jackbridge_sem_destroy(void* sem) noexcept; | ||||
__cdecl bool jackbridge_sem_post(void* sem) noexcept; | __cdecl bool jackbridge_sem_post(void* sem) noexcept; | ||||
__cdecl bool jackbridge_sem_timedwait(void* sem, uint secs) noexcept; | |||||
__cdecl bool jackbridge_sem_timedwait(void* sem, uint secs, bool* timedOut) noexcept; | |||||
__cdecl bool jackbridge_shm_is_valid(const void* shm) noexcept; | __cdecl bool jackbridge_shm_is_valid(const void* shm) noexcept; | ||||
__cdecl void jackbridge_shm_init(void* shm) noexcept; | __cdecl void jackbridge_shm_init(void* shm) noexcept; | ||||
@@ -55,20 +55,18 @@ bool jackbridge_sem_post(void* sem) noexcept | |||||
#endif | #endif | ||||
} | } | ||||
bool jackbridge_sem_timedwait(void* sem, uint secs) noexcept | |||||
bool jackbridge_sem_timedwait(void* sem, uint secs, bool* timedOut) noexcept | |||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(timedOut != nullptr, false); | |||||
#ifdef JACKBRIDGE_DUMMY | #ifdef JACKBRIDGE_DUMMY | ||||
return false; | return false; | ||||
#else | #else | ||||
if (carla_sem_timedwait((sem_t*)sem, secs)) | if (carla_sem_timedwait((sem_t*)sem, secs)) | ||||
{ | |||||
*timedOut = false; | |||||
return true; | return true; | ||||
/* | |||||
* As a sspecial case we ignore timeouts for plugin bridges. | |||||
* Some big Windows plugins (Kontakt, FL Studio VST) can time out when initializing. | |||||
* If any other error happens the plugin bridge is stopped. | |||||
*/ | |||||
if (errno == ETIMEDOUT) | |||||
return true; | |||||
} | |||||
*timedOut = (errno == ETIMEDOUT); | |||||
return false; | return false; | ||||
#endif | #endif | ||||
} | } | ||||
@@ -540,9 +540,9 @@ bool jackbridge_sem_post(void* sem) noexcept | |||||
return getBridgeInstance().sem_post_ptr(sem); | return getBridgeInstance().sem_post_ptr(sem); | ||||
} | } | ||||
bool jackbridge_sem_timedwait(void* sem, uint secs) noexcept | |||||
bool jackbridge_sem_timedwait(void* sem, uint secs, bool* timedOut) noexcept | |||||
{ | { | ||||
return getBridgeInstance().sem_timedwait_ptr(sem, secs); | |||||
return getBridgeInstance().sem_timedwait_ptr(sem, secs, timedOut); | |||||
} | } | ||||
bool jackbridge_shm_is_valid(const void* shm) noexcept | bool jackbridge_shm_is_valid(const void* shm) noexcept | ||||
@@ -109,7 +109,7 @@ typedef bool (__cdecl *jackbridgesym_set_property_change_callback)(jack_client_t | |||||
typedef bool (__cdecl *jackbridgesym_sem_init)(void* sem); | typedef bool (__cdecl *jackbridgesym_sem_init)(void* sem); | ||||
typedef void (__cdecl *jackbridgesym_sem_destroy)(void* sem); | typedef void (__cdecl *jackbridgesym_sem_destroy)(void* sem); | ||||
typedef bool (__cdecl *jackbridgesym_sem_post)(void* sem); | typedef bool (__cdecl *jackbridgesym_sem_post)(void* sem); | ||||
typedef bool (__cdecl *jackbridgesym_sem_timedwait)(void* sem, uint secs); | |||||
typedef bool (__cdecl *jackbridgesym_sem_timedwait)(void* sem, uint secs, bool* timedOut); | |||||
typedef bool (__cdecl *jackbridgesym_shm_is_valid)(const void* shm); | typedef bool (__cdecl *jackbridgesym_shm_is_valid)(const void* shm); | ||||
typedef void (__cdecl *jackbridgesym_shm_init)(void* shm); | typedef void (__cdecl *jackbridgesym_shm_init)(void* shm); | ||||
typedef void (__cdecl *jackbridgesym_shm_attach)(void* shm, const char* name); | typedef void (__cdecl *jackbridgesym_shm_attach)(void* shm, const char* name); | ||||
@@ -105,7 +105,7 @@ enum PluginBridgeNonRtServerOpcode { | |||||
kPluginBridgeNonRtServerReady, | kPluginBridgeNonRtServerReady, | ||||
kPluginBridgeNonRtServerSaved, | kPluginBridgeNonRtServerSaved, | ||||
kPluginBridgeNonRtServerUiClosed, | kPluginBridgeNonRtServerUiClosed, | ||||
kPluginBridgeNonRtServerError | |||||
kPluginBridgeNonRtServerError // uint/size, str[] | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||