# HG changeset patch # User Chris Cannam # Date 1561025376 -3600 # Node ID 8efce64dd85ec5669cf094df639996eddb0bc78f # Parent d7ae9bfb015e6a9ee45de1f60209704c6635c934 Fix potential deadlock when notifying a handler that more models are [not] available diff -r d7ae9bfb015e -r 8efce64dd85e transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Thu Jun 20 11:09:05 2019 +0100 +++ b/transform/ModelTransformerFactory.cpp Thu Jun 20 11:09:36 2019 +0100 @@ -32,11 +32,14 @@ #include #include +#include #include #include using std::vector; +using std::set; +using std::map; ModelTransformerFactory * ModelTransformerFactory::m_instance = new ModelTransformerFactory; @@ -53,7 +56,7 @@ ModelTransformer::Input ModelTransformerFactory::getConfigurationForTransform(Transform &transform, - const std::vector &candidateInputModels, + const vector &candidateInputModels, Model *defaultInputModel, AudioPlaySource *source, sv_frame_t startFrame, @@ -260,8 +263,6 @@ void ModelTransformerFactory::transformerFinished() { - QMutexLocker locker(&m_mutex); - QObject *s = sender(); ModelTransformer *transformer = dynamic_cast(s); @@ -272,6 +273,8 @@ return; } + m_mutex.lock(); + if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { cerr << "WARNING: ModelTransformerFactory::transformerFinished(" << transformer @@ -281,16 +284,34 @@ m_runningTransformers.erase(transformer); + map> toNotifyOfMore; + vector toNotifyOfNoMore; + if (m_handlers.find(transformer) != m_handlers.end()) { if (transformer->willHaveAdditionalOutputModels()) { vector mm = transformer->detachAdditionalOutputModels(); - m_handlers[transformer]->moreModelsAvailable(mm); + toNotifyOfMore[m_handlers[transformer]] = mm; } else { - m_handlers[transformer]->noMoreModelsAvailable(); + toNotifyOfNoMore.push_back(m_handlers[transformer]); } m_handlers.erase(transformer); } + m_mutex.unlock(); + + // These calls have to be made without the mutex held, as they may + // ultimately call back on us (e.g. we have one baroque situation + // where this could trigger a command to create a layer, which + // triggers the command history to clip the stack, which deletes a + // spare old model, which calls back on our modelAboutToBeDeleted) + + for (const auto &i: toNotifyOfMore) { + i.first->moreModelsAvailable(i.second); + } + for (AdditionalModelHandler *handler: toNotifyOfNoMore) { + handler->noMoreModelsAvailable(); + } + if (transformer->isAbandoned()) { if (transformer->getMessage() != "") { emit transformFailed("", transformer->getMessage());