Commit aa9fb196 authored by Thomas Eschenbacher's avatar Thomas Eschenbacher
Browse files

bugfix: improved stability and responsiveness when stopping/canceling of...

bugfix: improved stability and responsiveness when stopping/canceling of streams (including playback), fixes some GUI hanging and crashes
parent ab074322
20.07.70 [2020-02-16]
* bugfix: cleanup code in destructor of PluginManager caused crash when
closing the window due to broken iterator
* bugfix: Qt playback plugin crashed when stopping pre-listen
* bugfix: new data was not initialized in SampleArray::resize()
* Qt playback: improved responsiveness
* bugfix: improved stability and responsiveness when stopping/canceling
of streams (including playback), fixes some GUI hanging and crashes
20.03.70 [2020-02-16]
......
......@@ -113,6 +113,8 @@ void Kwave::FilterPlugin::run(QStringList params)
Kwave::MultiTrackReader source(
(m_listen) ? Kwave::FullSnapshot : Kwave::SinglePassForward,
signalManager(), tracks, first, last);
connect(this, SIGNAL(sigCancel()), &source, SLOT(cancel()),
Qt::DirectConnection);
if (m_listen) {
// pre-listen mode
......@@ -162,13 +164,12 @@ void Kwave::FilterPlugin::run(QStringList params)
if (m_listen) QThread::yieldCurrentThread();
source.goOn();
filter->goOn();
if (m_listen) QThread::yieldCurrentThread();
// watch out for changed parameters when in
// pre-listen mode
if (m_listen && paramsChanged()) {
updateFilter(filter);
}
}
if (m_listen && source.done()) {
// start the next loop
......
......@@ -41,6 +41,9 @@ namespace Kwave {
QObject::connect(s, output, d, input, Qt::DirectConnection);
QObject::connect(s, SIGNAL(sigCancel()), d, SLOT(cancel()),
Qt::DirectConnection);
return true;
}
......
......@@ -75,6 +75,12 @@ void Kwave::MultiPlaybackSink::input(unsigned int track,
{
QMutexLocker lock(&m_lock);
// shortcut for more responsiveness when pressing cancel
if (isCanceled()) {
m_in_buffer_filled.fill(false);
return;
}
Q_ASSERT(m_device);
Q_ASSERT(m_tracks);
if (!m_device || !m_tracks) return;
......@@ -101,16 +107,14 @@ void Kwave::MultiPlaybackSink::input(unsigned int track,
// play the output buffer
unsigned int retry = 5;
while (retry--) {
// shortcut for more responsiveness when pressing cancel
if (QThread::currentThread()->isInterruptionRequested())
break;
while (retry-- && !isCanceled()) {
int res = m_device->write(m_out_buffer);
if (res != 0) {
QThread::yieldCurrentThread();
} else break;
}
if (retry == 0)
break; // give up
}
m_in_buffer_filled.fill(false);
......
......@@ -25,7 +25,7 @@
//***************************************************************************
Kwave::MultiTrackReader::MultiTrackReader()
:Kwave::MultiTrackSource<Kwave::SampleReader, false>(0, Q_NULLPTR),
m_first(0), m_last(0), m_canceled(false)
m_first(0), m_last(0)
{
}
......@@ -35,8 +35,8 @@ Kwave::MultiTrackReader::MultiTrackReader(Kwave::ReaderMode mode,
const QVector<unsigned int> &track_list,
sample_index_t first,
sample_index_t last)
:Kwave::MultiTrackSource<Kwave::SampleReader, false>(0),
m_first(first), m_last(last), m_canceled(false)
:Kwave::MultiTrackSource<Kwave::SampleReader, false>(0, Q_NULLPTR),
m_first(first), m_last(last)
{
unsigned int index = 0;
......@@ -147,11 +147,5 @@ void Kwave::MultiTrackReader::seek(sample_index_t pos)
}
}
//***************************************************************************
void Kwave::MultiTrackReader::cancel()
{
m_canceled = true;
}
//***************************************************************************
//***************************************************************************
......@@ -86,9 +86,6 @@ namespace Kwave
(Kwave::MultiTrackSource<Kwave::SampleReader, false>::tracks() < 1);
}
/** returns true if the transfer has been canceled */
inline bool isCanceled() const { return m_canceled; }
/** @see QList::insert() */
virtual bool insert(unsigned int track, Kwave::SampleReader *reader)
Q_DECL_OVERRIDE;
......@@ -108,12 +105,6 @@ namespace Kwave
public slots:
/**
* Can be connected to some progress dialog to cancel the current
* transfer.
*/
void cancel();
/** Resets all readers to zero */
void reset();
......@@ -132,12 +123,6 @@ namespace Kwave
/** index of the last sample to read */
sample_index_t m_last;
/**
* Initialized as false, will be true if the transfer has
* been canceled
*/
bool m_canceled;
};
}
......
......@@ -96,6 +96,8 @@ namespace Kwave
*/
virtual bool insert(unsigned int track, SINK *sink) {
QList<SINK *>::insert(track, sink);
QObject::connect(this, SIGNAL(sigCancel()),
sink, SLOT(cancel()), Qt::DirectConnection);
return (at(track) == sink);
}
......@@ -104,6 +106,27 @@ namespace Kwave
while (!QList<SINK *>::isEmpty())
delete QList<SINK *>::takeLast();
}
/**
* overloaded function, in most cases the sink objects are connected
* to some sigCancel() signals of other stream objects, so that the
* cancel() slot of this object is not called.
*/
virtual bool isCanceled() const Q_DECL_OVERRIDE
{
if (Kwave::SampleSink::isCanceled())
return true;
unsigned int tracks = this->tracks();
for (unsigned int track = 0; track < tracks; track++) {
const SINK *sink = at(track);
if (sink && sink->isCanceled())
return true;
}
return false;
}
};
/**
......
......@@ -43,14 +43,14 @@ namespace Kwave
public:
/**
* Default constructor, which does no initialization of the
* objects themselfes. If you want to use this, you should
* objects themselves. If you want to use this, you should
* derive from this class, create all objects manually and
* "insert" them from the constructor.
*
* @param tracks number of tracks
* @param parent a parent object, passed to QObject (optional)
* @param parent a parent object, passed to QObject
*/
MultiTrackSource(unsigned int tracks, QObject *parent = Q_NULLPTR)
MultiTrackSource(unsigned int tracks, QObject *parent)
:Kwave::SampleSource(parent),
QList<SOURCE *>()
{
......@@ -71,8 +71,9 @@ namespace Kwave
*/
virtual void goOn() Q_DECL_OVERRIDE
{
QFutureSynchronizer<void> synchronizer;
if (isCanceled()) return;
QFutureSynchronizer<void> synchronizer;
foreach (SOURCE *src, static_cast< QList<SOURCE *> >(*this)) {
if (!src) continue;
synchronizer.addFuture(QtConcurrent::run(
......@@ -81,7 +82,6 @@ namespace Kwave
src)
);
}
synchronizer.waitForFinished();
}
......@@ -127,6 +127,8 @@ namespace Kwave
*/
inline virtual bool insert(unsigned int track, SOURCE *source) {
QList<SOURCE *>::insert(track, source);
QObject::connect(this, SIGNAL(sigCancel()),
source, SLOT(cancel()), Qt::DirectConnection);
return (at(track) == source);
}
......
......@@ -22,8 +22,7 @@
//***************************************************************************
Kwave::MultiWriter::MultiWriter()
:Kwave::MultiTrackSink<Kwave::Writer, false>(0, Q_NULLPTR),
m_canceled(false)
:Kwave::MultiTrackSink<Kwave::Writer, false>(0, Q_NULLPTR)
{
}
......@@ -75,12 +74,6 @@ void Kwave::MultiWriter::proceeded()
}
}
//***************************************************************************
void Kwave::MultiWriter::cancel()
{
m_canceled = true;
}
//***************************************************************************
sample_index_t Kwave::MultiWriter::last() const
{
......
......@@ -60,9 +60,6 @@ namespace Kwave
virtual bool insert(unsigned int track, Kwave::Writer *writer)
Q_DECL_OVERRIDE;
/** returns true if the transfer has been canceled */
inline bool isCanceled() const { return m_canceled; }
signals:
/**
......@@ -79,14 +76,6 @@ namespace Kwave
*/
void written(quint64 samples);
public slots:
/**
* Can be connected to some progress dialog to cancel the current
* transfer.
*/
void cancel();
private slots:
/**
......@@ -94,14 +83,6 @@ namespace Kwave
*/
void proceeded();
protected:
/**
* Initialized as false, will be true if the transfer has
* been canceled
*/
bool m_canceled;
};
}
......
......@@ -329,8 +329,13 @@ void Kwave::Plugin::closeProgressDialog(Kwave::Plugin *)
//***************************************************************************
void Kwave::Plugin::cancel()
{
m_stop = true;
if (m_thread) m_thread->requestInterruption();
if (!m_stop)
{
QMutexLocker lock(&m_thread_lock);
m_stop = true;
if (m_thread) m_thread->cancel();
}
emit sigCancel();
}
//***************************************************************************
......@@ -342,6 +347,8 @@ int Kwave::Plugin::execute(QStringList &params)
m_thread = new(std::nothrow) Kwave::WorkerThread(this, QVariant(params));
Q_ASSERT(m_thread);
if (!m_thread) return -ENOMEM;
connect(this, SIGNAL(sigCancel()), m_thread, SLOT(cancel()),
Qt::QueuedConnection);
// increment the use count, it is decremented at the end of
// the run_wrapper when the thread is finished
......
......@@ -291,6 +291,12 @@ namespace Kwave
/** can be used by plugins to execute toplevel commands */
void sigCommand(const QString &command);
/**
* emitted when cancel() is called, can be connected
* to the cancel() slot of child objects
*/
void sigCancel();
/**
* Sets the text of the progress dialog
* @param text new progress bar text, already be localized
......
......@@ -89,6 +89,13 @@ void Kwave::WorkerThread::start()
QThread::start();
}
//***************************************************************************
void Kwave::WorkerThread::cancel()
{
requestInterruption();
emit sigCancel();
}
//***************************************************************************
int Kwave::WorkerThread::stop(unsigned int timeout)
{
......
......@@ -60,6 +60,22 @@ namespace Kwave
*/
virtual void run() Q_DECL_OVERRIDE;
public slots:
/**
* Can be connected to a progress dialog to cancel the current
* operation.
*/
virtual void cancel();
signals:
/**
* emitted when cancel() is called, can be connected
* to the cancel() slot of child objects
*/
void sigCancel();
private:
/** pointer to the object that has a run() function */
......
......@@ -27,7 +27,9 @@ bool Kwave::StreamObject::m_interactive = false;
//***************************************************************************
Kwave::StreamObject::StreamObject(QObject *parent)
:QObject(Q_NULLPTR /*parent*/), m_lock_set_attribute(QMutex::Recursive)
:QObject(Q_NULLPTR /*parent*/),
m_lock_set_attribute(QMutex::Recursive),
m_canceled(false)
{
Q_UNUSED(parent)
}
......@@ -73,5 +75,14 @@ void Kwave::StreamObject::setInteractive(bool interactive)
m_interactive = interactive;
}
//***************************************************************************
void Kwave::StreamObject::cancel()
{
if (!m_canceled) {
m_canceled = true;
emit sigCancel();
}
}
//***************************************************************************
//***************************************************************************
......@@ -110,6 +110,17 @@ namespace Kwave
*/
static void setInteractive(bool interactive);
/** returns true if the transfer has been canceled */
virtual bool isCanceled() const { return m_canceled; }
public slots:
/**
* Can be connected to other stream objects to cancel the current
* transfer.
*/
void cancel();
signals:
/**
......@@ -118,6 +129,12 @@ namespace Kwave
*/
void attributeChanged(const QVariant value);
/**
* emitted when cancel() is called, can be connected
* to the cancel() slot of child objects
*/
void sigCancel();
private:
/** Mutex for locking access to setAttribute (recursive) */
......@@ -126,6 +143,11 @@ namespace Kwave
/** interactive mode: if enabled, use smaller block size */
static bool m_interactive;
/**
* Initialized as false, will be true if the transfer has
* been canceled
*/
bool m_canceled;
};
}
......
......@@ -523,9 +523,8 @@ qint64 Kwave::PlayBackQt::Buffer::readData(char *data, qint64 len)
Kwave::toInt(len)
);
if (Q_LIKELY(m_sem_filled.tryAcquire(count, m_timeout))) {
// qDebug(" read: locking...");
QMutexLocker _lock(&m_lock); // context: qt streaming engine
// qDebug(" read: locked, count=%lld", len);
m_sem_free.release(count);
if (read_bytes < 0) read_bytes = 0;
read_bytes += count;
......
......@@ -295,8 +295,6 @@ void Kwave::PlayBackPlugin::run(QStringList params)
const double t_sweep = 1.0; /* seconds per speaker */
const unsigned int periods = 3; /* number of periods to play */
qDebug("PlayBackPlugin::run()");
Q_UNUSED(params)
Q_ASSERT(m_dialog);
......@@ -329,6 +327,8 @@ void Kwave::PlayBackPlugin::run(QStringList params)
curve.insert(1.0, 0.0);
Kwave::CurveStreamAdapter curve_adapter(curve, curve_length);
connect(this, SIGNAL(sigCancel()), &curve_adapter, SLOT(cancel()),
Qt::DirectConnection);
Kwave::MultiTrackSource<Kwave::Delay, true> delay(channels);
for (unsigned int i = 0; i < channels; i++) {
......@@ -341,6 +341,8 @@ void Kwave::PlayBackPlugin::run(QStringList params)
Kwave::Osc osc;
osc.setAttribute(SLOT(setFrequency(QVariant)),
QVariant(rate / PLAYBACK_TEST_FREQUENCY));
connect(this, SIGNAL(sigCancel()), &osc, SLOT(cancel()),
Qt::DirectConnection);
Kwave::MultiTrackSource<Kwave::Mul, true> mul(channels);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment