From 2095658e2c850e7f355c1bd1f5ee33e3a5b961f5 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 21 Aug 2009 10:29:48 +0200
Subject: [PATCH] Trk: Factor out write queue using a functor.

---
 tests/manual/trk/launcher.cpp    | 180 +++++--------------------------
 tests/manual/trk/launcher.h      |  16 ---
 tests/manual/trk/trkdevice.cpp   | 176 +++++++++++++++++++++++++++++-
 tests/manual/trk/trkdevice.h     |  54 +++++++++-
 tests/manual/trk/trkfunctor.h    | 144 +++++++++++++++++++++++++
 tests/manual/trk/trklauncher.pri |   1 +
 6 files changed, 398 insertions(+), 173 deletions(-)
 create mode 100644 tests/manual/trk/trkfunctor.h

diff --git a/tests/manual/trk/launcher.cpp b/tests/manual/trk/launcher.cpp
index 6e3a68ee3f3..a3d3d06e035 100644
--- a/tests/manual/trk/launcher.cpp
+++ b/tests/manual/trk/launcher.cpp
@@ -40,32 +40,18 @@
 
 namespace trk {
 
-struct TrkMessage {
-    TrkMessage() { code = token = 0; callBack = 0; }
-    byte code;
-    byte token;
-    QByteArray data;
-    QVariant cookie;
-    Launcher::TrkCallBack callBack;
-};
+typedef TrkWriteQueueDevice::Callback Callback;
 
 struct LauncherPrivate {
     LauncherPrivate();
-    TrkDevice m_device;
+    TrkWriteQueueDevice m_device;
     QString m_trkServerName;
     QByteArray m_trkReadBuffer;
 
-    unsigned char m_trkWriteToken;
-    QQueue<TrkMessage> m_trkWriteQueue;
-    QHash<byte, TrkMessage> m_writtenTrkMessages;
-    QByteArray m_trkReadQueue;
-    bool m_trkWriteBusy;
-
     void logMessage(const QString &msg);
     // Debuggee state
     Session m_session; // global-ish data (process id, target information)
 
-    int m_timerId;
     QString m_fileName;
     QString m_copySrcFileName;
     QString m_copyDstFileName;
@@ -74,15 +60,10 @@ struct LauncherPrivate {
 };
 
 LauncherPrivate::LauncherPrivate() :
-    m_trkWriteToken(0),
-    m_trkWriteBusy(false),
-    m_timerId(-1),
     m_verbose(0)
 {
 }
 
-#define CB(s) &Launcher::s
-
 Launcher::Launcher() :
     d(new LauncherPrivate)
 {
@@ -135,12 +116,11 @@ bool Launcher::startServer(QString *errorMessage)
     }
     if (!d->m_device.open(d->m_trkServerName, errorMessage))
         return false;
-    d->m_timerId = startTimer(100);
-    sendTrkInitialPing();
-    sendTrkMessage(TrkConnect); // Connect
-    sendTrkMessage(TrkSupported, CB(handleSupportMask));
-    sendTrkMessage(TrkCpuType, CB(handleCpuType));
-    sendTrkMessage(TrkVersions, CB(handleTrkVersion));
+    d->m_device.sendTrkInitialPing();
+    d->m_device.sendTrkMessage(TrkConnect); // Connect
+    d->m_device.sendTrkMessage(TrkSupported, Callback(this, &Launcher::handleSupportMask));
+    d->m_device.sendTrkMessage(TrkCpuType, Callback(this, &Launcher::handleCpuType));
+    d->m_device.sendTrkMessage(TrkVersions, Callback(this, &Launcher::handleTrkVersion));
     if (d->m_fileName.isEmpty())
         return true;
     if (!d->m_copySrcFileName.isEmpty() && !d->m_copyDstFileName.isEmpty())
@@ -170,45 +150,10 @@ void Launcher::logMessage(const QString &msg)
         qDebug() << "ADAPTER: " << qPrintable(msg);
 }
 
-void Launcher::timerEvent(QTimerEvent *)
-{
-    if (d->m_verbose>1)
-        qDebug(".");
-    tryTrkWrite();
-}
-
-byte Launcher::nextTrkWriteToken()
-{
-    ++d->m_trkWriteToken;
-    if (d->m_trkWriteToken == 0)
-        ++d->m_trkWriteToken;
-    return d->m_trkWriteToken;
-}
-
-void Launcher::sendTrkMessage(byte code, TrkCallBack callBack,
-    const QByteArray &data, const QVariant &cookie)
-{
-    TrkMessage msg;
-    msg.code = code;
-    msg.token = nextTrkWriteToken();
-    msg.callBack = callBack;
-    msg.data = data;
-    msg.cookie = cookie;
-    queueTrkMessage(msg);
-}
-
-void Launcher::sendTrkInitialPing()
-{
-    TrkMessage msg;
-    msg.code = 0x00; // Ping
-    msg.token = 0; // reset sequence count
-    queueTrkMessage(msg);
-}
-
 void Launcher::waitForTrkFinished(const TrkResult &result)
 {
     Q_UNUSED(result)
-    sendTrkMessage(TrkPing, CB(handleWaitForFinished));
+    d->m_device.sendTrkMessage(TrkPing, Callback(this, &Launcher::handleWaitForFinished));
 }
 
 void Launcher::terminate()
@@ -216,57 +161,11 @@ void Launcher::terminate()
     QByteArray ba;
     appendShort(&ba, 0x0000, TargetByteOrder);
     appendInt(&ba, d->m_session.pid, TargetByteOrder);
-    sendTrkMessage(TrkDeleteItem, CB(waitForTrkFinished), ba);
-}
-
-void Launcher::sendTrkAck(byte token)
-{
-    logMessage(QString("SENDING ACKNOWLEDGEMENT FOR TOKEN %1").arg(int(token)));
-    TrkMessage msg;
-    msg.code = 0x80;
-    msg.token = token;
-    msg.data.append('\0');
-    // The acknowledgement must not be queued!
-    //queueMessage(msg);
-    trkWriteRawMessage(msg);
-    // 01 90 00 07 7e 80 01 00 7d 5e 7e
-}
-
-void Launcher::queueTrkMessage(const TrkMessage &msg)
-{
-    d->m_trkWriteQueue.append(msg);
-}
-
-void Launcher::tryTrkWrite()
-{
-    if (d->m_trkWriteBusy)
-        return;
-    if (d->m_trkWriteQueue.isEmpty())
-        return;
-
-    TrkMessage msg = d->m_trkWriteQueue.dequeue();
-    trkWrite(msg);
-}
-
-void Launcher::trkWriteRawMessage(const TrkMessage &msg)
-{
-    const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame());
-    logMessage("WRITE: " + stringFromArray(ba));
-    QString errorMessage;
-    if (!d->m_device.write(ba, &errorMessage))
-        logMessage(errorMessage);
-}
-
-void Launcher::trkWrite(const TrkMessage &msg)
-{
-    d->m_writtenTrkMessages.insert(msg.token, msg);
-    d->m_trkWriteBusy = true;
-    trkWriteRawMessage(msg);
+    d->m_device.sendTrkMessage(TrkDeleteItem, Callback(this, &Launcher::waitForTrkFinished), ba);
 }
 
 void Launcher::handleResult(const TrkResult &result)
 {
-    d->m_trkWriteBusy = false;
     QByteArray prefix = "READ BUF:                                       ";
     QByteArray str = result.toString().toUtf8();
     if (result.isDebugOutput) { // handle application output
@@ -275,28 +174,8 @@ void Launcher::handleResult(const TrkResult &result)
         return;
     }
     switch (result.code) {
-        case TrkNotifyAck: { // ACK
-            //logMessage(prefix + "ACK: " + str);
-            if (!result.data.isEmpty() && result.data.at(0))
-                logMessage(prefix + "ERR: " +QByteArray::number(result.data.at(0)));
-            //logMessage("READ RESULT FOR TOKEN: " << token);
-            if (!d->m_writtenTrkMessages.contains(result.token)) {
-                logMessage("NO ENTRY FOUND!");
-            }
-            TrkMessage msg = d->m_writtenTrkMessages.take(result.token);
-            TrkResult result1 = result;
-            result1.cookie = msg.cookie;
-            TrkCallBack cb = msg.callBack;
-            if (cb) {
-                //logMessage("HANDLE: " << stringFromArray(result.data));
-                (this->*cb)(result1);
-            } else {
-                QString msg = result.cookie.toString();
-                if (!msg.isEmpty())
-                    logMessage("HANDLE: " + msg + stringFromArray(result.data));
-            }
+        case TrkNotifyAck:
             break;
-        }
         case TrkNotifyNak: { // NAK
             logMessage(prefix + "NAK: " + str);
             //logMessage(prefix << "TOKEN: " << result.token);
@@ -311,17 +190,17 @@ void Launcher::handleResult(const TrkResult &result)
 //            uint pid = extractInt(data + 4); // ProcessID: 4 bytes;
 //            uint tid = extractInt(data + 8); // ThreadID: 4 bytes
             //logMessage(prefix << "      ADDR: " << addr << " PID: " << pid << " TID: " << tid);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
         case TrkNotifyException: { // Notify Exception (obsolete)
             logMessage(prefix + "NOTE: EXCEPTION  " + str);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
         case TrkNotifyInternalError: { //
             logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
 
@@ -353,8 +232,8 @@ void Launcher::handleResult(const TrkResult &result)
             QByteArray ba;
             appendInt(&ba, d->m_session.pid);
             appendInt(&ba, d->m_session.tid);
-            sendTrkMessage(TrkContinue, 0, ba, "CONTINUE");
-            //sendTrkAck(result.token)
+            d->m_device.sendTrkMessage(TrkContinue, Callback(), ba, "CONTINUE");
+            //d->m_device.sendTrkAck(result.token)
             break;
         }
         case TrkNotifyDeleted: { // NotifyDeleted
@@ -364,25 +243,25 @@ void Launcher::handleResult(const TrkResult &result)
             logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
                        arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
                        arg(name));
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             if (itemType == 0) { // process
-                sendTrkMessage(TrkDisconnect, CB(waitForTrkFinished));
+                d->m_device.sendTrkMessage(TrkDisconnect, Callback(this, &Launcher::waitForTrkFinished));
             }
             break;
         }
         case TrkNotifyProcessorStarted: { // NotifyProcessorStarted
             logMessage(prefix + "NOTE: PROCESSOR STARTED: " + str);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
         case TrkNotifyProcessorStandBy: { // NotifyProcessorStandby
             logMessage(prefix + "NOTE: PROCESSOR STANDBY: " + str);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
         case TrkNotifyProcessorReset: { // NotifyProcessorReset
             logMessage(prefix + "NOTE: PROCESSOR RESET: " + str);
-            sendTrkAck(result.token);
+            d->m_device.sendTrkAck(result.token);
             break;
         }
         default: {
@@ -409,7 +288,7 @@ void Launcher::handleTrkVersion(const TrkResult &result)
                 << " float size: " << d->m_session.fpTypeSize
                 << " Trk: v" << trkMajor << '.' << trkMinor << " Protocol: " << protocolMajor << '.' << protocolMinor;
         qWarning("%s", qPrintable(msg));
-        sendTrkMessage(TrkPing, CB(waitForTrkFinished));
+        d->m_device.sendTrkMessage(TrkPing, Callback(this, &Launcher::waitForTrkFinished));
     }
 }
 
@@ -429,13 +308,13 @@ void Launcher::handleFileCreation(const TrkResult &result)
         QByteArray ba;
         appendInt(&ba, copyFileHandle, TargetByteOrder);
         appendString(&ba, src.mid(pos, BLOCKSIZE), TargetByteOrder, false);
-        sendTrkMessage(TrkWriteFile, 0, ba);
+        d->m_device.sendTrkMessage(TrkWriteFile, Callback(), ba);
         pos += BLOCKSIZE;
     }
     QByteArray ba;
     appendInt(&ba, copyFileHandle, TargetByteOrder);
     appendInt(&ba, QDateTime::currentDateTime().toTime_t(), TargetByteOrder);
-    sendTrkMessage(TrkCloseFile, CB(handleFileCreated), ba);
+    d->m_device.sendTrkMessage(TrkCloseFile, Callback(this, &Launcher::handleFileCreated), ba);
 }
 
 void Launcher::handleFileCreated(const TrkResult &result)
@@ -480,13 +359,12 @@ void Launcher::handleCreateProcess(const TrkResult &result)
     QByteArray ba;
     appendInt(&ba, d->m_session.pid);
     appendInt(&ba, d->m_session.tid);
-    sendTrkMessage(TrkContinue, 0, ba, "CONTINUE");
+    d->m_device.sendTrkMessage(TrkContinue, Callback(), ba, "CONTINUE");
 }
 
 void Launcher::handleWaitForFinished(const TrkResult &result)
 {
     logMessage("   FINISHED: " + stringFromArray(result.data));
-    killTimer(d->m_timerId);
     emit finished();
 }
 
@@ -516,7 +394,7 @@ void Launcher::cleanUp()
     appendByte(&ba, 0x00);
     appendByte(&ba, 0x00);
     appendInt(&ba, d->m_session.pid);
-    sendTrkMessage(TrkDeleteItem, 0, ba, "Delete process");
+    d->m_device.sendTrkMessage(TrkDeleteItem, Callback(), ba, "Delete process");
 
     //---TRK------------------------------------------------------
     //  Command: 0x80 Acknowledge
@@ -552,7 +430,7 @@ void Launcher::cleanUp()
     //---IDE------------------------------------------------------
     //  Command: 0x02 Disconnect
     // [02 27]
-//    sendTrkMessage(0x02, CB(handleDisconnect));
+//    sendTrkMessage(0x02, Callback(this, &Launcher::handleDisconnect));
     //---TRK------------------------------------------------------
     //  Command: 0x80 Acknowledge
     // Error: 0x00
@@ -564,7 +442,7 @@ void Launcher::copyFileToRemote()
     QByteArray ba;
     appendByte(&ba, 0x10);
     appendString(&ba, d->m_copyDstFileName.toLocal8Bit(), TargetByteOrder, false);
-    sendTrkMessage(TrkOpenFile, CB(handleFileCreation), ba);
+    d->m_device.sendTrkMessage(TrkOpenFile, Callback(this, &Launcher::handleFileCreation), ba);
 }
 
 void Launcher::installRemotePackageSilently(const QString &fileName)
