[meego-commits] 5579: Changes to Trunk:Testing/meegotouch-feedback

Kaitlin Rupert kaitlin.rupert at intel.com
Mon Jul 12 18:27:57 UTC 2010


Hi,
I have made the following changes to meegotouch-feedback in project Trunk:Testing. Please review and accept ASAP.

Thank You,
Kaitlin Rupert

[This message was auto-generated]

---

Request #5579:

  submit:   devel:nokia:trunk/meegotouch-feedback(r4) -> Trunk:Testing/meegotouch-feedback


Message:
    Update to release tag 0.10.1-4.  This update depends on the libmeegotouch update.

State:   new          2010-07-12T06:22:59 krupert
Comment: None



changes files:
--------------
--- meegotouch-feedback.changes
+++ meegotouch-feedback.changes
@@ -0,0 +1,3 @@
+* Fri Jul 09 2010 Kaitlin Rupert <kaitlin.rupert at intel.com> 0.10.1
+- Update to release tag 0.10.1-4
+

spec files:
-----------

other changes:
--------------

++++++ meegotouch-feedback-0.10.1.tar.bz2
--- conf/daemon.conf
+++ conf/daemon.conf
@@ -1,4 +1,6 @@
 [General]
+# Feedback latency limit in microseconds
+feedback-latency-limit=2000
 # Feedback length limit in milliseconds
 feedback-length-limit=1000
 
--- debian/changelog
+++ debian/changelog
@@ -1,3 +1,11 @@
+meegofeedbackd (0.10.1-4) unstable; urgency=low
+
+  * Added dropping of feedbacks with high latency
+  * Added possibility to call backend play() methods in separate threads
+  * Implemented: SWP#FEFRA-387, SWP#FEFRA-374
+
+ -- Antti Pulakka <ext-antti.j.pulakka at nokia.com>  Thu, 10 Jun 2010 15:02:24 +0200
+
 meegofeedbackd (0.10.1-3) unstable; urgency=low
 
   * Fixed functional test
--- debian/meegofeedback-dummy.postinst
+++ debian/meegofeedback-dummy.postinst
+#! /bin/sh
+# postinst script for MeeGo Touch Feedback Framework dummy backend
+
+set -e
+
+case "$1" in
+	configure)
+        mkdir -p /usr/lib/meegofeedbackd/non-threaded-backends
+	if ! [ -e "/usr/lib/meegofeedbackd/non-threaded-backends/02-libmeegofeedback-dummy.so" ]; then
+		ln -s /usr/lib/meegofeedbackd/libmeegofeedback-dummy.so /usr/lib/meegofeedbackd/non-threaded-backends/02-libmeegofeedback-dummy.so
+	fi
+	;;
+	abort-upgrade|abort-remove|abort-deconfigure)
+	;;
+	*)
+	echo "postinst called with unknown argument \`$1'" >&2
+	exit 1
+	;;
+esac
+
+exit 0
--- debian/meegofeedback-dummy.postrm
+++ debian/meegofeedback-dummy.postrm
+#!/bin/sh
+set -e
+
+if [ "$1" = "remove" ] ; then
+	rm -f /usr/lib/meegofeedbackd/non-threaded-backends/02-libmeegofeedback-dummy.so
+fi
+
+#DEBHELPER#
+
+exit 0
+
--- src/daemon/mfconnection.cpp
+++ src/daemon/mfconnection.cpp
@@ -92,7 +92,7 @@
         return;
     }
 
-    session->feedbackHash.value(feedbackName)->play();
+    session->feedbackHash.value(feedbackName)->emitPlay(getTimestamp());
 }
 
 void MfConnection::terminate()
--- src/lib/mfconfig.cpp
+++ src/lib/mfconfig.cpp
@@ -16,11 +16,22 @@
 
 #include "mfconfig.h"
 
+#include <QDebug>
 #include <QSettings>
 
+qint64 MfConfig::_feedbackLatencyLimit = -1;
 int MfConfig::_feedbackLengthLimit = -1;
 bool MfConfig::_loaded = false;
 
+qint64 MfConfig::feedbackLatencyLimit()
+{
+    if (!_loaded) {
+        load();
+    }
+
+    return _feedbackLatencyLimit;
+}
+
 int MfConfig::feedbackLengthLimit()
 {
     if (!_loaded) {
@@ -35,5 +46,8 @@
     QSettings settings("/etc/meegofeedbackd/daemon.conf", QSettings::NativeFormat);
 
     _loaded = true;
+    _feedbackLatencyLimit = (qint64)settings.value("feedback-latency-limit", QVariant(2000)).toInt();
     _feedbackLengthLimit = settings.value("feedback-length-limit", QVariant(1000)).toInt();
+    qDebug() << "Loaded feedback latency limit:" << _feedbackLatencyLimit;
+    qDebug() << "Loaded feedback length limit:" << _feedbackLengthLimit;
 }
--- src/lib/mfconfig.h
+++ src/lib/mfconfig.h
@@ -17,13 +17,17 @@
 #ifndef MFCONFIG_H
 #define MFCONFIG_H
 
+#include <QtGlobal>
+
 class MfConfig {
 public:
+    static qint64 feedbackLatencyLimit();
     static int feedbackLengthLimit();
 
 private:
     static void load();
 
+    static qint64 _feedbackLatencyLimit;
     static int _feedbackLengthLimit;
     static bool _loaded;
 };
--- src/lib/mffeedback.cpp
+++ src/lib/mffeedback.cpp
@@ -18,9 +18,12 @@
 
 #include <QDir>
 #include <QDebug>
+#include <QtConcurrentRun>
 
-#include "mfmanager.h"
+#include "mfconfig.h"
 #include "mffeedbackhandle.h"
+#include "mfmanager.h"
+#include "mfutil.h"
 
 MfManager* MfFeedback::manager = 0;
 
@@ -38,6 +41,14 @@
     for (int i = 0; i < manager->backendList.size(); ++i) {
         feedbackHandles << QPointer<MfFeedbackHandle>();
     }
+
+    // Set correct amount of QFuture placeholders for concurrent playing
+    for (int i = 0; i < (manager->backendList.size() - manager->nonThreadedBackendCount); ++i) {
+        concurrentPlayers << QFuture<void>();
+    }
+
+    connect(this, SIGNAL(startPlay(qint64)), this,
+            SLOT(play(qint64)), Qt::QueuedConnection);
 }
 
 MfFeedback::~MfFeedback()
@@ -139,14 +150,47 @@
     return feedbackName;
 }
 
