]> Some of my projects - anidbudpclient.git/commitdiff
Rewrite handling of command timeouts.
authorAPTX <marek321@gmail.com>
Sun, 10 Oct 2010 21:20:49 +0000 (23:20 +0200)
committerAPTX <marek321@gmail.com>
Sun, 10 Oct 2010 21:20:49 +0000 (23:20 +0200)
abstractcommand.cpp
abstractcommand.h
client.cpp
client.h

index bf2d151bdbf04dc1182c74749cea38e0a4daa631..40f822945b1cebb9efbb728c7f46d31727559bf1 100644 (file)
@@ -33,6 +33,7 @@ AbstractReply::AbstractReply(const QByteArray &id, Client *client, QObject *pare
        m_id = id;
        m_client = client;
        m_commandPtr = 0;
+       m_controlCommand = false;
 }
 
 AbstractReply::~AbstractReply()
@@ -66,9 +67,29 @@ QByteArray AbstractReply::id() const
        return m_id;
 }
 
+QDateTime AbstractReply::timeSent() const
+{
+       return m_timeSent;
+}
+
+bool AbstractReply::controlCommand() const
+{
+       return m_controlCommand;
+}
+
 void AbstractReply::setId(const QByteArray &id)
 {
        m_id = id;
 }
 
+void AbstractReply::setTimeSent(const QDateTime &timeSent)
+{
+       m_timeSent = timeSent;
+}
+
+void AbstractReply::setControlCommand(bool controlCommand)
+{
+       m_controlCommand = controlCommand;
+}
+
 } // namespace AniDBUdpClient
index 62cad5412335838bc12f985c9e41db49f9774e74..1a7ce59a4e927dece07a8c60ef329987868b2266 100644 (file)
@@ -6,7 +6,7 @@
 #include <QObject>
 #include <QPair>
 #include <QVariantMap>