@@ -573,7 +451,7 @@ void Launcher::installRemotePackageSilently(const QString &fileName)
     QByteArray ba;
     appendByte(&ba, 'C');
     appendString(&ba, fileName.toLocal8Bit(), TargetByteOrder, false);
-    sendTrkMessage(TrkInstallFile, CB(handleInstallPackageFinished), ba);
+    d->m_device.sendTrkMessage(TrkInstallFile, Callback(this, &Launcher::handleInstallPackageFinished), ba);
 }
 
 void Launcher::handleInstallPackageFinished(const TrkResult &)
@@ -594,7 +472,7 @@ void Launcher::startInferiorIfNeeded()
     appendByte(&ba, 0); // ?
     appendByte(&ba, 0); // ?
     appendString(&ba, d->m_fileName.toLocal8Bit(), TargetByteOrder);
-    sendTrkMessage(TrkCreateItem, CB(handleCreateProcess), ba); // Create Item
+    d->m_device.sendTrkMessage(TrkCreateItem, Callback(this, &Launcher::handleCreateProcess), ba); // Create Item
 }
 
 }
diff --git a/tests/manual/trk/launcher.h b/tests/manual/trk/launcher.h
index d37aab16bb6..c2527d57d5c 100644
--- a/tests/manual/trk/launcher.h
+++ b/tests/manual/trk/launcher.h
@@ -70,27 +70,11 @@ private slots:
     void handleResult(const TrkResult &data);
 
 private:
