summaryrefslogtreecommitdiffstats
path: root/src/multimedia
diff options
context:
space:
mode:
authorLing Hu <ling.hu@nokia.com>2011-11-18 16:59:36 +1000
committerQt by Nokia <qt-info@nokia.com>2011-11-21 10:01:11 +0100
commit0e021ea4d1db86708ec6ae5eafb661f1e8b7d631 (patch)
treea2b9c7c2a73ac20ad9429397c85fa15958d012a8 /src/multimedia
parentcc61a0a8c72d9e6c92c750851e9ff417aefda410 (diff)
Fix SoundEffect(pulseaudio) crash in qfeedbackmmk auto test
Task-Number: QTBUG-22779 Some pulseaudio callback may happen after SoundEffect was deleted, thus the userdata(SoundEffect point) we passed previously may result in potential crash with QMetaObject::invokeMethod to queue some event. To solve this problem, the release mehtod is added to SoundEffectPrivate, and instead of calling d->deleteLater in SoundEffect::dtor, d->release is called. So SoundEffectPrivate will no when it is going to be deleted soon rather than handle everything in SoundEffectPrivate::dtor which may be too late. class RefObject is also added to be able to track the SoundEffectPrivate status by pulseaduio callbacks. I thought this could be avoided by checking the connection state of pulse stream. However, that doesn't work as expected, stream state remains Ready when checked in callbacks even after disconnect stream has been called. So RefObject is used instead and its lifecycle is managed by an internal reference count. When release is invoked, m_ref->onDeleted is called first, this will mark SoundEffectPrivate as dead. and then unloadPulseStream is called. After those two invocations, we can be asured that: 1. if some pulse callbacks has been called without knowing SoundEffectPrivate dead, the queued invocation on SoundEffectPrivate would be safe, since SoundEffectPrivate::deleteLater would be called after them. 2. Since on pulse callbacks would be executed when unloadPulseStream is called, then at this moment if some pulse callbacks is called again, it would certainly knows that SoundEffectPrivate is marked as dead and would not queue and event on SoundEffectPrivate. Now, the deleteLater can be safely called. Change-Id: I807f29cddb677d1f4bc078fd306ed0d83d6f7dc4 Reviewed-by: Ling Hu <ling.hu@nokia.com>
Diffstat (limited to 'src/multimedia')
-rw-r--r--src/multimedia/effects/qsoundeffect.cpp2
-rw-r--r--src/multimedia/effects/qsoundeffect_pulse_p.cpp167
-rw-r--r--src/multimedia/effects/qsoundeffect_pulse_p.h4
-rw-r--r--src/multimedia/effects/qsoundeffect_qmedia_p.cpp5
-rw-r--r--src/multimedia/effects/qsoundeffect_qmedia_p.h2
5 files changed, 158 insertions, 22 deletions
diff --git a/src/multimedia/effects/qsoundeffect.cpp b/src/multimedia/effects/qsoundeffect.cpp
index 7cfcdce70..19021fd0c 100644
--- a/src/multimedia/effects/qsoundeffect.cpp
+++ b/src/multimedia/effects/qsoundeffect.cpp
@@ -173,7 +173,7 @@ QSoundEffect::QSoundEffect(QObject *parent) :
QSoundEffect::~QSoundEffect()
{
- d->deleteLater();
+ d->release();
}
QStringList QSoundEffect::supportedMimeTypes()
diff --git a/src/multimedia/effects/qsoundeffect_pulse_p.cpp b/src/multimedia/effects/qsoundeffect_pulse_p.cpp
index da617287a..aa88f4c4b 100644
--- a/src/multimedia/effects/qsoundeffect_pulse_p.cpp
+++ b/src/multimedia/effects/qsoundeffect_pulse_p.cpp
@@ -276,6 +276,67 @@ public:
};
}
+class QSoundEffectRef
+{
+public:
+ QSoundEffectRef(QSoundEffectPrivate *target)
+ : m_ref(1)
+ , m_target(target)
+ {
+#ifdef QT_PA_DEBUG
+ qDebug() << "QSoundEffectRef(" << this << ") ctor";
+#endif
+ }
+
+ QSoundEffectRef *getRef()
+ {
+#ifdef QT_PA_DEBUG
+ qDebug() << "QSoundEffectRef(" << this << ") getRef";
+#endif
+ QMutexLocker locker(&m_mutex);
+ m_ref++;
+ return this;
+ }
+
+ void release()
+ {
+#ifdef QT_PA_DEBUG
+ qDebug() << "QSoundEffectRef(" << this << ") Release";
+#endif
+ m_mutex.lock();
+ --m_ref;
+ if (m_ref == 0) {
+ m_mutex.unlock();
+#ifdef QT_PA_DEBUG
+ qDebug() << "QSoundEffectRef(" << this << ") deleted";
+#endif
+ delete this;
+ return;
+ }
+ m_mutex.unlock();
+ }
+
+ QSoundEffectPrivate* soundEffect() const
+ {
+ QMutexLocker locker(&m_mutex);
+ return m_target;
+ }
+
+ void notifyDeleted()
+ {
+#ifdef QT_PA_DEBUG
+ qDebug() << "QSoundEffectRef(" << this << ") notifyDeleted";
+#endif
+ QMutexLocker locker(&m_mutex);
+ m_target = NULL;
+ }
+
+private:
+ int m_ref;
+ mutable QMutex m_mutex;
+ QSoundEffectPrivate *m_target;
+};
+
QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent):
QObject(parent),
m_pulseStream(0),
@@ -293,15 +354,28 @@ QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent):
m_sample(0) ,
m_position(0)
{
+ m_ref = new QSoundEffectRef(this);
pa_sample_spec_init(&m_pulseSpec);
}
-QSoundEffectPrivate::~QSoundEffectPrivate()
+void QSoundEffectPrivate::release()
{
+#ifdef QT_PA_DEBUG
+ qDebug() << this << "release";
+#endif
+ m_ref->notifyDeleted();
unloadPulseStream();
-
- if (m_sample)
+ if (m_sample) {
m_sample->release();
+ m_sample = 0;
+ }
+
+ this->deleteLater();
+}
+
+QSoundEffectPrivate::~QSoundEffectPrivate()
+{
+ m_ref->release();
}
QStringList QSoundEffectPrivate::supportedMimeTypes()
@@ -396,7 +470,7 @@ void QSoundEffectPrivate::updateVolume()
PulseDaemonLocker locker;
pa_cvolume volume;
volume.channels = m_pulseSpec.channels;
- pa_operation_unref(pa_context_set_sink_input_volume(daemon()->context(), m_sinkInputId, daemon()->calcVolume(&volume, m_volume), setvolume_callback, this));
+ pa_operation_unref(pa_context_set_sink_input_volume(daemon()->context(), m_sinkInputId, daemon()->calcVolume(&volume, m_volume), setvolume_callback, m_ref->getRef()));
Q_ASSERT(pa_cvolume_valid(&volume));
#ifdef QT_PA_DEBUG
qDebug() << this << "updateVolume =" << pa_cvolume_max(&volume);
@@ -420,9 +494,9 @@ void QSoundEffectPrivate::updateMuted()
if (m_sinkInputId < 0)
return;
PulseDaemonLocker locker;
- pa_operation_unref(pa_context_set_sink_input_mute(daemon()->context(), m_sinkInputId, m_muted, setmuted_callback, this));
+ pa_operation_unref(pa_context_set_sink_input_mute(daemon()->context(), m_sinkInputId, m_muted, setmuted_callback, m_ref->getRef()));
#ifdef QT_PA_DEBUG
- qDebug() << this << "updateMuted = " << daemon()->calcMuted(m_muted);
+ qDebug() << this << "updateMuted = " << m_muted;
#endif
}
@@ -502,17 +576,20 @@ void QSoundEffectPrivate::play()
void QSoundEffectPrivate::emptyStream()
{
+#ifdef QT_PA_DEBUG
+ qDebug() << this << "emptyStream";
+#endif
m_emptying = true;
- pa_stream_set_write_callback(m_pulseStream, 0, this);
- pa_stream_set_underflow_callback(m_pulseStream, 0, this);
- pa_operation_unref(pa_stream_flush(m_pulseStream, stream_flush_callback, this));
+ pa_stream_set_write_callback(m_pulseStream, 0, 0);
+ pa_stream_set_underflow_callback(m_pulseStream, 0, 0);
+ pa_operation_unref(pa_stream_flush(m_pulseStream, stream_flush_callback, m_ref->getRef()));
}
void QSoundEffectPrivate::emptyComplete()
{
PulseDaemonLocker locker;
m_emptying = false;
- pa_operation_unref(pa_stream_cork(m_pulseStream, 1, stream_cork_callback, this));
+ pa_operation_unref(pa_stream_cork(m_pulseStream, 1, stream_cork_callback, m_ref->getRef()));
}
void QSoundEffectPrivate::sampleReady()
@@ -546,7 +623,7 @@ void QSoundEffectPrivate::sampleReady()
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = m_sample->data().size();
- pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, this);
+ pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef()));
} else {
streamReady();
}
@@ -559,12 +636,12 @@ void QSoundEffectPrivate::sampleReady()
newBufferAttr.minreq = bufferAttr->tlength / 2;
newBufferAttr.prebuf = -1;
newBufferAttr.fragsize = -1;
- pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_reset_buffer_callback, this);
+ pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_reset_buffer_callback, m_ref->getRef()));
} else if (bufferAttr->prebuf > uint32_t(m_sample->data().size())) {
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = m_sample->data().size();
- pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, this);
+ pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef()));
} else {
streamReady();
}
@@ -801,7 +878,7 @@ void QSoundEffectPrivate::contextReady()
void QSoundEffectPrivate::stream_write_callback(pa_stream *s, size_t length, void *userdata)
{
Q_UNUSED(length);
- Q_UNUSED(s)
+ Q_UNUSED(s);
QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
#ifdef QT_PA_DEBUG
@@ -825,7 +902,7 @@ void QSoundEffectPrivate::stream_state_callback(pa_stream *s, void *userdata)
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = self->m_sample->data().size();
- pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, userdata);
+ pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, self->m_ref->getRef());
} else {
QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection);
}
@@ -851,10 +928,18 @@ void QSoundEffectPrivate::stream_state_callback(pa_stream *s, void *userdata)
void QSoundEffectPrivate::stream_reset_buffer_callback(pa_stream *s, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "stream_reset_buffer_callback";
+#endif
Q_UNUSED(s);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
+
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to reset buffer attribute");
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_reset_buffer_callback";
#endif
@@ -872,10 +957,18 @@ void QSoundEffectPrivate::stream_reset_buffer_callback(pa_stream *s, int success
void QSoundEffectPrivate::stream_adjust_prebuffer_callback(pa_stream *s, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "stream_adjust_prebuffer_callback";
+#endif
Q_UNUSED(s);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
+
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to adjust pre-buffer attribute");
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_adjust_prebuffer_callback";
#endif
@@ -884,10 +977,18 @@ void QSoundEffectPrivate::stream_adjust_prebuffer_callback(pa_stream *s, int suc
void QSoundEffectPrivate::setvolume_callback(pa_context *c, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "setvolume_callback";
+#endif
Q_UNUSED(c);
Q_UNUSED(userdata);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
#ifdef QT_PA_DEBUG
- qDebug() << reinterpret_cast<QSoundEffectPrivate*>(userdata) << "setvolume_callback";
+ qDebug() << self << "setvolume_callback";
#endif
if (!success) {
qWarning("QSoundEffect(pulseaudio): faild to set volume");
@@ -896,10 +997,18 @@ void QSoundEffectPrivate::setvolume_callback(pa_context *c, int success, void *u
void QSoundEffectPrivate::setmuted_callback(pa_context *c, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "setmuted_callback";
+#endif
Q_UNUSED(c);
Q_UNUSED(userdata);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
#ifdef QT_PA_DEBUG
- qDebug() << reinterpret_cast<QSoundEffectPrivate*>(userdata) << "setmuted_callback";
+ qDebug() << self << "setmuted_callback";
#endif
if (!success) {
qWarning("QSoundEffect(pulseaudio): faild to set muted");
@@ -923,10 +1032,18 @@ void QSoundEffectPrivate::stream_underrun_callback(pa_stream *s, void *userdata)
void QSoundEffectPrivate::stream_cork_callback(pa_stream *s, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "stream_cork_callback";
+#endif
Q_UNUSED(s);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
+
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to stop");
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_cork_callback";
#endif
@@ -935,10 +1052,18 @@ void QSoundEffectPrivate::stream_cork_callback(pa_stream *s, int success, void *
void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void *userdata)
{
+#ifdef QT_PA_DEBUG
+ qDebug() << "stream_flush_callback";
+#endif
Q_UNUSED(s);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
+
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to drain");
- QSoundEffectPrivate *self = reinterpret_cast<QSoundEffectPrivate*>(userdata);
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_flush_callback";
#endif
diff --git a/src/multimedia/effects/qsoundeffect_pulse_p.h b/src/multimedia/effects/qsoundeffect_pulse_p.h
index eaf78e8dd..3992617ba 100644
--- a/src/multimedia/effects/qsoundeffect_pulse_p.h
+++ b/src/multimedia/effects/qsoundeffect_pulse_p.h
@@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Multimedia)
+class QSoundEffectRef;
class QSoundEffectPrivate : public QObject
{
@@ -90,6 +91,8 @@ public:
bool isPlaying() const;
QSoundEffect::Status status() const;
+ void release();
+
public Q_SLOTS:
void play();
void stop();
@@ -154,6 +157,7 @@ private:
QSample *m_sample;
int m_position;
+ QSoundEffectRef *m_ref;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/effects/qsoundeffect_qmedia_p.cpp b/src/multimedia/effects/qsoundeffect_qmedia_p.cpp
index dfd56b09c..64a1754a9 100644
--- a/src/multimedia/effects/qsoundeffect_qmedia_p.cpp
+++ b/src/multimedia/effects/qsoundeffect_qmedia_p.cpp
@@ -76,6 +76,11 @@ QSoundEffectPrivate::QSoundEffectPrivate(QObject* parent):
connect(m_player, SIGNAL(volumeChanged(int)), SIGNAL(volumeChanged()));
}
+void QSoundEffectPrivate::release()
+{
+ this->deleteLater();
+}
+
QSoundEffectPrivate::~QSoundEffectPrivate()
{
}
diff --git a/src/multimedia/effects/qsoundeffect_qmedia_p.h b/src/multimedia/effects/qsoundeffect_qmedia_p.h
index 3e448f718..b93f47ce3 100644
--- a/src/multimedia/effects/qsoundeffect_qmedia_p.h
+++ b/src/multimedia/effects/qsoundeffect_qmedia_p.h
@@ -88,6 +88,8 @@ public:
bool isPlaying() const;
QSoundEffect::Status status() const;
+ void release();
+
public Q_SLOTS:
void play();
void stop();