-void MfFeedback::play()
+void MfFeedback::emitPlay(qint64 timestamp)
 {
-    for (int i = 0; i < feedbackHandles.size(); i++) {
+    emit startPlay(timestamp);
+}
+
+void MfFeedback::play(qint64 timestamp)
+{
+    int playerCount = 0;
+
+    if (timestamp != -1) {
+        long int timeDiff = getTimestamp()-timestamp;
+
+        if (timeDiff > MfConfig::feedbackLatencyLimit()) {
+            qDebug() << "Skip the feedback because of too much latency: " <<
+                    timeDiff << "usec";
+            return;
+        }
+    }
+
+    // Play feedbacks using non-threaded backend interfaces
+    for (int i = 0; i < manager->nonThreadedBackendCount; ++i) {
         if (feedbackHandles[i].isNull() == false &&
             feedbackHandles[i]->state() == MfFeedbackHandle::Ready) {
             feedbackHandles[i]->play();
         }
     }
+
+    // Play the rest of the feedbacks in separate threads
+    for (int i = manager->nonThreadedBackendCount; i < feedbackHandles.size(); ++i) {
+        if (feedbackHandles[i].isNull() == false &&
+            feedbackHandles[i]->state() == MfFeedbackHandle::Ready) {
+            concurrentPlayers[playerCount] = QtConcurrent::run(feedbackHandles[i].data(), &MfFeedbackHandle::play);
+            playerCount++;
+        }
+    }
+
+    // Wait until the playing has stopped to avoid concurrency problems
+    // in main thread.
+    for (int i = 0; i < playerCount; ++i) {
+        concurrentPlayers[i].waitForFinished();
+    }
 }
 
 const QList<QPointer<MfFeedbackHandle> >& MfFeedback::fbHandles()
--- src/lib/mffeedback.h
+++ src/lib/mffeedback.h
@@ -21,6 +21,7 @@
 #include <QList>
 #include <QString>
 #include <QPointer>
+#include <QFuture>
 
 class MfFeedbackHandle;
 class MfManager;
@@ -45,8 +46,13 @@
 
     const QList<QPointer<MfFeedbackHandle> > &fbHandles();
 
-public slots:
-    void play();
+    void emitPlay(qint64 timestamp);
+
+signals:
+    void startPlay(qint64 timestamp);
+
+private slots:
+    void play(qint64 timestamp);
 
 protected:
     void unload();
@@ -54,7 +60,12 @@
 
     static MfManager *manager;
     QString feedbackName;
+
+    // The size of feedbackHandles must always match the size of
+    // loaded backend interfaces.
     QList<QPointer<MfFeedbackHandle> > feedbackHandles;
+
+    QList<QFuture<void> > concurrentPlayers;
 };
 
 #endif
--- src/lib/mfmanager.cpp
+++ src/lib/mfmanager.cpp
@@ -17,6 +17,7 @@
 #include "mfmanager.h"
 
 #include <QDir>
+#include <QThreadPool>
 
 #include "mfbackendbase.h"
 #include "mfconfig.h"
@@ -25,13 +26,18 @@
 #include "mflog.h"
 #include "mfsourceinterface.h"
 
+#define MFMANAGER_MAX_THREADS 2
+
 // TODO: Take directory from header file made at configuration time?
-static const char gLibDir[] = "/usr/lib/meegofeedbackd";
+static const char gLibDirPrefix[] = "/usr/lib/meegofeedbackd";
+static const char gThreadedBackendsDir[] = "/threaded-backends";
+static const char gNonThreadedBackendsDir[] = "/non-threaded-backends";
+static const char gSourcesDir[] = "/sources";
 
 static MfManager *gInstance = NULL;
 
 MfManager::MfManager(QObject *parent)