-    void sendTrkMessage(unsigned char code,
-        TrkCallBack callBack = 0,
-        const QByteArray &data = QByteArray(),
-        const QVariant &cookie = QVariant());
-    // adds message to 'send' queue
-    void queueTrkMessage(const TrkMessage &msg);
-    void tryTrkWrite();
     void tryTrkRead();
-    // actually writes a message to the device
-    void trkWrite(const TrkMessage &msg);
-    void trkWriteRawMessage(const TrkMessage &msg);
-    // convienience messages
-    void sendTrkInitialPing();
-    void sendTrkAck(unsigned char token);
 
     // kill process and breakpoints
     void cleanUp();
 
-    void timerEvent(QTimerEvent *ev);
-    unsigned char nextTrkWriteToken();
-
     void handleFileCreation(const TrkResult &result);
     void handleFileCreated(const TrkResult &result);
     void handleInstallPackageFinished(const TrkResult &result);
diff --git a/tests/manual/trk/trkdevice.cpp b/tests/manual/trk/trkdevice.cpp
index a045fa41027..13608cb2da8 100644
--- a/tests/manual/trk/trkdevice.cpp
+++ b/tests/manual/trk/trkdevice.cpp
@@ -32,6 +32,9 @@
 
 #include <QtCore/QString>
 #include <QtCore/QDebug>