-
+#include <QDateTime>
 
 namespace AniDBUdpClient {
 
@@ -14,7 +14,7 @@ class Client;
 
 typedef QPair<QString, QVariantMap> Command;
 
-class AbstractReply;
+class ANIDBUDPCLIENTSHARED_EXPORT AbstractReply;
 
 class ANIDBUDPCLIENTSHARED_EXPORT AbstractCommand
 {
@@ -57,6 +57,8 @@ class ANIDBUDPCLIENTSHARED_EXPORT AbstractReply : public QObject
 
        Q_PROPERTY(QByteArray id READ id);
        Q_PROPERTY(ReplyCode replyCode READ replyCode);
+       Q_PROPERTY(QDateTime timeSent READ timeSent);
+       Q_PROPERTY(bool controlCommand READ controlCommand);
 
 public:
        typedef AbstractCommand CommandType;
@@ -72,15 +74,24 @@ public:
        virtual ReplyCode replyCode() const;
        virtual QByteArray id() const;
 
+       QDateTime timeSent() const;
+
+       bool controlCommand() const;
+
 signals:
        void replyReady(bool success = false);
 
 protected:
        void setId(const QByteArray &id);
+       void setTimeSent(const QDateTime &timeSent = QDateTime::currentDateTime());
+       void setControlCommand(bool controlCommand);
 
        QString m_rawReply;
        ReplyCode m_replyCode;
        QByteArray m_id;
+       QDateTime m_timeSent;
+       bool m_controlCommand;
+
        Client *m_client;
 
        AbstractCommand *m_commandPtr;
index e55c025201aeb5f1bec9544b2df02886e6c10b27..39f0db22dc344204de8c80a22ab9f4c6337513ff 100644 (file)
@@ -33,6 +33,7 @@ qDebug() << "Api instance init!";
        m_idlePolicy = DoNothingIdlePolicy;
 
        disconnecting = false;
+       authenticatingStarted = false;
        commandsTimedOut = 0;
 
        socket = new QUdpSocket(this);
@@ -43,6 +44,10 @@ qDebug() << "Api instance init!";
        idleTimer = new QTimer(this);
        idleTimer->setSingleShot(true);
 
+       replyTimeoutTimer = new QTimer(this);
+       replyTimeoutTimer->setSingleShot(true);
+       QObject::connect(replyTimeoutTimer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
+
        m_localPort = 9001;
        m_host = "api.anidb.info";
        m_hostPort = 9000;
@@ -131,10 +136,10 @@ qDebug() << "Api instance init!";
 
 Client::~Client()
 {
-       foreach (CommandData *cmd, sentCommands)
+       foreach (AbstractReply *cmd, sentCommands)
        {
                // Send CLIENT_DESTROYED to indicate that no real reply will come.
-               cmd->command->setRawReply(CLIENT_DESTROYED, "");
+               cmd->setRawReply(CLIENT_DESTROYED, "");
        }
        sentCommands.clear();
 
@@ -318,6 +323,10 @@ qDebug() << "Entering Connected State";
 void Client::enterAuthenticatingState()
 {
 qDebug() << "Entering Authenticating State";
+       if (authenticatingStarted)
+               return;
+
+       authenticatingStarted = true;
 
        if (m_sessionId.isEmpty())
        {
@@ -333,6 +342,7 @@ qDebug() << "Entering Authenticating State";
 void Client::doAuthenticate(bool success)
 {
 qDebug() << "doAuthenticate init";
+       authenticatingStarted = false;
        if (success)
        {
 qDebug() << "success!";
@@ -533,10 +543,35 @@ qDebug() << QString("Sending reply to command with id: %1")
                        replyCode = ReplyCode(replyCodeInt);
                }
 
-               CommandData *commandData = sentCommands.take(commandId);
-               AbstractReply *cmd = commandData->command;
-               bool controlCommand = commandData->controlCommand;
-               delete commandData;
+               AbstractReply *cmd = sentCommands.take(commandId);
+
+               if (sentCommandOrder.head() == commandId)
+               {
+                       sentCommandOrder.dequeue();
+
+                       if (sentCommandOrder.isEmpty())
+                       {
+                               replyTimeoutTimer->stop();
+                       }
+                       else
+                       {
+                               // If we got the first command sent, we update the replyTimeoutTimer to count the remaining time untill timeout.
+                               // This should be less than Client::UDP_API_COMMAND_TIMEOUT, but never negative.
+                               int newTimeout = qBound(0,  Client::UDP_API_COMMAND_TIMEOUT - sentCommands[commandId]->timeSent().secsTo(QDateTime::currentDateTime()), Client::UDP_API_COMMAND_TIMEOUT);
+                               newTimeout *= 1000;
+                               replyTimeoutTimer->start(newTimeout);
+qDebug() << "Starting replyTimeoutTimer" << newTimeout;
+                       }
+               }
+               else
+               {
+                       // If the reply is not for the first command sent,
+                       // then there is no need to change the replyTimeoutTimer
+                       // NOTE: even though this is linear search, the command should always be among the first few.
+                       sentCommandOrder.removeOne(commandId);
+               }
+
+               bool controlCommand = cmd->controlCommand();
 
                // Requeue command and reauthenticate if not logged in.
                switch (replyCode)
@@ -722,23 +757,29 @@ void Client::logout(bool force)
        enqueueControlCommand(createReply(LogoutCommand()), force);
 }
 
-void Client::commandTimeout(const QByteArray &commandId)
+void Client::commandTimeout()
 {
+       Q_ASSERT(!sentCommandOrder.isEmpty());
+
+       QByteArray commandId = sentCommandOrder.dequeue();
 qDebug() << commandId << "timed out";
-       if (!sentCommands.contains(commandId))
-               return;
 
-       CommandData *cmd = sentCommands.take(commandId);
+       if (!sentCommandOrder.isEmpty())
+       {
+               int newTimeout = qBound(0,  Client::UDP_API_COMMAND_TIMEOUT - sentCommands[commandId]->timeSent().secsTo(QDateTime::currentDateTime()), Client::UDP_API_COMMAND_TIMEOUT);
+               newTimeout *= 1000;
+               replyTimeoutTimer->start(newTimeout);
+qDebug() << "Starting replyTimeoutTimer" << newTimeout;
+       }
+       // If it's empty thereis no need to start replyTimeoutTimer again, as there are no commands waiting.
 
-       // Regenerate ID.
-       cmd->command->setId(nextCommandId());
+       AbstractReply *cmd = sentCommands.take(commandId);
+       cmd->setId(nextCommandId());
 
-       if (cmd->controlCommand)
-               enqueueControlCommand(cmd->command);
+       if (cmd->controlCommand())
+               enqueueControlCommand(cmd);
        else
-               enqueueCommand(cmd->command);
-
-       delete cmd;
+               enqueueCommand(cmd);
 
        ++commandsTimedOut;
 
@@ -796,10 +837,19 @@ void Client::sendCommand(AbstractReply *command, bool controlCommand)
        if (m_sessionId.length())
                datagram += "&s=" + m_sessionId;
 
-       CommandData *cmd = new CommandData(command, commandId, controlCommand);
-       QObject::connect(cmd, SIGNAL(timeout(QByteArray)), this, SLOT(commandTimeout(QByteArray)));
 
-       sentCommands[commandId] = cmd;
+       command->setControlCommand(controlCommand);
+       command->setTimeSent();
+
+       sentCommands[commandId] = command;
+       sentCommandOrder.enqueue(commandId);
+
+       if (!replyTimeoutTimer->isActive())
+       {
+               replyTimeoutTimer->start(Client::UDP_API_COMMAND_TIMEOUT * 1000);
+qDebug() << "Starting replyTimeoutTimer" << replyTimeoutTimer->interval();
+       }
+
 
 qDebug() << QString("SENDING datagram:\n\t%1\nto: %2 ([%3]:%4)")
                .arg(datagram.constData())
@@ -861,19 +911,4 @@ qDebug() << QString("Generated id %1").arg(result.constData());
 
 Client *Client::m_instance = 0;
 
-CommandData::CommandData(AbstractReply *command, const QByteArray &commandId, bool controlCommand) : QObject()
-{
-       this->command = command;
-       this->controlCommand = controlCommand;
-       this->commandId = commandId;
-
-       connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout()));
-       timer.start(Client::UDP_API_COMMAND_TIMEOUT * 1000);
-}
-
-void CommandData::timerTimeout()
-{
-       emit timeout(commandId);
-}
-
 } // namespace AniDBUdpClient
index 1d197ffb4a23cfba4c70d3ee6a0f2cefba788e4b..2e0984f7da07f2576c27245c2bc28dd13e4e140e 100644 (file)
--- a/client.h
+++ b/client.h
@@ -164,7 +164,7 @@ private slots:
 
        void logout();
 
-       void commandTimeout(const QByteArray &commandId);
+       void commandTimeout();
 
        void removeDeletedFromQueue(QObject *object);
 
@@ -184,7 +184,8 @@ private:
 
        QQueue<AbstractReply *> commandQueue;
        QQueue<AbstractReply *> controlCommandQueue;
-       QMap<QByteArray, CommandData *> sentCommands;
+       QMap<QByteArray, AbstractReply *> sentCommands;
+       QQueue<QByteArray> sentCommandOrder;
        QUdpSocket *socket;
 
 
@@ -205,6 +206,7 @@ private:
        QString m_errorString;
 
        bool disconnecting;
+       bool authenticatingStarted;
 
        int commandsTimedOut;
 
@@ -240,24 +242,6 @@ private:
        QHistoryState *connectedHistoryState;
 };
 
-class CommandData : QObject
-{
-       friend class Client;
-
-       Q_OBJECT
-public:
-       AbstractReply *command;
-       bool controlCommand;
-       QByteArray commandId;
-       QTimer timer;
-
-       CommandData(AbstractReply *command, const QByteArray &commandId, bool controlCommand = false);
-signals:
-       void timeout(QByteArray commandId);
-private slots:
-       void timerTimeout();
-};
-
 } // namespace AniDBUdpClient
 
 #include <QScriptEngine>