-    : QObject(parent)
+    : QObject(parent), nonThreadedBackendCount(0)
 {
     if (gInstance) {
         qCritical("MfManager: Creating an instance but another one already exists");
@@ -45,10 +51,22 @@
 {
     deleteCache();
 
-    qDeleteAll(sourceList);
+    // Unload the sources and delete QPluginLoaders. Note that
+    // QPluginLoader will delete the source instances when unloading.
+    for (int i = 0; i < sourceLoaderList.size(); ++i) {
+        sourceLoaderList[i]->unload();
+        delete sourceLoaderList[i];
+        sourceLoaderList[i] = NULL;
+    }
     sourceList.clear();
 
-    qDeleteAll(backendList);
+    // Unload the backends and delete QPluginLoaders. Note that
+    // QPluginLoader will delete the backend instances when unloading.
+    for (int i = 0; i < backendLoaderList.size(); ++i) {
+        backendLoaderList[i]->unload();
+        delete backendLoaderList[i];
+        backendLoaderList[i] = NULL;
+    }
     backendList.clear();
 
     gInstance = NULL;
@@ -62,17 +80,49 @@
 bool MfManager::init(const QString &theme, const QMap<QString, MfBackendInterface::playbackVolume> &volumes)
 {
     bool ok = true;
+    QString pluginPath;
+    QThreadPool *globalThreadPool = QThreadPool::globalInstance();
 
     // Store the current theme name
     currentThemeName = theme;
 
-    loadPlugins(gLibDir);
+    // Load non-threaded backend plugins
+    pluginPath = gLibDirPrefix;
+    pluginPath.append(gNonThreadedBackendsDir);
+    loadBackendPlugins(pluginPath);
+
+    // Store the number of non-threaded backends for later use
+    nonThreadedBackendCount = backendList.size();
+
+    // Load threaded backend plugins
+    pluginPath = gLibDirPrefix;
+    pluginPath.append(gThreadedBackendsDir);
+    loadBackendPlugins(pluginPath);
 
     if (backendList.size() == 0) {
         qCritical("MfManager: No backend library found!");
         return false;
     }
 
+    // Load source plugins
+    pluginPath = gLibDirPrefix;
+    pluginPath.append(gSourcesDir);
+    loadSourcePlugins(pluginPath);
+
+    if (sourceList.size() == 0) {
+        qCritical("MfManager: No source library found!");
+    }
+
+    // Adjust amount of QThreads in QThreadPool to match the amount of
+    // threaded backend but no more than MFMANAGER_MAX_THREADS.
+    if (globalThreadPool) {
+        int targetThreadCount = qBound(0, backendList.size() - nonThreadedBackendCount, MFMANAGER_MAX_THREADS);
+
+        if (globalThreadPool->maxThreadCount() < targetThreadCount) {
+            globalThreadPool->setMaxThreadCount(targetThreadCount);
+        }
+    }
+
     // Set initial volumes for the backends
     for (int i = 0; i < backendList.size(); ++i) {
         if (volumes.contains(backendList[i]->name()) == true) {
@@ -82,6 +132,7 @@
             backendList[i]->setVolume(MfBackendInterface::VolumeMedium);
         }
     }
+
     // Listen to the lost/established connections
     for (int i = 0; i < backendList.size(); ++i) {
         connect(backendList[i], SIGNAL(lostConnection()), this, SLOT(backendDisconnected()),
@@ -94,21 +145,16 @@
                 Qt::QueuedConnection);
     }
 
-    if (sourceList.size() == 0) {
-        qCritical("MfManager: No source library found!");
-    }
-
     return ok;
 }
 
-void MfManager::loadPlugins(const QString &libDir)
+void MfManager::loadSourcePlugins(const QString &libDir)
 {
     QDir dir;
     QStringList fileList;
     QString fileName;
     QPluginLoader *pluginLoader;
     QObject *rootComponent = 0;
-    MfBackendBase *backend = 0;
     MfSourceInterface *source = 0;
 
     dir.setPath(libDir);
@@ -123,73 +169,167 @@
     for (int i = 0; i < fileList.size(); i++) {
         fileName = fileList[i];
 
-        // Search source or backend libraries with a pattern
+        // Search source libraries with a pattern
         if (fileName.startsWith(QLatin1String("libmeegofeedback-")) && fileName.endsWith(QLatin1String(".so"))) {
-            backend = 0;
             source = 0;
             QString filePath = dir.filePath(fileName);
 
-            qDebug() << "MfManager: found source or backend library file" << fileName;
+            qDebug() << "MfManager: found source library file" << fileName;
 
             pluginLoader = new QPluginLoader(filePath, this);
 
             rootComponent = pluginLoader->instance();
             if (!rootComponent) {
-                qWarning() << "MfManager: Unable to instantiate backend" << filePath;
+                qWarning() << "MfManager: Unable to instantiate source plugin" << filePath;
                 if (!pluginLoader->errorString().isEmpty()) {
                     qWarning() << "Detailed error message:" << pluginLoader->errorString();
                 }
                 goto FAILED;
             }
 
-            backend = qobject_cast<MfBackendBase*>(rootComponent);
             source = qobject_cast<MfSourceInterface*>(rootComponent);
-            if (!source && !backend) {
+
+            if (!source) {
                 qWarning() << "MfManager:"
-                           << filePath << "does not implement MfSourceInterface nor MfBackendBase.";
+                           << filePath << "does not implement MfSourceInterface.";
                 goto FAILED;
             }
 
-            if (backend) {
-                backend->reconnect();
+            // Make sure the same source doesn't get loaded more than
+            // once. It is possible to load the same plugin more than
+            // once if symlinks are used. In this case QPluginLoader
+            // will return the same instance that was loaded before. We
+            // don't want that here.
+            for (int i2 = 0; i2 < sourceList.size(); ++i2) {
+                if (source == sourceList[i2]) {
+                    qWarning() << "MfManager: Source library"
+                               << filePath << "already loaded.";
+                    goto FAILED;
+                }
             }
+
             if (source && !source->init()) {
                 qWarning() << "MfManager:" << filePath << "failed to initialize.";
                 goto FAILED;
             }
 
-            mfLog() << "Loaded backend" << filePath;
+            mfLog() << "Loaded source plugin" << filePath;
 
-            // Add the backend to the backend list
-            if (backend) {
-                backendList << backend;
-            }
             // Add the source to the source list
             if (source) {
                 sourceList << source;
             }
-            // Delete the plugin loader instance
+
+            // Add the plugin loader instance to source loader list
             if (pluginLoader) {
-                delete pluginLoader;
+                sourceLoaderList << pluginLoader;
             }
 
-            // If we reached that point, everything went just fine.
+            // If we reached this point, everything went just fine.
             continue;
+
         FAILED:
+            // Calling unload() will delete the instanciated source
+            // if the source hasn't been loaded before.
+            if (pluginLoader) {
+                if (pluginLoader->isLoaded()) {
+                    pluginLoader->unload();
+                }
+                delete pluginLoader;
+                pluginLoader = NULL;
+            }
+        }
+    }
+}
+
+void MfManager::loadBackendPlugins(const QString &libDir)
+{
+    QDir dir;
+    QStringList fileList;
+    QString fileName;
+    QPluginLoader *pluginLoader;
+    QObject *rootComponent = 0;
+    MfBackendBase *backend = 0;
+
+    dir.setPath(libDir);
+
+    if (!dir.exists()) {
+        qCritical() << "MfManager: Lib directory" << libDir << "doesn't exist.";
+        return;
+    }
+
+    fileList = dir.entryList(QDir::Files, QDir::Name);
+
+    for (int i1 = 0; i1 < fileList.size(); i1++) {
+        fileName = fileList[i1];
+
+        // Search source or backend libraries with a pattern
+        if (fileName.contains(QLatin1String("libmeegofeedback-")) && fileName.endsWith(QLatin1String(".so"))) {
+            backend = NULL;
+            QString filePath = dir.filePath(fileName);
+
+            qDebug() << "MfManager: found backend library file" << fileName;
+
+            pluginLoader = new QPluginLoader(filePath, this);
+
+            rootComponent = pluginLoader->instance();
+            if (!rootComponent) {
+                qWarning() << "MfManager: Unable to instantiate backend plugin" << filePath;
+                if (!pluginLoader->errorString().isEmpty()) {
+                    qWarning() << "Detailed error message:" << pluginLoader->errorString();
+                }
+                goto FAILED;
+            }
+
+            backend = qobject_cast<MfBackendBase*>(rootComponent);
+
+            if (!backend) {
+                qWarning() << "MfManager:"
+                           << filePath << "does not implement MfBackendBase.";
+                goto FAILED;
+            }
+
+            // Make sure the same backend doesn't get loaded more than
+            // once. It is possible to load the same plugin more than
+            // once if symlinks are used. In this case QPluginLoader
+            // will return the same instance that was loaded before. We
+            // don't want that here.
+            for (int i2 = 0; i2 < backendList.size(); ++i2) {
+                if (backend == backendList[i2]) {
+                    qWarning() << "MfManager: Backend library"
+                               << filePath << "already loaded.";
+                    goto FAILED;
+                }
+            }
+
             if (backend) {
-                delete backend;
-                backend = NULL;
+                backend->reconnect();
             }
-            if (source) {
-                delete source;
-                source = NULL;
+
+            mfLog() << "Loaded backend plugin" << filePath;
+
+            // Add the backend to the backend list
+            if (backend) {
+                backendList << backend;
+            }
+
+            // Add the plugin loader instance to backend loader list
+            if (pluginLoader) {
+                backendLoaderList << pluginLoader;
             }
 
+            // If we reached that point, everything went just fine.
+            continue;
+
+        FAILED:
+            // Calling unload() will delete the instanciated backend
+            // if the backend hasn't been loaded before.
             if (pluginLoader) {
                 if (pluginLoader->isLoaded()) {
                     pluginLoader->unload();
                 }
                 delete pluginLoader;
+                pluginLoader = NULL;
             }
         }
     }
--- src/lib/mfmanager.h
+++ src/lib/mfmanager.h
@@ -45,7 +45,8 @@
 
     bool init(const QString &theme, const QMap<QString, MfBackendInterface::playbackVolume> &volumes);
 
-    virtual void loadPlugins(const QString &libDir);
+    virtual void loadBackendPlugins(const QString &libDir);
+    virtual void loadSourcePlugins(const QString &libDir);
 
     QPointer<MfFeedbackHandle> searchFromCache(const QString &feedbackDir, const int &backendInterfaceIndex);
     void addToCache(MfFeedbackHandle* feedbackHandle);
@@ -72,12 +73,15 @@
     void unloadHandlesForABackend(MfBackendBase* backend);
 
 public:
+    int nonThreadedBackendCount;
     QList<MfBackendBase*> backendList;
     QList<MfSourceInterface*> sourceList;
 
 protected:
     QString currentThemeName;
     QHash<QString, QPointer<MfFeedbackHandle> > feedbackHandleHash;
+    QList<QPluginLoader*> backendLoaderList;
+    QList<QPluginLoader*> sourceLoaderList;
 };
 
 #endif
--- src/lib/mfutil.h
+++ src/lib/mfutil.h
@@ -63,4 +63,14 @@
  */
 void writeTimestamp(const char* message);
 
+/* Get the current timestamp in microseconds.
+ */
+inline qint64 getTimestamp()
+{
+    struct timespec time;
+
+    clock_gettime(CLOCK_MONOTONIC, &time);
+    return (qint64)time.tv_sec*1000000+time.tv_nsec/1000;
+}
+
 #endif
--- tests/ft_configuration/gconf_mock.cpp
+++ tests/ft_configuration/gconf_mock.cpp
@@ -1,7 +1,24 @@
+/* This file is part of meegofeedbackd
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
+#include "gconf_mock.h"
+
 #include <QtGlobal>
 #include <QtDebug>
 
-#include "gconf_mock.h"
 #include "globals.h"
 
 G_DEFINE_TYPE (GConfClient, gconf_client, G_TYPE_OBJECT);
--- tests/ft_configuration/gconf_mock.h
+++ tests/ft_configuration/gconf_mock.h
@@ -1,3 +1,19 @@
+/* This file is part of meegofeedbackd
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * and appearing in the file LICENSE.LGPL included in the packaging
+ * of this file.
+ */
+
 #ifndef GCONF_MOCK_H
 #define GCONF_MOCK_H
 
--- tests/ft_configuration/mfmanagertest.h
+++ tests/ft_configuration/mfmanagertest.h
@@ -37,15 +37,20 @@
     MfManagerTest() {};
     ~MfManagerTest() {};
 
-    void loadPlugins(const QString &libDir)
+    void loadBackendPlugins(const QString &libDir)
     {
-        Q_UNUSED(libDir);
-
-        audioBackend = (MfBackendDummyAudio *)new MfBackendDummyAudio();
-        backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        if (libDir == "/usr/lib/meegofeedbackd/non-threaded-backends") {
+            audioBackend = (MfBackendDummyAudio *)new MfBackendDummyAudio();
+            backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        } else if (libDir == "/usr/lib/meegofeedbackd/threaded-backends") {
+            vibraBackend = new MfBackendDummyVibra();
+            backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+        }
+    };
 
-        vibraBackend = new MfBackendDummyVibra();
-        backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+    void loadSourcePlugins(const QString &libDir)
+    {
+        Q_UNUSED(libDir);
     };
 
     QHash<QString, QPointer<MfFeedbackHandle> > feedbackCache()
--- tests/ft_reconnection/mfconfig_mock.cpp
+++ tests/ft_reconnection/mfconfig_mock.cpp
@@ -14,13 +14,18 @@
  * of this file.
  */
 
-#include <mfconfig.h>
-
 #include "mfconfig_mock.h"
 
+#include <mfconfig.h>
+
 int feedbackLimit = 1000;
 
 int MfConfig::feedbackLengthLimit()
 {
     return feedbackLimit;
 }
+
+qint64 MfConfig::feedbackLatencyLimit()
+{
+    return 2000;
+}
--- tests/ft_reconnection/mfmanagerhandlestest.h
+++ tests/ft_reconnection/mfmanagerhandlestest.h
@@ -14,8 +14,8 @@
  * of this file.
  */
 
-#ifndef MFMANAGERHANDLETEST_H
-#define MFMANAGERHANDLETEST_H
+#ifndef MFMANAGERHANDLESTEST_H
+#define MFMANAGERHANDLESTEST_H
 
 #include <mffeedbackhandle.h>
 #include <mfmanager.h>
@@ -35,16 +35,22 @@
     MfManagerHandlesTest() {};
     ~MfManagerHandlesTest() {};
 
-    void loadPlugins(const QString &libDir)
+    void loadBackendPlugins(const QString &libDir)
+    {
+        if (libDir == "/usr/lib/meegofeedbackd/non-threaded-backends") {
+            audioBackend = (MfBackendDummyAudio *)new MfBackendDummyAudio();
+            audioBackend->init(0);
+            backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        } else if (libDir == "/usr/lib/meegofeedbackd/threaded-backends") {
+            vibraBackend = new MfBackendDummyVibra();
+            vibraBackend->init(0);
+            backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+        }
+    };
+
+    void loadSourcePlugins(const QString &libDir)
     {
         Q_UNUSED(libDir);
-        audioBackend = new MfBackendDummyAudio();
-        audioBackend->init(0);
-        backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
-
-        vibraBackend = new MfBackendDummyVibra();
-        vibraBackend->init(0);
-        backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
     };
 
     bool checkHandleCache(MfBackendBase* backend)
--- tests/ft_reconnection/mfmanagertest.h
+++ tests/ft_reconnection/mfmanagertest.h
@@ -35,16 +35,22 @@
     MfManagerTest() {};
     ~MfManagerTest() {};
 
-    void loadPlugins(const QString &libDir)
+    void loadBackendPlugins(const QString &libDir)
     {
-        Q_UNUSED(libDir);
-        audioBackend = new MfBackendDummyAudio();
-        audioBackend->reconnect();
-        backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        if (libDir == "/usr/lib/meegofeedbackd/non-threaded-backends") {
+            audioBackend = (MfBackendDummyAudio *)new MfBackendDummyAudio();
+            backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+            audioBackend->reconnect();
+        } else if (libDir == "/usr/lib/meegofeedbackd/threaded-backends") {
+            vibraBackend = new MfBackendDummyVibra();
+            vibraBackend->reconnect();
+            backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+        }
+    };
 
-        vibraBackend = new MfBackendDummyVibra();
-        vibraBackend->reconnect();
-        backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+    void loadSourcePlugins(const QString &libDir)
+    {
+        Q_UNUSED(libDir);
     };
 
     MfBackendDummyAudio *audioBackend;
--- tests/ft_theming/main.cpp
+++ tests/ft_theming/main.cpp
@@ -138,7 +138,8 @@
                         manager->vibraBackend->playList.clear();
 
                         // Play feedback
-                        feedback->play();
+                        feedback->emitPlay(-1);
+                        QCoreApplication::processEvents();
 
                         const QList<QPointer<MfFeedbackHandle> >& feedbackHandles = feedback->fbHandles();
                         if (feedbackHandles.size() != feedbackHandleStates[i1].size()) {
@@ -204,10 +205,9 @@
                 appName = lineStr.section(',', 1, 1);
                 qDebug() << "Test case: theme:" << themeName << "app:" << appName;
                 manager->loadTheme(themeName);
-                // Run a small event loop to process the theme loading because
-                // it uses queued connection.
-                QTimer::singleShot(50, QCoreApplication::instance(), SLOT(quit()));
-                QCoreApplication::instance()->exec();
+                // Process events because the theme loading uses
+                // signals/slots with queued connection.
+                QCoreApplication::processEvents();
                 if (session != NULL && oldAppName == appName) {
                     qDebug() << "Application name remained the same, reusing MfSession instance.";
                 } else {
@@ -350,10 +350,9 @@
                 }
                 qDebug() << "Test case: theme:" << themeName << "apps:" << lineStr.section(',', 1);
                 manager->loadTheme(themeName);
-                // Run a small event loop to process the theme loading because
-                // it uses queued connection.
-                QTimer::singleShot(50, QCoreApplication::instance(), SLOT(quit()));
-                QCoreApplication::instance()->exec();
+                // Process events because the theme loading uses
+                // signals/slots with queued connection.
+                QCoreApplication::processEvents();
                 cacheContent.clear();
             } else {
                 // Parse cache entry
--- tests/ft_theming/mfmanagertest.h
+++ tests/ft_theming/mfmanagertest.h
@@ -37,14 +37,20 @@
     MfManagerTest() {};
     ~MfManagerTest() {};
 
-    void loadPlugins(const QString &libDir)
+    void loadBackendPlugins(const QString &libDir)
     {
-        Q_UNUSED(libDir);
-        audioBackend = new MfBackendDummyAudio();
-        backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        if (libDir == "/usr/lib/meegofeedbackd/non-threaded-backends") {
+            audioBackend = (MfBackendDummyAudio *)new MfBackendDummyAudio();
+            backendList.append(dynamic_cast<MfBackendBase*>(audioBackend));
+        } else if (libDir == "/usr/lib/meegofeedbackd/threaded-backends") {
+            vibraBackend = new MfBackendDummyVibra();
+            backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+        }
+    };
 
-        vibraBackend = new MfBackendDummyVibra();
-        backendList.append(dynamic_cast<MfBackendBase*>(vibraBackend));
+    void loadSourcePlugins(const QString &libDir)
+    {
+        Q_UNUSED(libDir);
     };
 
     QHash<QString, QPointer<MfFeedbackHandle> > feedbackCache()
--- tests/ut_mffeedback/mfmanager_mock.cpp
+++ tests/ut_mffeedback/mfmanager_mock.cpp
@@ -70,6 +70,8 @@
     backendInstance = new MfBackendFoo();
     backendList.append(qobject_cast<MfBackendInterface*>(backendInstance));
 
+    nonThreadedBackendCount = 1;
+
     backendInstance = new MfBackendFoo();
     backendList.append(qobject_cast<MfBackendInterface*>(backendInstance));
 
--- tests/ut_mffeedback/mfmanager_mock.h
+++ tests/ut_mffeedback/mfmanager_mock.h
@@ -61,6 +61,7 @@
     void loadTheme(const QString& themeName);
 
 public:
+    int nonThreadedBackendCount;
     QList<MfBackendInterface*> backendList;
     QList<MfSourceInterface*> sourceList;
 
--- tests/ut_mffeedback/ut_mffeedback.cpp
+++ tests/ut_mffeedback/ut_mffeedback.cpp
@@ -70,7 +70,8 @@
     QCOMPARE(feedback->name().isEmpty(), true);
 
     // Play should succeed even in this case
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Nothing should have been actually played
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 0);
@@ -114,7 +115,8 @@
     QCOMPARE(feedback->name(), QString("foo"));
 
     // Play should succeed even in this case
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Nothing should have been actually played
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 0);
@@ -146,7 +148,8 @@
     // Name should be the last part of first path
     QCOMPARE(feedback->name(), QString("foo"));
 
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Played paths should contain 2 paths
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 2);
@@ -184,7 +187,8 @@
     // Name should be the last part of first path
     QCOMPARE(feedback->name(), QString("foo"));
 
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Played paths should contain 0 paths
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 0);
@@ -219,7 +223,8 @@
     // Name should be the last part of first path
     QCOMPARE(feedback->name(), QString("foo"));
 
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Cached handle should have been played twice
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 2);
@@ -266,7 +271,8 @@
         QCOMPARE(testHandles[i].isNull(), false);
     }
 
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Load second set
     feedback->load(dirList2);
@@ -276,7 +282,8 @@
         QCOMPARE(testHandles[i].isNull(), false);
     }
 