+#include <QtCore/QQueue>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
 
 #ifdef Q_OS_WIN
 #  include <windows.h>
@@ -194,6 +197,7 @@ void TrkDevice::close()
     }
 #ifdef Q_OS_WIN
     CloseHandle(d->hdevice);
+    d->hdevice = INVALID_HANDLE_VALUE;
 #else
     d->file.close();
 #endif
@@ -287,8 +291,8 @@ void TrkDevice::tryTrkRead()
         return;
     const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
     if (!len) {
-        d->errorString = QString::fromLatin1("Partial message: %1").arg(stringFromArray(d->trkReadBuffer));
-        qWarning("%s\n", qPrintable(d->errorString));
+        const QString msg = QString::fromLatin1("Partial message: %1").arg(stringFromArray(d->trkReadBuffer));
+        emitError(msg);
         return;
     }
 #else
@@ -300,9 +304,9 @@ void TrkDevice::tryTrkRead()
     const ushort len = isValidTrkResult(d->trkReadBuffer, d->serialFrame);
     if (!len) {
         if (d->trkReadBuffer.size() > 10) {
-            d->errorString = QString::fromLatin1("Unable to extract message from '%1' '%2'").
+            const QString msg = QString::fromLatin1("Unable to extract message from '%1' '%2'").
                              arg(QLatin1String(d->trkReadBuffer.toHex())).arg(QString::fromAscii(d->trkReadBuffer));
-            qWarning("%s\n", qPrintable(d->errorString));
+            emitError(msg);
         }
         return;
     }
