comparison transform/ModelTransformerFactory.cpp @ 1727:8efce64dd85e

Fix potential deadlock when notifying a handler that more models are [not] available
author Chris Cannam
date Thu, 20 Jun 2019 11:09:36 +0100
parents b17fb3a4560c
children fe3f7f8df3a3
comparison
equal deleted inserted replaced
1726:d7ae9bfb015e 1727:8efce64dd85e
30 30
31 #include <vamp-hostsdk/PluginHostAdapter.h> 31 #include <vamp-hostsdk/PluginHostAdapter.h>
32 32
33 #include <iostream> 33 #include <iostream>
34 #include <set> 34 #include <set>
35 #include <map>
35 36
36 #include <QRegExp> 37 #include <QRegExp>
37 #include <QMutexLocker> 38 #include <QMutexLocker>
38 39
39 using std::vector; 40 using std::vector;
41 using std::set;
42 using std::map;
40 43
41 ModelTransformerFactory * 44 ModelTransformerFactory *
42 ModelTransformerFactory::m_instance = new ModelTransformerFactory; 45 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
43 46
44 ModelTransformerFactory * 47 ModelTransformerFactory *
51 { 54 {
52 } 55 }
53 56
54 ModelTransformer::Input 57 ModelTransformer::Input
55 ModelTransformerFactory::getConfigurationForTransform(Transform &transform, 58 ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
56 const std::vector<Model *> &candidateInputModels, 59 const vector<Model *> &candidateInputModels,
57 Model *defaultInputModel, 60 Model *defaultInputModel,
58 AudioPlaySource *source, 61 AudioPlaySource *source,
59 sv_frame_t startFrame, 62 sv_frame_t startFrame,
60 sv_frame_t duration, 63 sv_frame_t duration,
61 UserConfigurator *configurator) 64 UserConfigurator *configurator)
258 } 261 }
259 262
260 void 263 void
261 ModelTransformerFactory::transformerFinished() 264 ModelTransformerFactory::transformerFinished()
262 { 265 {
263 QMutexLocker locker(&m_mutex);
264
265 QObject *s = sender(); 266 QObject *s = sender();
266 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s); 267 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
267 268
268 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl; 269 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
269 270
270 if (!transformer) { 271 if (!transformer) {
271 cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl; 272 cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl;
272 return; 273 return;
273 } 274 }
274 275
276 m_mutex.lock();
277
275 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { 278 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
276 cerr << "WARNING: ModelTransformerFactory::transformerFinished(" 279 cerr << "WARNING: ModelTransformerFactory::transformerFinished("
277 << transformer 280 << transformer
278 << "): I have no record of this transformer running!" 281 << "): I have no record of this transformer running!"
279 << endl; 282 << endl;
280 } 283 }
281 284
282 m_runningTransformers.erase(transformer); 285 m_runningTransformers.erase(transformer);
283 286
287 map<AdditionalModelHandler *, vector<Model *>> toNotifyOfMore;
288 vector<AdditionalModelHandler *> toNotifyOfNoMore;
289
284 if (m_handlers.find(transformer) != m_handlers.end()) { 290 if (m_handlers.find(transformer) != m_handlers.end()) {
285 if (transformer->willHaveAdditionalOutputModels()) { 291 if (transformer->willHaveAdditionalOutputModels()) {
286 vector<Model *> mm = transformer->detachAdditionalOutputModels(); 292 vector<Model *> mm = transformer->detachAdditionalOutputModels();
287 m_handlers[transformer]->moreModelsAvailable(mm); 293 toNotifyOfMore[m_handlers[transformer]] = mm;
288 } else { 294 } else {
289 m_handlers[transformer]->noMoreModelsAvailable(); 295 toNotifyOfNoMore.push_back(m_handlers[transformer]);
290 } 296 }
291 m_handlers.erase(transformer); 297 m_handlers.erase(transformer);
292 } 298 }
293 299
300 m_mutex.unlock();
301
302 // These calls have to be made without the mutex held, as they may
303 // ultimately call back on us (e.g. we have one baroque situation
304 // where this could trigger a command to create a layer, which
305 // triggers the command history to clip the stack, which deletes a
306 // spare old model, which calls back on our modelAboutToBeDeleted)
307
308 for (const auto &i: toNotifyOfMore) {
309 i.first->moreModelsAvailable(i.second);
310 }
311 for (AdditionalModelHandler *handler: toNotifyOfNoMore) {
312 handler->noMoreModelsAvailable();
313 }
314
294 if (transformer->isAbandoned()) { 315 if (transformer->isAbandoned()) {
295 if (transformer->getMessage() != "") { 316 if (transformer->getMessage() != "") {
296 emit transformFailed("", transformer->getMessage()); 317 emit transformFailed("", transformer->getMessage());
297 } 318 }
298 } 319 }