-    feedback->play();
+    feedback->emitPlay(-1);
+    QCoreApplication::processEvents();
 
     // Played paths should contain 3 paths
     QCOMPARE(MfFeedbackHandle::playedPaths.size(), 3);
--- tests/ut_mffeedback/ut_mffeedback.pro
+++ tests/ut_mffeedback/ut_mffeedback.pro
@@ -2,7 +2,7 @@
 
 TARGET = ut_mffeedback
 
-COPY_TESTED_SOURCES = $$FMSRCDIR/lib/mffeedback.cpp
+COPY_TESTED_SOURCES = $$FMSRCDIR/lib/mffeedback.cpp $$FMSRCDIR/lib/mfconfig.cpp
 copysourcefiles.input = COPY_TESTED_SOURCES
 copysourcefiles.output = ${QMAKE_FILE_BASE}.cpp
 copysourcefiles.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_BASE}.cpp
--- tests/ut_mfmanager/globals.cpp
+++ tests/ut_mfmanager/globals.cpp
@@ -16,6 +16,11 @@
 
 #include "globals.h"
 
+QList< QPointer<MfBackendFoo> > g_threadedBackendFooInstances;
+QList< QPointer<MfBackendFoo> > g_nonThreadedBackendFooInstances;
 QList< QPointer<MfSourceFoo> > g_sourceFooInstances;