@@ -320,4 +324,166 @@ void TrkDevice::timerEvent(QTimerEvent *)
     tryTrkRead();
 }
 
-} // namespace trk
+void TrkDevice::emitError(const QString &s)
+{
+    d->errorString = s;
+    qWarning("%s\n", qPrintable(s));
+    emit error(s);
+}
+
+/* A message to be send to TRK, triggering a callback on receipt
+ * of the answer. */
+struct TrkMessage {
+    typedef TrkFunctor1<const TrkResult &> Callback;
+
+    explicit TrkMessage(unsigned char code = 0u,
+                        unsigned char token = 0u,
+                        Callback callback = Callback());
+
+    unsigned char code;
+    unsigned char token;
+    QByteArray data;
+    QVariant cookie;
+    Callback callback;
+    bool invokeOnNAK;
+};
+
+TrkMessage::TrkMessage(unsigned char c,
+                        unsigned char t,
+                        Callback cb) :
+    code(c),
+    token(t),
+    callback(cb),
+    invokeOnNAK(false)
+{
+}
+
+// ------- TrkWriteQueueDevice
+
+struct TrkWriteQueueDevicePrivate {
+    typedef QMap<unsigned char, TrkMessage> TokenMessageMap;
+    TrkWriteQueueDevicePrivate();
+
+    unsigned char trkWriteToken;
+    QQueue<TrkMessage> trkWriteQueue;
+    TokenMessageMap writtenTrkMessages;
+    bool trkWriteBusy;
+};
+
+TrkWriteQueueDevicePrivate::TrkWriteQueueDevicePrivate() :
+    trkWriteToken(0),
+    trkWriteBusy(false)
+{
+}
+
+TrkWriteQueueDevice::TrkWriteQueueDevice(QObject *parent) :
+    TrkDevice(parent),
+    qd(new TrkWriteQueueDevicePrivate)
+{
+    connect(this, SIGNAL(messageReceived(TrkResult)), this, SLOT(slotHandleResult(TrkResult)));
+}
+
+TrkWriteQueueDevice::~TrkWriteQueueDevice()
+{
+    delete qd;
+}
+
+unsigned char TrkWriteQueueDevice::nextTrkWriteToken()
+{
+    ++qd->trkWriteToken;
+    if (qd->trkWriteToken == 0)
+        ++qd->trkWriteToken;
+    return qd->trkWriteToken;
+}
+
+void TrkWriteQueueDevice::sendTrkMessage(unsigned char code, Callback callback,
+                                         const QByteArray &data, const QVariant &cookie,
+                                         bool invokeOnNAK)
+{
+    TrkMessage msg(code, nextTrkWriteToken(), callback);
+    msg.data = data;
+    msg.cookie = cookie;
+    msg.invokeOnNAK = invokeOnNAK;
+    queueTrkMessage(msg);
+}
+
+void TrkWriteQueueDevice::sendTrkInitialPing()
+{
+    const TrkMessage msg(0, 0); // Ping, reset sequence count
+    queueTrkMessage(msg);
+}
+
+bool TrkWriteQueueDevice::sendTrkAck(unsigned char token)
+{
+    TrkMessage msg(0x80, token);
+    msg.token = token;
+    msg.data.append('\0');
+    // The acknowledgement must not be queued!
+    return trkWriteRawMessage(msg);
+    // 01 90 00 07 7e 80 01 00 7d 5e 7e
+}
+
+void TrkWriteQueueDevice::queueTrkMessage(const TrkMessage &msg)
+{
+    qd->trkWriteQueue.append(msg);
+}
+
+void TrkWriteQueueDevice::tryTrkWrite()
+{
+    // Invoked from timer, try to flush out message queue
+    if (qd->trkWriteBusy)
+        return;
+    if (qd->trkWriteQueue.isEmpty())
+        return;
+
+    const TrkMessage msg = qd->trkWriteQueue.dequeue();
+    trkWrite(msg);
+}
+
+bool TrkWriteQueueDevice::trkWriteRawMessage(const TrkMessage &msg)
+{
+    const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, serialFrame());
+    if (verbose())
+         qDebug() << ("WRITE: " + stringFromArray(ba));
+    QString errorMessage;
+    const bool rc = write(ba, &errorMessage);
+    if (!rc)
+        emitError(errorMessage);
+    return rc;
+}
+
+bool TrkWriteQueueDevice::trkWrite(const TrkMessage &msg)
+{
+    qd->writtenTrkMessages.insert(msg.token, msg);
+    qd->trkWriteBusy = true;
+    return trkWriteRawMessage(msg);
+}
+
+void TrkWriteQueueDevice::timerEvent(QTimerEvent *ev)
+{
+    tryTrkWrite();
+    TrkDevice::timerEvent(ev);
+}
+
+void TrkWriteQueueDevice::slotHandleResult(const TrkResult &result)
+{
+    qd->trkWriteBusy = false;
+    if (result.code != TrkNotifyAck && result.code != TrkNotifyNak)
+        return;
+    // Find which request the message belongs to and invoke callback
+    // if ACK or on NAK if desired.
+    const TrkWriteQueueDevicePrivate::TokenMessageMap::iterator it = qd->writtenTrkMessages.find(result.token);
+    if (it == qd->writtenTrkMessages.end())
+        return;
+    const bool invokeCB = it.value().callback
+                          && (result.code == TrkNotifyAck || it.value().invokeOnNAK);
+
+    if (invokeCB) {
+        TrkResult result1 = result;
+        result1.cookie = it.value().cookie;
+        it.value().callback(result1);
+    }
+    qd->writtenTrkMessages.erase(it);
+}
+
+} // namespace tr
diff --git a/tests/manual/trk/trkdevice.h b/tests/manual/trk/trkdevice.h
index 7392c8a62c3..53047bfb899 100644
--- a/tests/manual/trk/trkdevice.h
+++ b/tests/manual/trk/trkdevice.h
@@ -30,6 +30,8 @@
 #ifndef TRKDEVICE_H
 #define TRKDEVICE_H
 
