annotate 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
rev   line source
Chris@320 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@320 2
Chris@320 3 /*
Chris@320 4 Sonic Visualiser
Chris@320 5 An audio file viewer and annotation editor.
Chris@320 6 Centre for Digital Music, Queen Mary, University of London.
Chris@320 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@320 8
Chris@320 9 This program is free software; you can redistribute it and/or
Chris@320 10 modify it under the terms of the GNU General Public License as
Chris@320 11 published by the Free Software Foundation; either version 2 of the
Chris@320 12 License, or (at your option) any later version. See the file
Chris@320 13 COPYING included with this distribution for more information.
Chris@320 14 */
Chris@320 15
Chris@331 16 #include "ModelTransformerFactory.h"
Chris@320 17
Chris@331 18 #include "FeatureExtractionModelTransformer.h"
Chris@331 19 #include "RealTimeEffectModelTransformer.h"
Chris@320 20
Chris@332 21 #include "TransformFactory.h"
Chris@332 22
Chris@389 23 #include "base/AudioPlaySource.h"
Chris@389 24
Chris@320 25 #include "plugin/FeatureExtractionPluginFactory.h"
Chris@320 26 #include "plugin/RealTimePluginFactory.h"
Chris@320 27 #include "plugin/PluginXml.h"
Chris@320 28
Chris@320 29 #include "data/model/DenseTimeValueModel.h"
Chris@320 30
Chris@475 31 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@320 32
Chris@320 33 #include <iostream>
Chris@320 34 #include <set>
Chris@1727 35 #include <map>
Chris@320 36
Chris@320 37 #include <QRegExp>
Chris@1703 38 #include <QMutexLocker>
Chris@320 39
Chris@850 40 using std::vector;
Chris@1727 41 using std::set;
Chris@1727 42 using std::map;
Chris@850 43
Chris@331 44 ModelTransformerFactory *
Chris@331 45 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
Chris@320 46
Chris@331 47 ModelTransformerFactory *
Chris@331 48 ModelTransformerFactory::getInstance()
Chris@320 49 {
Chris@320 50 return m_instance;
Chris@320 51 }
Chris@320 52
Chris@331 53 ModelTransformerFactory::~ModelTransformerFactory()
Chris@320 54 {
Chris@320 55 }
Chris@320 56
Chris@350 57 ModelTransformer::Input
Chris@350 58 ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
Chris@1727 59 const vector<Model *> &candidateInputModels,
Chris@350 60 Model *defaultInputModel,
Chris@389 61 AudioPlaySource *source,
Chris@1048 62 sv_frame_t startFrame,
Chris@1048 63 sv_frame_t duration,
Chris@653 64 UserConfigurator *configurator)
Chris@320 65 {
Chris@1703 66 QMutexLocker locker(&m_mutex);
Chris@1703 67
Chris@1582 68 ModelTransformer::Input input(nullptr);
Chris@350 69
Chris@350 70 if (candidateInputModels.empty()) return input;
Chris@320 71
Chris@320 72 //!!! This will need revision -- we'll have to have a callback
Chris@320 73 //from the dialog for when the candidate input model is changed,
Chris@320 74 //as we'll need to reinitialise the channel settings in the dialog
Chris@345 75 Model *inputModel = candidateInputModels[0];
Chris@320 76 QStringList candidateModelNames;
Chris@345 77 QString defaultModelName;
Chris@653 78 QMap<QString, Model *> modelMap;
Chris@930 79 for (int i = 0; i < (int)candidateInputModels.size(); ++i) {
Chris@320 80 QString modelName = candidateInputModels[i]->objectName();
Chris@320 81 QString origModelName = modelName;
Chris@320 82 int dupcount = 1;
Chris@653 83 while (modelMap.contains(modelName)) {
Chris@320 84 modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
Chris@320 85 }
Chris@320 86 modelMap[modelName] = candidateInputModels[i];
Chris@320 87 candidateModelNames.push_back(modelName);
Chris@345 88 if (candidateInputModels[i] == defaultInputModel) {
Chris@345 89 defaultModelName = modelName;
Chris@345 90 }
Chris@320 91 }
Chris@320 92
Chris@350 93 QString id = transform.getPluginIdentifier();
Chris@320 94
Chris@653 95 bool ok = true;
Chris@350 96 QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
Chris@320 97
Chris@1264 98 SVDEBUG << "ModelTransformer: last configuration for identifier " << transform.getIdentifier() << ": " << configurationXml << endl;
Chris@320 99
Chris@1582 100 Vamp::PluginBase *plugin = nullptr;
Chris@320 101
Chris@1225 102 if (RealTimePluginFactory::instanceFor(id)) {
Chris@320 103
Chris@1264 104 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating real-time plugin" << endl;
Chris@1264 105
Chris@320 106 RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
Chris@320 107
Chris@1040 108 sv_samplerate_t sampleRate = inputModel->getSampleRate();
Chris@930 109 int blockSize = 1024;
Chris@930 110 int channels = 1;
Chris@653 111 if (source) {
Chris@1321 112 sampleRate = source->getSourceSampleRate();
Chris@320 113 blockSize = source->getTargetBlockSize();
Chris@320 114 channels = source->getTargetChannelCount();
Chris@320 115 }
Chris@320 116
Chris@320 117 RealTimePluginInstance *rtp = factory->instantiatePlugin
Chris@320 118 (id, 0, 0, sampleRate, blockSize, channels);
Chris@320 119
Chris@320 120 plugin = rtp;
Chris@1225 121
Chris@1225 122 } else {
Chris@1225 123
Chris@1264 124 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating Vamp plugin" << endl;
Chris@1225 125
Chris@1225 126 Vamp::Plugin *vp =
Chris@1225 127 FeatureExtractionPluginFactory::instance()->instantiatePlugin
Chris@1225 128 (id, float(inputModel->getSampleRate()));
Chris@1225 129
Chris@1225 130 plugin = vp;
Chris@320 131 }
Chris@320 132
Chris@320 133 if (plugin) {
Chris@320 134
Chris@350 135 // Ensure block size etc are valid
Chris@350 136 TransformFactory::getInstance()->
Chris@350 137 makeContextConsistentWithPlugin(transform, plugin);
Chris@320 138
Chris@350 139 // Prepare the plugin with any existing parameters already
Chris@350 140 // found in the transform
Chris@350 141 TransformFactory::getInstance()->
Chris@350 142 setPluginParameters(transform, plugin);
Chris@350 143
Chris@350 144 // For this interactive usage, we want to override those with
Chris@350 145 // whatever the user chose last time around
Chris@350 146 PluginXml(plugin).setParametersFromXml(configurationXml);
Chris@320 147
Chris@653 148 if (configurator) {
Chris@653 149 ok = configurator->configure(input, transform, plugin,
Chris@653 150 inputModel, source,
Chris@653 151 startFrame, duration,
Chris@653 152 modelMap,
Chris@653 153 candidateModelNames,
Chris@653 154 defaultModelName);
Chris@320 155 }
Chris@320 156
Chris@516 157
Chris@350 158 TransformFactory::getInstance()->
Chris@350 159 makeContextConsistentWithPlugin(transform, plugin);
Chris@350 160
Chris@350 161 configurationXml = PluginXml(plugin).toXmlString();
Chris@320 162
Chris@1264 163 SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: got configuration, deleting plugin" << endl;
Chris@1264 164
Chris@653 165 delete plugin;
Chris@320 166 }
Chris@320 167
Chris@350 168 if (ok) {
Chris@350 169 m_lastConfigurations[transform.getIdentifier()] = configurationXml;
Chris@350 170 input.setModel(inputModel);
Chris@350 171 }
Chris@320 172
Chris@350 173 return input;
Chris@320 174 }
Chris@320 175
Chris@331 176 ModelTransformer *
Chris@850 177 ModelTransformerFactory::createTransformer(const Transforms &transforms,
Chris@350 178 const ModelTransformer::Input &input)
Chris@320 179 {
Chris@1582 180 ModelTransformer *transformer = nullptr;
Chris@320 181
Chris@850 182 QString id = transforms[0].getPluginIdentifier();
Chris@320 183
Chris@1225 184 if (RealTimePluginFactory::instanceFor(id)) {
Chris@350 185
Chris@350 186 transformer =
Chris@850 187 new RealTimeEffectModelTransformer(input, transforms[0]);
Chris@350 188
Chris@320 189 } else {
Chris@1225 190
Chris@1225 191 transformer =
Chris@1225 192 new FeatureExtractionModelTransformer(input, transforms);
Chris@320 193 }
Chris@320 194
Chris@850 195 if (transformer) transformer->setObjectName(transforms[0].getIdentifier());
Chris@331 196 return transformer;
Chris@320 197 }
Chris@320 198
Chris@320 199 Model *
Chris@350 200 ModelTransformerFactory::transform(const Transform &transform,
Chris@361 201 const ModelTransformer::Input &input,
Chris@877 202 QString &message,
Chris@877 203 AdditionalModelHandler *handler)
Chris@320 204 {
Chris@690 205 SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl;
Chris@408 206
Chris@850 207 Transforms transforms;
Chris@850 208 transforms.push_back(transform);
Chris@877 209 vector<Model *> mm = transformMultiple(transforms, input, message, handler);
Chris@1582 210 if (mm.empty()) return nullptr;
Chris@850 211 else return mm[0];
Chris@320 212 }
Chris@320 213
Chris@849 214 vector<Model *>
Chris@849 215 ModelTransformerFactory::transformMultiple(const Transforms &transforms,
Chris@849 216 const ModelTransformer::Input &input,
Chris@877 217 QString &message,
Chris@877 218 AdditionalModelHandler *handler)
Chris@849 219 {
Chris@849 220 SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl;
Chris@849 221
Chris@1703 222 QMutexLocker locker(&m_mutex);
Chris@1703 223
Chris@849 224 ModelTransformer *t = createTransformer(transforms, input);
Chris@850 225 if (!t) return vector<Model *>();
Chris@849 226
Chris@877 227 if (handler) {
Chris@877 228 m_handlers[t] = handler;
Chris@877 229 }
Chris@849 230
Chris@849 231 m_runningTransformers.insert(t);
Chris@849 232
Chris@877 233 connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
Chris@877 234
Chris@849 235 t->start();
Chris@850 236 vector<Model *> models = t->detachOutputModels();
Chris@849 237
Chris@850 238 if (!models.empty()) {
Chris@849 239 QString imn = input.getModel()->objectName();
Chris@849 240 QString trn =
Chris@849 241 TransformFactory::getInstance()->getTransformFriendlyName
Chris@850 242 (transforms[0].getIdentifier());
Chris@924 243 for (int i = 0; i < (int)models.size(); ++i) {
Chris@850 244 if (imn != "") {
Chris@850 245 if (trn != "") {
Chris@850 246 models[i]->setObjectName(tr("%1: %2").arg(imn).arg(trn));
Chris@850 247 } else {
Chris@850 248 models[i]->setObjectName(imn);
Chris@850 249 }
Chris@850 250 } else if (trn != "") {
Chris@850 251 models[i]->setObjectName(trn);
Chris@849 252 }
Chris@849 253 }
Chris@849 254 } else {
Chris@849 255 t->wait();
Chris@849 256 }
Chris@849 257
Chris@849 258 message = t->getMessage();
Chris@849 259
Chris@850 260 return models;
Chris@849 261 }
Chris@849 262
Chris@320 263 void
Chris@331 264 ModelTransformerFactory::transformerFinished()
Chris@320 265 {
Chris@320 266 QObject *s = sender();
Chris@331 267 ModelTransformer *transformer = dynamic_cast<ModelTransformer *>(s);
Chris@320 268
Chris@690 269 // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
Chris@320 270
Chris@331 271 if (!transformer) {
Chris@1429 272 cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl;
Chris@1429 273 return;
Chris@320 274 }
Chris@320 275
Chris@1727 276 m_mutex.lock();
Chris@1727 277
Chris@331 278 if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
Chris@843 279 cerr << "WARNING: ModelTransformerFactory::transformerFinished("
Chris@331 280 << transformer
Chris@331 281 << "): I have no record of this transformer running!"
Chris@843 282 << endl;
Chris@320 283 }
Chris@320 284
Chris@331 285 m_runningTransformers.erase(transformer);
Chris@320 286
Chris@1727 287 map<AdditionalModelHandler *, vector<Model *>> toNotifyOfMore;
Chris@1727 288 vector<AdditionalModelHandler *> toNotifyOfNoMore;
Chris@1727 289
Chris@877 290 if (m_handlers.find(transformer) != m_handlers.end()) {
Chris@877 291 if (transformer->willHaveAdditionalOutputModels()) {
Chris@877 292 vector<Model *> mm = transformer->detachAdditionalOutputModels();
Chris@1727 293 toNotifyOfMore[m_handlers[transformer]] = mm;
Chris@878 294 } else {
Chris@1727 295 toNotifyOfNoMore.push_back(m_handlers[transformer]);
Chris@877 296 }
Chris@877 297 m_handlers.erase(transformer);
Chris@877 298 }
Chris@877 299
Chris@1727 300 m_mutex.unlock();
Chris@1727 301
Chris@1727 302 // These calls have to be made without the mutex held, as they may
Chris@1727 303 // ultimately call back on us (e.g. we have one baroque situation
Chris@1727 304 // where this could trigger a command to create a layer, which
Chris@1727 305 // triggers the command history to clip the stack, which deletes a
Chris@1727 306 // spare old model, which calls back on our modelAboutToBeDeleted)
Chris@1727 307
Chris@1727 308 for (const auto &i: toNotifyOfMore) {
Chris@1727 309 i.first->moreModelsAvailable(i.second);
Chris@1727 310 }
Chris@1727 311 for (AdditionalModelHandler *handler: toNotifyOfNoMore) {
Chris@1727 312 handler->noMoreModelsAvailable();
Chris@1727 313 }
Chris@1727 314
Chris@1079 315 if (transformer->isAbandoned()) {
Chris@1079 316 if (transformer->getMessage() != "") {
Chris@1079 317 emit transformFailed("", transformer->getMessage());
Chris@1079 318 }
Chris@1079 319 }
Chris@1079 320
Chris@331 321 transformer->wait(); // unnecessary but reassuring
Chris@331 322 delete transformer;
Chris@320 323 }
Chris@320 324
Chris@320 325 void
Chris@331 326 ModelTransformerFactory::modelAboutToBeDeleted(Model *m)
Chris@320 327 {
Chris@328 328 TransformerSet affected;
Chris@320 329
Chris@1703 330 {
Chris@1703 331 QMutexLocker locker(&m_mutex);
Chris@1703 332
Chris@1703 333 for (TransformerSet::iterator i = m_runningTransformers.begin();
Chris@1703 334 i != m_runningTransformers.end(); ++i) {
Chris@320 335
Chris@1703 336 ModelTransformer *t = *i;
Chris@320 337
Chris@1703 338 if (t->getInputModel() == m) {
Chris@1703 339 affected.insert(t);
Chris@1703 340 } else {
Chris@1703 341 vector<Model *> mm = t->getOutputModels();
Chris@1703 342 for (int i = 0; i < (int)mm.size(); ++i) {
Chris@1703 343 if (mm[i] == m) affected.insert(t);
Chris@1703 344 }
Chris@850 345 }
Chris@320 346 }
Chris@320 347 }
Chris@320 348
Chris@328 349 for (TransformerSet::iterator i = affected.begin();
Chris@320 350 i != affected.end(); ++i) {
Chris@320 351
Chris@331 352 ModelTransformer *t = *i;
Chris@320 353
Chris@320 354 t->abandon();
Chris@320 355
Chris@320 356 t->wait(); // this should eventually call back on
Chris@331 357 // transformerFinished, which will remove from
Chris@328 358 // m_runningTransformers and delete.
Chris@320 359 }
Chris@320 360 }
Chris@320 361
Chris@1703 362 bool
Chris@1703 363 ModelTransformerFactory::haveRunningTransformers() const
Chris@1703 364 {
Chris@1703 365 QMutexLocker locker(&m_mutex);
Chris@1703 366
Chris@1703 367 return (!m_runningTransformers.empty());
Chris@1703 368 }