-QList< QPointer<MfBackendFoo> > g_backendFooInstances;
-QStringList g_directoryEntryList;
+QStringList g_threadedBackendsDirectoryEntryList;
+QStringList g_nonThreadedBackendsDirectoryEntryList;
+QStringList g_sourcesDirectoryEntryList;
+QStringList g_loadedInstances;
+
--- tests/ut_mfmanager/globals.h
+++ tests/ut_mfmanager/globals.h
@@ -24,9 +24,15 @@
 #include "mfsourcefoo.h"
 #include "mfbackendfoo.h"
 
+extern QList< QPointer<MfBackendFoo> > g_threadedBackendFooInstances;
+extern QList< QPointer<MfBackendFoo> > g_nonThreadedBackendFooInstances;
 extern QList< QPointer<MfSourceFoo> > g_sourceFooInstances;
-extern QList< QPointer<MfBackendFoo> > g_backendFooInstances;
 
-// What files are present in meegofeedbackd's lib dir
-extern QStringList g_directoryEntryList;
+// What files are present in meegofeedbackd's lib dirs
+extern QStringList g_threadedBackendsDirectoryEntryList;
+extern QStringList g_nonThreadedBackendsDirectoryEntryList;
+extern QStringList g_sourcesDirectoryEntryList;
+
+// Used by mocked QPluginLoader to keep track of loaded instances
+extern QStringList g_loadedInstances;
 #endif