+#include "trkfunctor.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QVariant>
 #include <QtCore/QByteArray>
@@ -37,7 +39,9 @@
 namespace trk {
 
 struct TrkResult;
+struct TrkMessage;
 struct TrkDevicePrivate;
+struct TrkWriteQueueDevicePrivate;
 
 /* TrkDevice: Implements a Windows COM or Linux device for
  * Trk communications. Provides synchronous write and asynchronous
@@ -71,14 +75,62 @@ public:
 
 signals:
     void messageReceived(const TrkResult&);
+    void error(const QString &s);
+
+protected:
+    void emitError(const QString &);
+    virtual void timerEvent(QTimerEvent *ev);
 
 private:
     void tryTrkRead();
-    virtual void timerEvent(QTimerEvent *ev);
 
     TrkDevicePrivate *d;
 };
 
+/* TrkWriteQueueDevice: Extends TrkDevice by write message queue allowing
+ * for queueing messages with a notification callback. If the message receives
+ * an ACK, the callback is invoked. */
+class TrkWriteQueueDevice : public TrkDevice
+{
+    Q_OBJECT
+public:
+    explicit TrkWriteQueueDevice(QObject *parent = 0);
+    virtual ~TrkWriteQueueDevice();
+
+    // Construct as 'TrkWriteQueueDevice::Callback(instance, &Klass::method);'
+    typedef TrkFunctor1<const TrkResult &> Callback;
+
+    // Enqueue a message with a notification callback.
+    void sendTrkMessage(unsigned char code,
+                        Callback callBack = Callback(),
+                        const QByteArray &data = QByteArray(),
+                        const QVariant &cookie = QVariant(),
+                        // Invoke callback on receiving NAK, too.
+                        bool invokeOnNAK = false);
+
+    // Enqeue an initial ping
+    void sendTrkInitialPing();
+
+    // Send an Ack synchronously, bypassing the queue
+    bool sendTrkAck(unsigned char token);
+
+private slots:
+    void slotHandleResult(const TrkResult &);
+
+protected:
+    virtual void timerEvent(QTimerEvent *ev);
+
+private:
+    unsigned char nextTrkWriteToken();
+    void queueTrkMessage(const TrkMessage &msg);
+    void tryTrkWrite();
+    bool trkWriteRawMessage(const TrkMessage &msg);
+    bool trkWrite(const TrkMessage &msg);
+
+    TrkWriteQueueDevicePrivate *qd;
+};
+
+
 } // namespace trk
 
 #endif // TRKDEVICE_H
