kwave  18.07.70
Plugin.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  Plugin.cpp - base class of all Kwave plugins
3  -------------------
4  begin : Thu Jul 27 2000
5  copyright : (C) 2000 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <errno.h>
21 #include <math.h>
22 #include <new>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include <QApplication>
27 #include <QProgressDialog>
28 #include <QThread>
29 #include <QTime>
30 #include <QVariantList>
31 #include <QWidget>
32 
33 #include <KLocalizedString>
34 
36 #include "libkwave/Plugin.h"
37 #include "libkwave/PluginManager.h"
38 #include "libkwave/Sample.h"
39 #include "libkwave/SignalManager.h"
40 #include "libkwave/String.h"
41 #include "libkwave/Utils.h"
42 #include "libkwave/WorkerThread.h"
43 
44 #ifdef DEBUG
45 #include <execinfo.h> // for backtrace()
46 #endif
47 
49 #define PROGRESS_UPDATES_PER_SECOND 4
50 
52 #define PROGRESS_MAXIMUM 1000000
53 
54 /*
55  A plugin can be unloaded through two different ways. The possible
56  scenarios are:
57 
58  1. WITHOUT a thread
59  main thread: new --- release
60 
61  2. WITH a plugin thread
62  main thread: new --- use --- start --- release
63  plugin thread: --- run --- release
64 
65  The plugin can be unloaded either in the main thread or in the
66  context of the plugin's thread, depending on what occurs first.
67 
68  */
69 
70 //***************************************************************************
71 Kwave::Plugin::Plugin(QObject *parent, const QVariantList &args)
72  :QObject(Q_NULLPTR),
73  Kwave::Runnable(),
74  m_plugin_manager(qobject_cast<Kwave::PluginManager *>(parent)),
75  m_name(args[0].toString()),
76  m_description(args[1].toString()),
77  m_thread(Q_NULLPTR),
78  m_thread_lock(),
79  m_progress_enabled(true),
80  m_stop(0),
81  m_progress(Q_NULLPTR),
82  m_confirm_cancel(Q_NULLPTR),
83  m_usage_count(1),
84  m_usage_lock(),
85  m_progress_timer(),
86  m_current_progress(-1),
87  m_progress_lock(QMutex::Recursive)
88 {
89  Q_ASSERT(m_plugin_manager);
90  connect(&m_progress_timer, SIGNAL(timeout()),
91  this, SLOT(updateProgressTick()),
92  Qt::DirectConnection);
93 }
94 
95 //***************************************************************************
97 {
98  // inform our owner that we close. This allows the plugin to
99  // delete itself
100  close();
101 
102  // lock usage
103  QMutexLocker lock_usage(&m_usage_lock);
104  {
105  QMutexLocker lock_thread(&m_thread_lock);
106  if (m_thread) {
107  if (m_thread->isRunning()) m_thread->wait(5000);
108  if (m_thread->isRunning()) m_thread->stop();
109  if (m_thread->isRunning()) m_thread->wait(1000);
110  if (m_thread->isRunning()) {
111  // unable to stop the thread
112  qWarning("Kwave::Plugin::stop(): stale thread !");
113  }
114  delete m_thread;
115  m_thread = Q_NULLPTR;
116  }
117  }
118 
119  // finally get rid of the confirm/cancel proxy and the progress dialog
120  closeProgressDialog(this);
121 }
122 
123 //***************************************************************************
124 void Kwave::Plugin::load(QStringList &)
125 {
126 }
127 
128 //***************************************************************************
130 {
131 }
132 
133 //***************************************************************************
134 QStringList *Kwave::Plugin::setup(QStringList &)
135 {
136  QStringList *result = new QStringList();
137  Q_ASSERT(result);
138  return result;
139 }
140 
141 //***************************************************************************
142 int Kwave::Plugin::start(QStringList &)
143 {
144  QMutexLocker lock(&m_thread_lock);
145  m_stop = false;
146 
147  // check: start() must be called from the GUI thread only!
148  Q_ASSERT(this->thread() == QThread::currentThread());
149  Q_ASSERT(this->thread() == qApp->thread());
150 
151  // create a progress dialog for processing mode (not used for pre-listen)
152  if (m_progress_enabled && !m_progress) {
153  m_progress = new QProgressDialog(parentWidget());
154  Q_ASSERT(m_progress);
155  }
156 
157  // set up the progress dialog when in processing (not pre-listen) mode
159  sample_index_t first, last;
160  QList<unsigned int> tracks;
161 
162  selection(&tracks, &first, &last, true);
163  m_progress->setModal(true);
164  m_progress->setVisible(false);
165  m_progress->setMinimumDuration(2000);
166  m_progress->setAutoClose(false);
167  m_progress->setMaximum(PROGRESS_MAXIMUM);
168  m_progress->setValue(0);
169  m_progress->setLabelText(progressText());
170  int h = m_progress->sizeHint().height();
171  int w = m_progress->sizeHint().height();
172  if (w < 4 * h) w = 4 * h;
173  m_progress->setFixedSize(w, h);
174 
175  // use a "proxy" that asks for confirmation of cancel
176  if (!m_confirm_cancel) {
178  Q_NULLPTR, Q_NULLPTR, this, SLOT(cancel()));
179  Q_ASSERT(m_confirm_cancel);
180  }
181  connect(m_progress, SIGNAL(canceled()),
182  m_confirm_cancel, SLOT(cancel()));
183  connect(this, SIGNAL(setProgressText(QString)),
184  m_progress, SLOT(setLabelText(QString)),
185  Qt::QueuedConnection);
186  connect(this, SIGNAL(sigDone(Kwave::Plugin*)),
187  this, SLOT(closeProgressDialog(Kwave::Plugin*)),
188  Qt::QueuedConnection);
189  m_progress->setVisible(true);
190  }
191 
192  return 0;
193 }
194 
195 //***************************************************************************
196 QString Kwave::Plugin::name() const
197 {
198  return m_name;
199 }
200 
201 //***************************************************************************
203 {
204  return m_description;
205 }
206 
207 //***************************************************************************
209 {
210  return i18n("Running plugin '%1'...", name());
211 }
212 
213 //***************************************************************************
215 {
216  cancel();
217 
218  if (m_thread && m_thread->isRunning() &&
219  (QThread::currentThread() == m_thread)) {
220  qWarning("Kwave::Plugin::stop(): plugin '%s' called stop() from "
221  "within it's own worker thread (from run() ?). "
222  "This would produce a deadlock, PLEASE FIX THIS !",
223  DBG(name()));
224 
225 #ifdef DEBUG
226  qDebug("pthread_self()=%p, tid=%p",
227  reinterpret_cast<void *>(QThread::currentThread()),
228  reinterpret_cast<void *>(m_thread));
229  void *buf[256];
230  int n = backtrace(buf, 256);
231  backtrace_symbols_fd(buf, n, 2);
232 #endif
233  return -EBUSY;
234  }
235 
236  {
237  QMutexLocker lock(&m_thread_lock);
238  if (m_thread) {
239  if (m_thread->isRunning()) m_thread->wait(5000);
240  if (m_thread->isRunning()) m_thread->stop();
241  if (m_thread->isRunning()) m_thread->wait(1000);
242  if (m_thread->isRunning()) {
243  // unable to stop the thread
244  qWarning("Kwave::Plugin::stop(): stale thread !");
245  }
246  delete m_thread;
247  m_thread = Q_NULLPTR;
248  }
249  }
250  return 0;
251 }
252 
253 //***************************************************************************
255 {
256  m_progress_enabled = enable;
257 }
258 
259 //***************************************************************************
260 void Kwave::Plugin::updateProgress(qreal progress)
261 {
262  // check: this must be called from the GUI thread only!
263  Q_ASSERT(this->thread() == QThread::currentThread());
264  Q_ASSERT(this->thread() == qApp->thread());
265 
266 // qDebug("Kwave::Plugin::updateProgress(%0.1f)", progress);
267  QMutexLocker lock(&m_progress_lock);
268 
269  // take over the current progress
270  // note: no lock needed, this is called in the GUI thread context!
271  m_current_progress = qreal(PROGRESS_MAXIMUM / 100.0) * progress;
272 
273  // start the timer for updating the progress bar if it is not active
274  if (!m_progress_timer.isActive() && m_progress) {
275  m_progress_timer.setSingleShot(true);
277  }
278 }
279 
280 //***************************************************************************
282 {
283  // check: this must be called from the GUI thread only!
284  Q_ASSERT(this->thread() == QThread::currentThread());
285  Q_ASSERT(this->thread() == qApp->thread());
286 
287  QMutexLocker lock(&m_progress_lock);
288  if (m_progress) m_progress->setValue(
289  Kwave::toInt(qBound<double>(0.0, m_current_progress, PROGRESS_MAXIMUM)));
290 }
291 
292 //***************************************************************************
294 {
295  // check: this must be called from the GUI thread only!
296  Q_ASSERT(this->thread() == QThread::currentThread());
297  Q_ASSERT(this->thread() == qApp->thread());
298 
299  QMutexLocker lock(&m_progress_lock);
300 
301  // stop the progress timer
302  m_progress_timer.stop();
303  m_progress_timer.disconnect();
304 
305  if (m_confirm_cancel) m_confirm_cancel->disconnect();
306  if (m_progress) m_progress->disconnect();
307 
308  // NOTE: as the progress dialog is *modal*, it is higly probable
309  // that this function is called from the context of the event
310  // loop that is provided by the progress dialog
311  // => deleting this object should be done somewhere later...
312  if (m_confirm_cancel) {
313  m_confirm_cancel->deleteLater();
314  m_confirm_cancel = Q_NULLPTR;
315  }
316 
317  if (m_progress) {
318  m_progress->done(0);
319  m_progress->deleteLater();
320  m_progress = Q_NULLPTR;
321  }
322 }
323 
324 //***************************************************************************
326 {
327  m_stop = true;
328 }
329 
330 //***************************************************************************
331 int Kwave::Plugin::execute(QStringList &params)
332 {
333  QMutexLocker lock(&m_thread_lock);
334  m_stop = false;
335 
336  m_thread = new(std::nothrow) Kwave::WorkerThread(this, QVariant(params));
337  Q_ASSERT(m_thread);
338  if (!m_thread) return -ENOMEM;
339 
340  // increment the use count, it is decremented at the end of
341  // the run_wrapper when the thread is finished
342  use();
343 
344  // start the thread, this executes run()
345  m_thread->start();
346 
347  return 0;
348 }
349 
350 //***************************************************************************
352 {
353  return !isRunning();
354 }
355 
356 //***************************************************************************
358 {
359  return (m_thread) && m_thread->isRunning();
360 }
361 
362 //***************************************************************************
363 void Kwave::Plugin::run(QStringList params)
364 {
365  Q_UNUSED(params);
366 }
367 
368 //***************************************************************************
369 void Kwave::Plugin::run_wrapper(const QVariant &params)
370 {
371  // signal that we are running
372  emit sigRunning(this);
373 
374  // start time measurement
375  QTime t;
376  t.start();
377 
378  // call the plugin's run function in this worker thread context
379  run(params.toStringList());
380 
381  // evaluate the elapsed time
382  double seconds = static_cast<double>(t.elapsed()) * 1E-3;
383  qDebug("plugin %s done, running for %0.03g seconds",
384  DBG(name()), seconds);
385 
386  // emit the "done" signal
387  emit sigDone(this);
388 
389  m_stop = false;
390  release();
391 }
392 
393 //***************************************************************************
395 {
396  // only call stop() if we are NOT in the worker thread / run function !
397  if (m_thread && m_thread->isRunning() &&
398  (QThread::currentThread() != m_thread) )
399  {
400  // check: this must be called from the GUI thread only!
401  Q_ASSERT(this->thread() == QThread::currentThread());
402  Q_ASSERT(this->thread() == qApp->thread());
403 
404  closeProgressDialog(this);
405  stop();
406  } else if ((QThread::currentThread() == m_thread)) {
407  qWarning("Kwave::Plugin::close -> called from worker thread?");
408  }
409 }
410 
411 //***************************************************************************
413 {
414  QMutexLocker lock(&m_usage_lock);
415  Q_ASSERT(m_usage_count); // should be at least 1 (from constructor)
416  m_usage_count++;
417 }
418 
419 //***************************************************************************
421 {
422  bool finished = false;
423 
424  {
425  QMutexLocker lock(&m_usage_lock);
426  Q_ASSERT(m_usage_count);
427  if (m_usage_count) {
428  m_usage_count--;
429  if (!m_usage_count) finished = true;
430  }
431  }
432 
433  if (finished) emit sigClosed(this);
434 }
435 
436 //***************************************************************************
438 {
439  Q_ASSERT(m_plugin_manager);
440  return *m_plugin_manager;
441 }
442 
443 //***************************************************************************
445 {
446  return manager().signalManager();
447 }
448 
449 //***************************************************************************
451 {
452  return manager().parentWidget();
453 }
454 
455 //***************************************************************************
457 {
458  return signalManager().signalName();
459 }
460 
461 //***************************************************************************
463 {
464  return manager().signalLength();
465 }
466 
467 //***************************************************************************
469 {
470  return manager().signalRate();
471 }
472 
473 //***************************************************************************
474 const QList<unsigned int> Kwave::Plugin::selectedTracks()
475 {
476  return signalManager().selectedTracks();
477 }
478 
479 //***************************************************************************
480 sample_index_t Kwave::Plugin::selection(QList<unsigned int> *tracks,
481  sample_index_t *left,
482  sample_index_t *right,
483  bool expand_if_empty)
484 {
487 
488  // expand to the whole signal if left==right and expand_if_empty is set
489  if ((l == r) && (expand_if_empty)) {
490  l = 0;
491  r = manager().signalLength();
492  if (r) r--;
493  }
494 
495  // get the list of selected tracks
496  if (tracks) *tracks = signalManager().selectedTracks();
497 
498  if (left) *left = l;
499  if (right) *right = r;
500  return (r != l) ? (r - l + 1) : 0;
501 }
502 
503 //***************************************************************************
505 {
506  manager().selectRange(offset, length);
507 }
508 
509 //***************************************************************************
510 void Kwave::Plugin::emitCommand(const QString &command)
511 {
512  manager().enqueueCommand(command);
513 }
514 
515 //***************************************************************************
517 {
519 }
520 
521 //***************************************************************************
523 {
524  Q_ASSERT(new_plugin_manager);
525  if (!new_plugin_manager) return;
526  m_plugin_manager = new_plugin_manager;
527 
528  if (m_progress)
530  if (m_confirm_cancel)
532 }
533 
534 //***************************************************************************
535 //***************************************************************************
void emitCommand(const QString &command)
Definition: Plugin.cpp:510
QAtomicInt m_stop
Definition: Plugin.h:367
QPointer< QWidget > parentWidget()
void enqueueCommand(const QString &command)
Definition: App.h:33
unsigned int m_usage_count
Definition: Plugin.h:379
Kwave::WorkerThread * m_thread
Definition: Plugin.h:358
QWidget * parentWidget() const
Definition: Plugin.cpp:450
QProgressDialog * m_progress
Definition: Plugin.h:370
Kwave::SignalManager & signalManager()
Definition: Plugin.cpp:444
void setPluginManager(Kwave::PluginManager *new_plugin_manager)
Definition: Plugin.cpp:522
virtual QString name() const
Definition: Plugin.cpp:196
#define PROGRESS_MAXIMUM
Definition: Plugin.cpp:52
QTimer m_progress_timer
Definition: Plugin.h:385
virtual void close()
Definition: Plugin.cpp:394
void release()
Definition: Plugin.cpp:420
virtual void migrateToActiveContext()
Definition: Plugin.cpp:516
QString m_name
Definition: Plugin.h:350
quint64 sample_index_t
Definition: Sample.h:28
QMutex m_usage_lock
Definition: Plugin.h:382
Kwave::PluginManager & manager() const
Definition: Plugin.cpp:437
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
Kwave::SignalManager & signalManager()
virtual int stop()
Definition: Plugin.cpp:214
void sigRunning(Kwave::Plugin *plugin)
void migratePluginToActiveContext(Kwave::Plugin *plugin)
void sigDone(Kwave::Plugin *plugin)
sample_index_t signalLength()
QMutex m_thread_lock
Definition: Plugin.h:361
virtual bool canClose() const
Definition: Plugin.cpp:351
void closeProgressDialog(Kwave::Plugin *)
Definition: Plugin.cpp:293
int toInt(T x)
Definition: Utils.h:127
void sigClosed(Kwave::Plugin *p)
virtual QString progressText()
Definition: Plugin.cpp:208
virtual void cancel()
Definition: Plugin.cpp:325
virtual void unload()
Definition: Plugin.cpp:129
virtual ~Plugin() Q_DECL_OVERRIDE
Definition: Plugin.cpp:96
virtual sample_index_t signalLength()
Definition: Plugin.cpp:462
QString signalName()
Definition: Plugin.cpp:456
QString m_description
Definition: Plugin.h:353
bool m_progress_enabled
Definition: Plugin.h:364
virtual void setProgressDialogEnabled(bool enable)
Definition: Plugin.cpp:254
void updateProgressTick()
Definition: Plugin.cpp:281
Kwave::PluginManager * m_plugin_manager
Definition: Plugin.h:347
sample_index_t selectionStart()
virtual double signalRate()
Definition: Plugin.cpp:468
virtual void selectRange(sample_index_t offset, sample_index_t length)
Definition: Plugin.cpp:504
QMutex m_progress_lock
Definition: Plugin.h:391
int execute(QStringList &params)
Definition: Plugin.cpp:331
#define PROGRESS_UPDATES_PER_SECOND
Definition: Plugin.cpp:49
virtual void load(QStringList &params)
Definition: Plugin.cpp:124
#define DBG(qs)
Definition: String.h:55
virtual int stop(unsigned int timeout=10000)
virtual void run(QStringList params)
Definition: Plugin.cpp:363
virtual void updateProgress(qreal progress)
Definition: Plugin.cpp:260
virtual const QList< unsigned int > selectedTracks()
Definition: Plugin.cpp:474
const QList< unsigned int > selectedTracks()
virtual void start()
virtual int start(QStringList &params)
Definition: Plugin.cpp:142
void use()
Definition: Plugin.cpp:412
bool isRunning() const
Definition: Plugin.cpp:357
virtual QString description() const
Definition: Plugin.cpp:202
sample_index_t selectionEnd()
void setProgressText(const QString &text)
Kwave::ConfirmCancelProxy * m_confirm_cancel
Definition: Plugin.h:376
Plugin(QObject *parent, const QVariantList &args)
Definition: Plugin.cpp:71
virtual void run_wrapper(const QVariant &params) Q_DECL_OVERRIDE
Definition: Plugin.cpp:369
virtual QStringList * setup(QStringList &previous_params)
Definition: Plugin.cpp:134
virtual sample_index_t selection(QList< unsigned int > *tracks=Q_NULLPTR, sample_index_t *left=Q_NULLPTR, sample_index_t *right=Q_NULLPTR, bool expand_if_empty=false)
Definition: Plugin.cpp:480
qreal m_current_progress
Definition: Plugin.h:388
void selectRange(sample_index_t offset, sample_index_t length)