--- tests/ut_mfmanager/mfbackendfoo.cpp
+++ tests/ut_mfmanager/mfbackendfoo.cpp
@@ -18,7 +18,11 @@
 
 MfBackendFoo::MfBackendFoo() : limit(0)
 {
-    initCalled = false;
+    initCalled = 0;
+}
+
+MfBackendFoo::~MfBackendFoo()
+{
 }
 
 QString MfBackendFoo::name() const
@@ -28,7 +32,7 @@
 
 bool MfBackendFoo::init(int lengthLimit)
 {
-    initCalled = true;
+    initCalled++;;
     limit = lengthLimit;
 
     return true;
--- tests/ut_mfmanager/mfbackendfoo.h
+++ tests/ut_mfmanager/mfbackendfoo.h
@@ -27,6 +27,7 @@
 public:
 
     MfBackendFoo();
+    ~MfBackendFoo();
 
     // Implementation of MfBackendInterface methods
     QString name() const;
@@ -37,7 +38,7 @@
     void play(void *feedbackHandle);
     bool isReady();
 
-    bool initCalled;
+    int initCalled;
     int limit;
 };
 
--- tests/ut_mfmanager/mfsourcefoo.cpp
+++ tests/ut_mfmanager/mfsourcefoo.cpp
@@ -15,15 +15,20 @@
  */
 
 #include "mfsourcefoo.h"
+#include <QDebug>
 
 MfSourceFoo::MfSourceFoo(QObject *parent)
     : QObject(parent)
 {
-    initCalled = false;
+    initCalled = 0;
+}
+
+MfSourceFoo::~MfSourceFoo()
+{
 }
 
 bool MfSourceFoo::init()
 {
-    initCalled = true;
+    initCalled++;
     return true;
 }
--- tests/ut_mfmanager/mfsourcefoo.h
+++ tests/ut_mfmanager/mfsourcefoo.h
@@ -26,9 +26,11 @@
 
 public:
     MfSourceFoo(QObject *parent = 0);
+    ~MfSourceFoo();
+
     bool init();
 
-    bool initCalled;
+    int initCalled;
 };
 
 #endif
--- tests/ut_mfmanager/qdir_mock.cpp
+++ tests/ut_mfmanager/qdir_mock.cpp
@@ -15,12 +15,15 @@
  */
 
 #include <QDir>
+#include <QString>
 
 #include "globals.h"
 
+static QString g_path;
+
 void QDir::setPath(const QString &path)
 {
-    Q_UNUSED(path);
+    g_path = path;
 }
 
 bool QDir::exists() const
@@ -33,10 +36,18 @@
     Q_UNUSED(filters);
     Q_UNUSED(sort);
 
-    return g_directoryEntryList;
+    if (g_path == "/usr/lib/meegofeedbackd/threaded-backends") {
+        return g_threadedBackendsDirectoryEntryList;
+    } else if (g_path == "/usr/lib/meegofeedbackd/non-threaded-backends") {
+        return g_nonThreadedBackendsDirectoryEntryList;
+    } else if (g_path == "/usr/lib/meegofeedbackd/sources") {
+        return g_sourcesDirectoryEntryList;
+    } else {
+        return QStringList();
+    }
 }
 
 QString QDir::filePath(const QString &fileName) const
 {
-    return "/usr/lib/meegofeedbackd/" + fileName;
+    return g_path + "/" + fileName;
 }
--- tests/ut_mfmanager/qpluginloader_mock.cpp
+++ tests/ut_mfmanager/qpluginloader_mock.cpp
@@ -23,18 +23,23 @@
 QPluginLoader::QPluginLoader(QObject *parent)
     : QObject(parent)
 {
+    m_instance = NULL;
     m_isLoaded = false;
 }
 
 QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
     : QObject(parent)
 {
+    m_instance = NULL;
     m_fileName = fileName;
     m_isLoaded = false;
 }
 
 QPluginLoader::~QPluginLoader()
 {
+    if (m_isLoaded) {
+        unload();
+    }
 }
 
 QObject *QPluginLoader::instance()