diff --git a/tests/manual/trk/trkfunctor.h b/tests/manual/trk/trkfunctor.h
new file mode 100644
index 00000000000..69a67e97a61
--- /dev/null
+++ b/tests/manual/trk/trkfunctor.h
@@ -0,0 +1,144 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used 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.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef _TRK_FUNCTOR_H_
+#define _TRK_FUNCTOR_H_
+
+#include <QtGlobal>
+
+namespace trk {
+namespace Internal {
+/* Helper class for the 1-argument functor:
+ * Cloneable base class for the implementation which is
+ * invokeable with the argument. */
+template <class Argument>
+class TrkFunctor1ImplBase {
+    Q_DISABLE_COPY(TrkFunctor1ImplBase)
+public:
+    TrkFunctor1ImplBase() {}
+    virtual TrkFunctor1ImplBase *clone() const = 0;
+    virtual void invoke(Argument a) = 0;
+    virtual ~TrkFunctor1ImplBase() {}
+};
+
+/* Helper class for the 1-argument functor: Implementation for
+ * a class instance with a member function pointer. */
+template <class Klass, class Argument>
+class TrkFunctor1MemberPtrImpl : public TrkFunctor1ImplBase<Argument> {
+public:
+    typedef void (Klass::*MemberFuncPtr)(Argument);
+
+    explicit TrkFunctor1MemberPtrImpl(Klass *instance,
+                                   MemberFuncPtr memberFunc) :
+                                   m_instance(instance),
+                                   m_memberFunc(memberFunc) {}
+
+    virtual TrkFunctor1ImplBase<Argument> *clone() const
+    {
+        return new TrkFunctor1MemberPtrImpl<Klass, Argument>(m_instance, m_memberFunc);
+    }
+
+    virtual void invoke(Argument a)
+        { (m_instance->*m_memberFunc)(a); }
+private:
+    Klass *m_instance;
+    MemberFuncPtr m_memberFunc;
+};
+
+} // namespace Internal
+
+/* Default-constructible, copyable 1-argument functor providing an
+ * operator()(Argument) that invokes a member function of a class:
+ * \code
+class Foo {
+public:
+    void print(const std::string &);
+};
+...
+Foo foo;
+TrkFunctor1<const std::string &> f1(&foo, &Foo::print);
+f1("test");
+\endcode */
+
+template <class Argument>
+class TrkFunctor1 {
+public:
+    TrkFunctor1() : m_impl(0) {}
+
+    template <class Klass>
+        TrkFunctor1(Klass *instance,
+                 void (Klass::*memberFunc)(Argument)) :
+        m_impl(new Internal::TrkFunctor1MemberPtrImpl<Klass,Argument>(instance, memberFunc)) {}
+
+    ~TrkFunctor1()
+    {
+        clean();
+    }
+
+    TrkFunctor1(const TrkFunctor1 &rhs) :
+        m_impl(0)
+    {
+        if (rhs.m_impl)
+            m_impl = rhs.m_impl->clone();
+    }
+
+    TrkFunctor1 &operator=(const TrkFunctor1 &rhs)
+    {
+        if (this != &rhs) {
+            clean();
+            if (rhs.m_impl)
+                m_impl = rhs.m_impl->clone();
+        }
+        return *this;
+    }
+
+    inline bool isNull() const { return m_impl == 0; }
+    operator bool() const { return !isNull(); }
+
+    void operator()(Argument a)
+    {
+        if (m_impl)
+            m_impl->invoke(a);
+    }
+
+private:
+    void clean()
+    {
+        if (m_impl) {
+            delete m_impl;
+            m_impl = 0;
+        }
+    }
+
+    Internal::TrkFunctor1ImplBase<Argument> *m_impl;
+};
+
+} // namespace trk
+
+#endif // _TRK_FUNCTOR_H_
diff --git a/tests/manual/trk/trklauncher.pri b/tests/manual/trk/trklauncher.pri
index 8824c4a5c40..3593888b861 100644
--- a/tests/manual/trk/trklauncher.pri
+++ b/tests/manual/trk/trklauncher.pri
@@ -4,5 +4,6 @@ SOURCES += $$PWD/launcher.cpp \
     $$PWD/trkutils.cpp \
     $$PWD/trkdevice.cpp
 HEADERS += $$PWD/trkutils.h \
+    $$PWD/trkfunctor.h \
     $$PWD/trkdevice.h \
     $$PWD/launcher.h
-- 
GitLab