From b064262ff665e0d690aa70f43a9c2665e66d2128 Mon Sep 17 00:00:00 2001 From: APTX Date: Sun, 10 Oct 2010 23:20:49 +0200 Subject: [PATCH] Rewrite handling of command timeouts. --- abstractcommand.cpp | 21 +++++++++ abstractcommand.h | 15 ++++++- client.cpp | 105 +++++++++++++++++++++++++++++--------------- client.h | 24 ++-------- 4 files changed, 108 insertions(+), 57 deletions(-) diff --git a/abstractcommand.cpp b/abstractcommand.cpp index bf2d151..40f8229 100644 --- a/abstractcommand.cpp +++ b/abstractcommand.cpp @@ -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 diff --git a/abstractcommand.h b/abstractcommand.h index 62cad54..1a7ce59 100644 --- a/abstractcommand.h +++ b/abstractcommand.h @@ -6,7 +6,7 @@ #include #include #include - +#include namespace AniDBUdpClient { @@ -14,7 +14,7 @@ class Client; typedef QPair 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; diff --git a/client.cpp b/client.cpp index e55c025..39f0db2 100644 --- a/client.cpp +++ b/client.cpp @@ -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 diff --git a/client.h b/client.h index 1d197ff..2e0984f 100644 --- 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 commandQueue; QQueue controlCommandQueue; - QMap sentCommands; + QMap sentCommands; + QQueue 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 -- 2.52.0