@@ -55,12 +60,38 @@
 {
     bool ok = true;
 
-    if (m_fileName == "/usr/lib/meegofeedbackd/libmeegofeedback-befoo.so") {
-        m_instance = new MfBackendFoo();
-        g_backendFooInstances.append((MfBackendFoo*)m_instance);
-    } else if (m_fileName == "/usr/lib/meegofeedbackd/libmeegofeedback-srcfoo.so") {
-        m_instance = new MfSourceFoo();
+    if (m_fileName == "/usr/lib/meegofeedbackd/threaded-backends/libmeegofeedback-befoo1.so" ||
+        m_fileName == "/usr/lib/meegofeedbackd/threaded-backends/libmeegofeedback-befoo2.so") {
+        if (!g_loadedInstances.contains(m_fileName)) {
+            // First creation of this backend
+            m_instance = new MfBackendFoo();
+        } else {
+            // This backend has been instanciated before
+            m_instance = g_threadedBackendFooInstances[0];
+        }
+        g_threadedBackendFooInstances.append((MfBackendFoo*)m_instance);
+        g_loadedInstances.append(m_fileName);
+    } else if (m_fileName == "/usr/lib/meegofeedbackd/non-threaded-backends/libmeegofeedback-befoo1.so" ||
+               m_fileName == "/usr/lib/meegofeedbackd/non-threaded-backends/libmeegofeedback-befoo2.so") {
+        if (!g_loadedInstances.contains(m_fileName)) {
+            // First creation of this backend
+            m_instance = new MfBackendFoo();
+        } else {
+            // This backend has been instanciated before
+            m_instance = g_nonThreadedBackendFooInstances[0];
+        }
+        g_nonThreadedBackendFooInstances.append((MfBackendFoo*)m_instance);
+        g_loadedInstances.append(m_fileName);
+    } else if (m_fileName == "/usr/lib/meegofeedbackd/sources/libmeegofeedback-srcfoo.so") {
+        if (!g_loadedInstances.contains(m_fileName)) {
+            // First creation of this backend
+            m_instance = new MfSourceFoo();
+        } else {
+            // This backend has been instanciated before
+            m_instance = g_sourceFooInstances[0];
+        }
         g_sourceFooInstances.append((MfSourceFoo*)m_instance);
+        g_loadedInstances.append(m_fileName);
     } else {
         ok = false;
     }
@@ -72,7 +103,14 @@
 {
     if (m_instance)
     {
-        delete m_instance;
+        // g_loadedInstances is used as a reference count of created instances.
+        // Instance will be removed only when it is the last existing instance.
+        if (g_loadedInstances.count(m_fileName) <= 1) {
+            delete m_instance;
+        }
+        if (g_loadedInstances.contains(m_fileName)) {
+            g_loadedInstances.removeAt(g_loadedInstances.indexOf(m_fileName));
+        }
         m_instance = 0;
         m_isLoaded = false;
     }
--- tests/ut_mfmanager/ut_mfmanager.cpp
+++ tests/ut_mfmanager/ut_mfmanager.cpp
@@ -33,9 +33,13 @@
 
 void Ut_MfManager::cleanup()
 {
+    g_threadedBackendFooInstances.clear();
+    g_nonThreadedBackendFooInstances.clear();
     g_sourceFooInstances.clear();
-    g_backendFooInstances.clear();
-    g_directoryEntryList.clear();
+    g_threadedBackendsDirectoryEntryList.clear();
+    g_nonThreadedBackendsDirectoryEntryList.clear();
+    g_sourcesDirectoryEntryList.clear();
+    g_loadedInstances.clear();
 }
 
 void Ut_MfManager::initNoSourcesOneBackend()
@@ -45,8 +49,8 @@
     QMap<QString, MfBackendInterface::playbackVolume> emptyMap;
     bool ok;
 
-    // There's only one backend there and nothing more.
-    g_directoryEntryList << "libmeegofeedback-befoo.so";
+    // There's only one non-threaded backend there and nothing more.
+    g_nonThreadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so";
 
     // Initialization should not fail if there is backend
     // but no source. Sources are optional.
@@ -55,18 +59,19 @@
     QVERIFY(ok);
 
     // Get the loaded backend
-    QCOMPARE(g_backendFooInstances.size(), 1);
-    fooBackend = g_backendFooInstances.at(0);
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 1);
+    fooBackend = g_nonThreadedBackendFooInstances.at(0);
 
-    // Run a small event loop to process the backend initialization because
-    // it uses queued connection.
-    QTimer::singleShot(50, QCoreApplication::instance(), SLOT(quit()));
-    QCoreApplication::instance()->exec();
+    // Wait a while to get backend initialised. Backend initalization
+    // uses queued signals/slots so this gives some time to process
+    // those events.
+    QTest::qWait(50);
 
     // Check that MfBackendFoo was properly initialized
-    QVERIFY(fooBackend->initCalled);
+    QCOMPARE(fooBackend->initCalled, 1);
 
-    // No sources loaded, naturally
+    // Make sure nothing extra was created
+    QCOMPARE(g_threadedBackendFooInstances.size(), 0);
     QCOMPARE(g_sourceFooInstances.size(), 0);
 
     delete manager;
@@ -84,7 +89,8 @@
     // It can't init if there's no backend library available.
     QCOMPARE(ok, false);
 
-    QCOMPARE(g_backendFooInstances.size(), 0);
+    QCOMPARE(g_threadedBackendFooInstances.size(), 0);
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 0);
     QCOMPARE(g_sourceFooInstances.size(), 0);
 
     delete manager;
@@ -99,24 +105,22 @@
     bool ok;
 
     // Put one source library and one backend library there
-    g_directoryEntryList << "libmeegofeedback-befoo.so";
-    g_directoryEntryList << "libmeegofeedback-srcfoo.so";
+    g_threadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so";
+    g_sourcesDirectoryEntryList << "libmeegofeedback-srcfoo.so";
 
     manager = new MfManager();
     ok = manager->init(MeegoDefaultTheme, emptyMap);
     QVERIFY(ok);
 
     // Get the loaded backend
-    QCOMPARE(g_backendFooInstances.size(), 1);
-    fooBackend = g_backendFooInstances.at(0);
+    QCOMPARE(g_threadedBackendFooInstances.size(), 1);
+    fooBackend = g_threadedBackendFooInstances.at(0);
 
-    // Run a small event loop to process the backend initialization because
-    // it uses queued connection.
-    QTimer::singleShot(50, QCoreApplication::instance(), SLOT(quit()));
-    QCoreApplication::instance()->exec();
+    // Wait a while to get backend initialised
+    QTest::qWait(50);
 
     // Check that MfBackendFoo was properly initialized
-    QVERIFY(fooBackend->initCalled);
+    QCOMPARE(fooBackend->initCalled, 1);
     QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
 
     // Get the loaded source
@@ -124,7 +128,199 @@
     fooSource = g_sourceFooInstances.at(0);
 
     // Check that MfSourceFoo was properly initialized
-    QVERIFY(fooSource->initCalled);
+    QCOMPARE(fooSource->initCalled, 1);
+
+    // Make sure nothing extra was created
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 0);
+
+    delete manager;
+}
+
+void Ut_MfManager::initOneSourceTwoBackends()
+{
+    MfManager *manager;
+    MfBackendFoo *fooBackend = 0;
+    MfSourceFoo *fooSource = 0;
+    QMap<QString, MfBackendInterface::playbackVolume> emptyMap;
+    bool ok;
+
+    // Put one source library and one backend library there
+    g_threadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so";
+    g_nonThreadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so";
+    g_sourcesDirectoryEntryList << "libmeegofeedback-srcfoo.so";
+
+    manager = new MfManager();
+    ok = manager->init(MeegoDefaultTheme, emptyMap);
+    QVERIFY(ok);
+
+    // Wait a while to get backend initialised
+    QTest::qWait(50);
+
+    // Get the loaded backend
+    QCOMPARE(g_threadedBackendFooInstances.size(), 1);
+    fooBackend = g_threadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Get the loaded backend
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 1);
+    fooBackend = g_nonThreadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Get the loaded source
+    QCOMPARE(g_sourceFooInstances.size(), 1);
+    fooSource = g_sourceFooInstances.at(0);
+
+    // Check that MfSourceFoo was properly initialized
+    QCOMPARE(fooSource->initCalled, 1);
+
+    delete manager;
+}
+
+void Ut_MfManager::initFourBackends()
+{
+    MfManager *manager;
+    MfBackendFoo *fooBackend = 0;
+    QMap<QString, MfBackendInterface::playbackVolume> emptyMap;
+    bool ok;
+
+    // Put one source library and one backend library there
+    g_threadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so" << "libmeegofeedback-befoo2.so";
+    g_nonThreadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so" << "libmeegofeedback-befoo2.so";
+
+    manager = new MfManager();
+    ok = manager->init(MeegoDefaultTheme, emptyMap);
+    QVERIFY(ok);
+
+    // Wait a while to get backend initialised
+    QTest::qWait(50);
+
+    // Get the loaded backend #1
+    QCOMPARE(g_threadedBackendFooInstances.size(), 2);
+    fooBackend = g_threadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Get the loaded backend #2
+    fooBackend = g_threadedBackendFooInstances.at(1);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Get the loaded backend #3
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 2);
+    fooBackend = g_nonThreadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Get the loaded backend #4
+    fooBackend = g_nonThreadedBackendFooInstances.at(1);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Make sure nothing extra was created
+    QCOMPARE(g_sourceFooInstances.size(), 0);
+
+    delete manager;
+}
+
+void Ut_MfManager::initTwoBackendsTwice()
+{
+    MfManager *manager;
+    MfBackendFoo *fooBackend = 0;
+    QMap<QString, MfBackendInterface::playbackVolume> emptyMap;
+    bool ok;
+
+    // Put one source library and one backend library there
+    g_threadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so" << "libmeegofeedback-befoo1.so";
+    g_nonThreadedBackendsDirectoryEntryList << "libmeegofeedback-befoo2.so" << "libmeegofeedback-befoo2.so";
+
+    manager = new MfManager();
+    ok = manager->init(MeegoDefaultTheme, emptyMap);
+    QVERIFY(ok);
+
+    // Wait a while to get backend initialised
+    QTest::qWait(50);
+
+    // The two backend instances should be the same and
+    // the init method should be called only once.
+    QCOMPARE(g_threadedBackendFooInstances.size(), 2);
+    QCOMPARE(g_threadedBackendFooInstances.at(0),
+             g_threadedBackendFooInstances.at(1));
+    fooBackend = g_threadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // The two backend instances should be the same and
+    // the init method should be called only once.
+    QCOMPARE(g_nonThreadedBackendFooInstances.size(), 2);
+    QCOMPARE(g_nonThreadedBackendFooInstances.at(0),
+             g_nonThreadedBackendFooInstances.at(1));
+    fooBackend = g_nonThreadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // Make sure nothing extra was created
+    QCOMPARE(g_sourceFooInstances.size(), 0);
+
+    delete manager;
+}
+
+void Ut_MfManager::initTwoSourcesTwice()
+{
+    MfManager *manager;
+    MfSourceFoo *fooSource = 0;
+    MfBackendFoo *fooBackend = 0;
+    QMap<QString, MfBackendInterface::playbackVolume> emptyMap;
+    bool ok;
+
+    // Put one source library and one backend library there
+    g_threadedBackendsDirectoryEntryList << "libmeegofeedback-befoo1.so";
+    g_sourcesDirectoryEntryList << "libmeegofeedback-srcfoo.so" << "libmeegofeedback-srcfoo.so";
+
+    manager = new MfManager();
+    ok = manager->init(MeegoDefaultTheme, emptyMap);
+    QVERIFY(ok);
+
+    // Wait a while to get backend initialised
+    QTest::qWait(50);
+
+    // Get the loaded backend
+    QCOMPARE(g_threadedBackendFooInstances.size(), 1);
+    fooBackend = g_threadedBackendFooInstances.at(0);
+
+    // Check that MfBackendFoo was properly initialized
+    QCOMPARE(fooBackend->initCalled, 1);
+    QVERIFY(fooBackend->limit == MfConfig::feedbackLengthLimit());
+
+    // The two source instances should be the same and
+    // the init method should be called only once.
+    QCOMPARE(g_sourceFooInstances.size(), 2);
+    QCOMPARE(g_sourceFooInstances.at(0),
+             g_sourceFooInstances.at(1));
+    fooSource = g_sourceFooInstances.at(0);
+
+    // Check that MfSourceFoo was properly initialized
+    QCOMPARE(fooSource->initCalled, 1);
+
+    // Make sure nothing extra was created
+    QCOMPARE(g_nonThreadedBackendsDirectoryEntryList.size(), 0);
 
     delete manager;
 }
--- tests/ut_mfmanager/ut_mfmanager.h
+++ tests/ut_mfmanager/ut_mfmanager.h
@@ -33,6 +33,10 @@
     void initNoSourcesOneBackend();
     void initNoSourcesNoBackend();
     void initOneSourceOneBackend();
+    void initOneSourceTwoBackends();
+    void initFourBackends();
+    void initTwoBackendsTwice();
+    void initTwoSourcesTwice();
 };
 
 #endif



More information about the MeeGo-commits mailing list