Changeset 14732


Ignore:
Timestamp:
Apr 19, 2016, 8:41:59 PM (5 years ago)
Author:
mikedld
Message:

Refactor RPC requests code for proper queueing (patch by intelfx @ GH-10)

This refactoring is driven by the need to be able to do true queued RPC calls
(where each successive call uses the result of the previous).

Currently, such queueing of requests is done by assigning them special "magic"
tag numbers, which are then intercepted in one big switch() statement and acted
upon. This (aside from making code greatly unclear) effectively makes each such
queue a singleton, because state passing is restricted to global variables.

We refactor RpcClient? to assign an unique tag to each remote call, and then
abstract all the call<->response matching with Qt's future/promise mechanism.

Finally, we introduce a "RPC request queue" class (RpcQueue?) which is built on
top of QFutureWatcher and C++11's <functional> library. This class maintains
a queue of functions, where each function receives an RPC response, does
necessary processing, performs another call and finally returns its future.

Location:
trunk/qt
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/qt/AddData.cc

    r14609 r14732  
    11/*
    2  * This file Copyright (C) 2012-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    116116  return ret;
    117117}
     118
     119QString
     120AddData::readableShortName () const
     121{
     122  switch (type)
     123    {
     124      case FILENAME:
     125        return QFileInfo (filename).fileName ();
     126
     127      case URL:
     128        return url.path ().split (QLatin1Char ('/')).last ();
     129
     130      default:
     131        return readableName ();
     132    }
     133}
  • trunk/qt/AddData.h

    r14724 r14732  
    11/*
    2  * This file Copyright (C) 2012-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2012-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    3434    QByteArray toBase64 () const;
    3535    QString readableName () const;
     36    QString readableShortName () const;
    3637
    3738    static bool isSupported (const QString& str) { return AddData (str).type != NONE; }
  • trunk/qt/CMakeLists.txt

    r14727 r14732  
    5555    RelocateDialog.cc
    5656    RpcClient.cc
     57    RpcQueue.cc
    5758    Session.cc
    5859    SessionDialog.cc
     
    113114    RelocateDialog.h
    114115    RpcClient.h
     116    RpcQueue.h
    115117    Session.h
    116118    SessionDialog.h
  • trunk/qt/FreeSpaceLabel.cc

    r14537 r14732  
    11/*
    2  * This file Copyright (C) 2013-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2013-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1515#include "Formatter.h"
    1616#include "FreeSpaceLabel.h"
     17#include "RpcQueue.h"
    1718#include "Session.h"
    1819
     
    2526  QLabel (parent),
    2627  mySession (nullptr),
    27   myTag (-1),
    2828  myTimer (this)
    2929{
     
    4040    return;
    4141
    42   if (mySession != nullptr)
    43     disconnect (mySession, nullptr, this, nullptr);
    44 
    4542  mySession = &session;
    46 
    47   connect (mySession, SIGNAL (executed (int64_t, QString, tr_variant *)),
    48            this,      SLOT (onSessionExecuted (int64_t, QString, tr_variant *)));
    49 
    5043  onTimer ();
    5144}
     
    7467  tr_variantDictAddStr (&args, TR_KEY_path, myPath.toUtf8 ().constData());
    7568
    76   myTag = mySession->getUniqueTag ();
    77   mySession->exec ("free-space", &args, myTag);
     69  RpcQueue * q = new RpcQueue ();
     70
     71  q->add (
     72    [this, &args] ()
     73    {
     74      return mySession->exec ("free-space", &args);
     75    });
     76
     77  q->add (
     78    [this] (const RpcResponse& r)
     79    {
     80      QString str;
     81
     82      // update the label
     83      int64_t bytes = -1;
     84      if (tr_variantDictFindInt (r.args.get (), TR_KEY_size_bytes, &bytes) && bytes >= 0)
     85        setText (tr ("%1 free").arg (Formatter::sizeToString (bytes)));
     86      else
     87        setText (QString ());
     88
     89      // update the tooltip
     90      size_t len = 0;
     91      const char * path = 0;
     92      tr_variantDictFindStr (r.args.get (), TR_KEY_path, &path, &len);
     93      str = QString::fromUtf8 (path, len);
     94      setToolTip (QDir::toNativeSeparators (str));
     95
     96      myTimer.start ();
     97    });
     98
     99  q->run ();
    78100}
    79 
    80 void
    81 FreeSpaceLabel::onSessionExecuted (int64_t tag, const QString& result, tr_variant * arguments)
    82 {
    83   Q_UNUSED (result);
    84 
    85   if (tag != myTag)
    86     return;
    87 
    88   QString str;
    89 
    90   // update the label
    91   int64_t bytes = -1;
    92   if (tr_variantDictFindInt (arguments, TR_KEY_size_bytes, &bytes) && bytes >= 0)
    93     setText (tr("%1 free").arg(Formatter::sizeToString (bytes)));
    94   else
    95     setText (QString ());
    96 
    97   // update the tooltip
    98   size_t len = 0;
    99   const char * path = 0;
    100   tr_variantDictFindStr (arguments, TR_KEY_path, &path, &len);
    101   str = QString::fromUtf8 (path, len);
    102   setToolTip (QDir::toNativeSeparators (str));
    103 
    104   myTimer.start ();
    105 }
  • trunk/qt/FreeSpaceLabel.h

    r14724 r14732  
    11/*
    2  * This file Copyright (C) 2013-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2013-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    99
    1010#pragma once
    11 
    12 #include <cstdint>
    1311
    1412#include <QLabel>
     
    3533
    3634  private slots:
    37     void onSessionExecuted (int64_t tag, const QString& result, tr_variant * arguments);
    3835    void onTimer ();
    3936
    4037  private:
    4138    Session * mySession;
    42     int64_t myTag;
    4339    QString myPath;
    4440    QTimer myTimer;
  • trunk/qt/MainWindow.cc

    r14719 r14732  
    11/*
    2  * This file Copyright (C) 2009-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    286286  connect (&mySession, SIGNAL (dataSendProgress ()), this, SLOT (dataSendProgress ()));
    287287  connect (&mySession, SIGNAL (httpAuthenticationRequired ()), this, SLOT (wrongAuthentication ()));
    288   connect (&mySession, SIGNAL (error (QNetworkReply::NetworkError)), this, SLOT (onError (QNetworkReply::NetworkError)));
    289   connect (&mySession, SIGNAL (errorMessage (QString)), this, SLOT (errorMessage(QString)));
     288  connect (&mySession, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)), this, SLOT (onNetworkResponse (QNetworkReply::NetworkError, QString)));
    290289
    291290  if (mySession.isServer ())
     
    13771376
    13781377void
    1379 MainWindow::onError (QNetworkReply::NetworkError code)
     1378MainWindow::onNetworkResponse (QNetworkReply::NetworkError code, const QString& message)
    13801379{
    13811380  const bool hadError = myNetworkError;
     
    13841383
    13851384  myNetworkError = haveError;
     1385  myErrorMessage = message;
    13861386  refreshTrayIconSoon();
    13871387  updateNetworkIcon();
     
    13911391  if (hadError && !haveError)
    13921392    myModel.clear();
    1393 }
    1394 
    1395 void
    1396 MainWindow::errorMessage (const QString& msg)
    1397 {
    1398     myErrorMessage = msg;
    13991393}
    14001394
  • trunk/qt/MainWindow.h

    r14724 r14732  
    11/*
    2  * This file Copyright (C) 2009-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    129129    void dataReadProgress ();
    130130    void dataSendProgress ();
    131     void onError (QNetworkReply::NetworkError);
    132     void errorMessage (const QString&);
     131    void onNetworkResponse (QNetworkReply::NetworkError code, const QString& message);
    133132    void toggleWindows (bool doShow);
    134133    void onSetPrefs ();
  • trunk/qt/RpcClient.cc

    r14634 r14732  
    11/*
    2  * This file Copyright (C) 2014-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2014-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    88 */
    99
     10#include <cstring>
    1011#include <iostream>
    1112
     
    2829
    2930#define REQUEST_DATA_PROPERTY_KEY "requestData"
     31#define REQUEST_FUTUREINTERFACE_PROPERTY_KEY "requestReplyFutureInterface"
    3032
    3133namespace
     
    4850  QObject (parent),
    4951  mySession (nullptr),
    50   myNAM (nullptr)
     52  myNAM (nullptr),
     53  myNextTag (0)
    5154{
    5255  qRegisterMetaType<TrVariantPtr> ("TrVariantPtr");
    53 
    54   connect (this, SIGNAL (responseReceived (TrVariantPtr)),
    55            this, SLOT (parseResponse (TrVariantPtr)));
    5656}
    5757
     
    106106}
    107107
    108 void
    109 RpcClient::exec (tr_quark method, tr_variant * args, int64_t tag)
    110 {
    111   exec (tr_quark_get_string (method, nullptr), args, tag);
    112 }
    113 
    114 void
    115 RpcClient::exec (const char* method, tr_variant * args, int64_t tag)
     108RpcResponseFuture
     109RpcClient::exec (tr_quark method, tr_variant * args)
     110{
     111  return exec (tr_quark_get_string (method, nullptr), args);
     112}
     113
     114RpcResponseFuture
     115RpcClient::exec (const char * method, tr_variant * args)
    116116{
    117117  TrVariantPtr json = createVariant ();
    118118  tr_variantInitDict (json.get (), 3);
    119119  tr_variantDictAddStr (json.get (), TR_KEY_method, method);
    120   if (tag >= 0)
    121     tr_variantDictAddInt (json.get (), TR_KEY_tag, tag);
    122120  if (args != nullptr)
    123121    tr_variantDictSteal (json.get (), TR_KEY_arguments, args);
    124122
    125   sendRequest (json);
    126 }
    127 
    128 void
     123  return sendRequest (json);
     124}
     125
     126int64_t
     127RpcClient::getNextTag ()
     128{
     129  return myNextTag++;
     130}
     131
     132void
     133RpcClient::sendNetworkRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise)
     134{
     135  QNetworkRequest request;
     136  request.setUrl (myUrl);
     137  request.setRawHeader ("User-Agent", (qApp->applicationName () + QLatin1Char ('/') + QString::fromUtf8 (LONG_VERSION_STRING)).toUtf8 ());
     138  request.setRawHeader ("Content-Type", "application/json; charset=UTF-8");
     139
     140  if (!mySessionId.isEmpty ())
     141    request.setRawHeader (TR_RPC_SESSION_ID_HEADER, mySessionId.toUtf8 ());
     142
     143  size_t rawJsonDataLength;
     144  char * rawJsonData = tr_variantToStr (json.get (), TR_VARIANT_FMT_JSON_LEAN, &rawJsonDataLength);
     145  QByteArray jsonData (rawJsonData, rawJsonDataLength);
     146  tr_free (rawJsonData);
     147
     148  QNetworkReply * reply = networkAccessManager ()->post (request, jsonData);
     149  reply->setProperty (REQUEST_DATA_PROPERTY_KEY, QVariant::fromValue (json));
     150  reply->setProperty (REQUEST_FUTUREINTERFACE_PROPERTY_KEY, QVariant::fromValue (promise));
     151
     152  connect (reply, SIGNAL (downloadProgress (qint64, qint64)), this, SIGNAL (dataReadProgress ()));
     153  connect (reply, SIGNAL (uploadProgress (qint64, qint64)), this, SIGNAL (dataSendProgress ()));
     154
     155#ifdef DEBUG_HTTP
     156  std::cerr << "sending " << "POST " << qPrintable (myUrl.path ()) << std::endl;
     157  for (const QByteArray& b: request.rawHeaderList ())
     158    std::cerr << b.constData ()
     159              << ": "
     160              << request.rawHeader (b).constData ()
     161              << std::endl;
     162  std::cerr << "Body:\n" << jsonData.constData () << std::endl;
     163#endif
     164}
     165
     166void
     167RpcClient::sendLocalRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise, int64_t tag)
     168{
     169  myLocalRequests.insert (tag, promise);
     170  tr_rpc_request_exec_json (mySession, json.get (), localSessionCallback, this);
     171}
     172
     173RpcResponseFuture
    129174RpcClient::sendRequest (TrVariantPtr json)
    130175{
     176  int64_t tag = getNextTag ();
     177  tr_variantDictAddInt (json.get (), TR_KEY_tag, tag);
     178
     179  QFutureInterface<RpcResponse> promise;
     180  promise.setExpectedResultCount (1);
     181  promise.setProgressRange (0, 1);
     182  promise.setProgressValue (0);
     183  promise.reportStarted ();
     184
    131185  if (mySession != nullptr)
    132     {
    133       tr_rpc_request_exec_json (mySession, json.get (), localSessionCallback, this);
    134     }
     186    sendLocalRequest (json, promise, tag);
    135187  else if (!myUrl.isEmpty ())
    136     {
    137       QNetworkRequest request;
    138       request.setUrl (myUrl);
    139       request.setRawHeader ("User-Agent", (qApp->applicationName () + QLatin1Char ('/') + QString::fromUtf8 (LONG_VERSION_STRING)).toUtf8 ());
    140       request.setRawHeader ("Content-Type", "application/json; charset=UTF-8");
    141 
    142       if (!mySessionId.isEmpty ())
    143         request.setRawHeader (TR_RPC_SESSION_ID_HEADER, mySessionId.toUtf8 ());
    144 
    145       size_t rawJsonDataLength;
    146       char * rawJsonData = tr_variantToStr (json.get (), TR_VARIANT_FMT_JSON_LEAN, &rawJsonDataLength);
    147       QByteArray jsonData (rawJsonData, rawJsonDataLength);
    148       tr_free (rawJsonData);
    149 
    150       QNetworkReply * reply = networkAccessManager ()->post (request, jsonData);
    151       reply->setProperty (REQUEST_DATA_PROPERTY_KEY, QVariant::fromValue (json));
    152       connect (reply, SIGNAL (downloadProgress (qint64, qint64)), this, SIGNAL (dataReadProgress ()));
    153       connect (reply, SIGNAL (uploadProgress (qint64, qint64)), this, SIGNAL (dataSendProgress ()));
    154       connect (reply, SIGNAL (error (QNetworkReply::NetworkError)), this, SIGNAL (error (QNetworkReply::NetworkError)));
    155 
    156 #ifdef DEBUG_HTTP
    157       std::cerr << "sending " << "POST " << qPrintable (myUrl.path ()) << std::endl;
    158       for (const QByteArray& b: request.rawHeaderList ())
    159         std::cerr << b.constData ()
    160                   << ": "
    161                   << request.rawHeader (b).constData ()
    162                   << std::endl;
    163       std::cerr << "Body:\n" << jsonData.constData () << std::endl;
    164 #endif
    165     }
     188    sendNetworkRequest (json, promise);
     189
     190  return promise.future ();
    166191}
    167192
     
    174199
    175200      connect (myNAM, SIGNAL (finished (QNetworkReply *)),
    176                this, SLOT (onFinished (QNetworkReply *)));
     201               this, SLOT (networkRequestFinished (QNetworkReply * )));
    177202
    178203      connect (myNAM, SIGNAL (authenticationRequired (QNetworkReply *,QAuthenticator *)),
     
    196221  // this callback is invoked in the libtransmission thread, so we don't want
    197222  // to process the response here... let's push it over to the Qt thread.
    198   self->responseReceived (json);
    199 }
    200 
    201 void
    202 RpcClient::onFinished (QNetworkReply * reply)
    203 {
     223  QMetaObject::invokeMethod (self, "localRequestFinished", Qt::QueuedConnection, Q_ARG (TrVariantPtr, json));
     224}
     225
     226void
     227RpcClient::networkRequestFinished (QNetworkReply *reply)
     228{
     229  reply->deleteLater ();
     230
     231  QFutureInterface<RpcResponse> promise = reply->property (REQUEST_FUTUREINTERFACE_PROPERTY_KEY).value<QFutureInterface<RpcResponse>> ();
     232
    204233#ifdef DEBUG_HTTP
    205234  std::cerr << "http response header: " << std::endl;
     
    218247      // update it and resubmit the request.
    219248      mySessionId = QString::fromUtf8 (reply->rawHeader (TR_RPC_SESSION_ID_HEADER));
    220       sendRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).value<TrVariantPtr> ());
    221     }
    222   else if (reply->error () != QNetworkReply::NoError)
    223     {
    224       emit errorMessage (reply->errorString ());
     249
     250      sendNetworkRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).value<TrVariantPtr> (), promise);
     251      return;
     252    }
     253
     254  emit networkResponse (reply->error(), reply->errorString());
     255
     256  if (reply->error () != QNetworkReply::NoError)
     257    {
     258      RpcResponse result;
     259      result.networkError = reply->error ();
     260
     261      promise.setProgressValueAndText (1, reply->errorString ());
     262      promise.reportFinished (&result);
    225263    }
    226264  else
    227265    {
     266      RpcResponse result;
     267
    228268      const QByteArray jsonData = reply->readAll ().trimmed ();
    229 
    230269      TrVariantPtr json = createVariant ();
    231270      if (tr_variantFromJson (json.get (), jsonData.constData (), jsonData.size ()) == 0)
    232         parseResponse (json);
    233 
    234       emit error (QNetworkReply::NoError);
    235     }
    236 
    237   reply->deleteLater ();
    238 }
    239 
    240 void
    241 RpcClient::parseResponse (TrVariantPtr json)
     271        result = parseResponseData (*json);
     272
     273      promise.setProgressValue (1);
     274      promise.reportFinished (&result);
     275    }
     276}
     277
     278void
     279RpcClient::localRequestFinished (TrVariantPtr response)
     280{
     281  int64_t tag = parseResponseTag (*response);
     282  RpcResponse result = parseResponseData (*response);
     283  QFutureInterface<RpcResponse> promise = myLocalRequests.take (tag);
     284
     285  promise.setProgressRange (0, 1);
     286  promise.setProgressValue (1);
     287  promise.reportFinished (&result);
     288}
     289
     290int64_t
     291RpcClient::parseResponseTag (tr_variant& json)
    242292{
    243293  int64_t tag;
    244   if (!tr_variantDictFindInt (json.get (), TR_KEY_tag, &tag))
     294
     295  if (!tr_variantDictFindInt (&json, TR_KEY_tag, &tag))
    245296    tag = -1;
    246297
     298  return tag;
     299}
     300
     301RpcResponse
     302RpcClient::parseResponseData (tr_variant& json)
     303{
     304  RpcResponse ret;
     305
    247306  const char * result;
    248   if (!tr_variantDictFindStr (json.get (), TR_KEY_result, &result, nullptr))
    249     result = nullptr;
     307  if (tr_variantDictFindStr (&json, TR_KEY_result, &result, nullptr))
     308    {
     309      ret.result = QString::fromUtf8 (result);
     310      ret.success = std::strcmp (result, "success") == 0;
     311    }
    250312
    251313  tr_variant * args;
    252   if (!tr_variantDictFindDict (json.get (), TR_KEY_arguments, &args))
    253     args = nullptr;
    254 
    255   emit executed (tag, result == nullptr ? QString () : QString::fromUtf8 (result), args);
    256 }
     314  if (tr_variantDictFindDict (&json, TR_KEY_arguments, &args))
     315    {
     316      ret.args = createVariant ();
     317      *ret.args = *args;
     318      tr_variantInitBool (args, false);
     319    }
     320
     321  return ret;
     322}
  • trunk/qt/RpcClient.h

    r14724 r14732  
    11/*
    2  * This file Copyright (C) 2014-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2014-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1212#include <memory>
    1313
     14#include <QFuture>
     15#include <QFutureInterface>
     16#include <QHash>
    1417#include <QNetworkReply>
    1518#include <QObject>
     
    3336}
    3437
     38struct RpcResponse
     39{
     40  QString result;
     41  TrVariantPtr args;
     42  bool success = false;
     43  QNetworkReply::NetworkError networkError = QNetworkReply::NoError;
     44};
     45
     46Q_DECLARE_METATYPE (QFutureInterface<RpcResponse>);
     47
     48// The response future -- the RPC engine returns one for each request made.
     49typedef QFuture<RpcResponse> RpcResponseFuture;
     50
    3551class RpcClient: public QObject
    3652{
     
    4864    const QUrl& url () const;
    4965
    50     void exec (tr_quark method, tr_variant * args, int64_t tag = -1);
    51     void exec (const char* method, tr_variant * args, int64_t tag = -1);
     66    RpcResponseFuture exec (tr_quark method, tr_variant * args);
     67    RpcResponseFuture exec (const char * method, tr_variant * args);
    5268
    5369  signals:
     
    5571    void dataReadProgress ();
    5672    void dataSendProgress ();
    57     void error (QNetworkReply::NetworkError code);
    58     void errorMessage (const QString& message);
    59     void executed (int64_t tag, const QString& result, tr_variant * args);
    60 
    61     // private
    62     void responseReceived (TrVariantPtr json);
     73    void networkResponse (QNetworkReply::NetworkError code, const QString& message);
    6374
    6475  private:
    65     void sendRequest (TrVariantPtr json);
     76    RpcResponseFuture sendRequest (TrVariantPtr json);
    6677    QNetworkAccessManager * networkAccessManager ();
     78    int64_t getNextTag ();
     79
     80    void sendNetworkRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise);
     81    void sendLocalRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise, int64_t tag);
     82    int64_t parseResponseTag (tr_variant& response);
     83    RpcResponse parseResponseData (tr_variant& response);
    6784
    6885    static void localSessionCallback (tr_session * s, tr_variant * response, void * vself);
    6986
    7087  private slots:
    71     void onFinished (QNetworkReply * reply);
    72     void parseResponse (TrVariantPtr json);
     88    void networkRequestFinished (QNetworkReply *reply);
     89    void localRequestFinished (TrVariantPtr response);
    7390
    7491  private:
     
    7794    QUrl myUrl;
    7895    QNetworkAccessManager * myNAM;
     96    QHash<int64_t, QFutureInterface<RpcResponse>> myLocalRequests;
     97    int64_t myNextTag;
    7998};
    8099
  • trunk/qt/Session.cc

    r14718 r14732  
    11/*
    2  * This file Copyright (C) 2009-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    2929#include "AddData.h"
    3030#include "Prefs.h"
     31#include "RpcQueue.h"
    3132#include "Session.h"
    3233#include "SessionDialog.h"
    3334#include "Torrent.h"
    3435#include "Utils.h"
    35 
    36 namespace
    37 {
    38   enum
    39   {
    40     TAG_SOME_TORRENTS,
    41     TAG_ALL_TORRENTS,
    42     TAG_SESSION_STATS,
    43     TAG_SESSION_INFO,
    44     TAG_BLOCKLIST_UPDATE,
    45     TAG_ADD_TORRENT,
    46     TAG_PORT_TEST,
    47     TAG_MAGNET_LINK,
    48     TAG_RENAME_PATH,
    49 
    50     FIRST_UNIQUE_TAG
    51   };
    52 }
    5336
    5437/***
     
    7053      tr_variantListAddQuark (list, key);
    7154  }
    72 }
    73 
    74 /***
    75 ****
    76 ***/
    77 
    78 void
    79 FileAdded::executed (int64_t tag, const QString& result, tr_variant * arguments)
    80 {
    81   if (tag != myTag)
    82     return;
    83 
    84   if (result == QLatin1String ("success"))
    85     {
    86       tr_variant * dup;
    87       const char * str;
    88       if (tr_variantDictFindDict (arguments, TR_KEY_torrent_duplicate, &dup) &&
    89           tr_variantDictFindStr (dup, TR_KEY_name, &str, NULL))
    90         {
    91           const QString myFilename = QFileInfo (myName).fileName ();
    92           const QString name = QString::fromUtf8 (str);
    93           QMessageBox::warning (qApp->activeWindow (),
    94                                 tr ("Add Torrent"),
    95                                 tr ("<p><b>Unable to add \"%1\".</b></p><p>It is a duplicate of \"%2\" which is already added.</p>").arg (myFilename).arg (name));
    96         }
    97 
    98       if (!myDelFile.isEmpty ())
    99         {
    100           QFile file (myDelFile);
    101           file.setPermissions (QFile::ReadOwner | QFile::WriteOwner);
    102           file.remove ();
    103         }
    104     }
    105   else
    106     {
    107       QString text = result;
    108 
    109       for (int i=0, n=text.size (); i<n; ++i)
    110         if (!i || text[i-1].isSpace ())
    111           text[i] = text[i].toUpper ();
    112 
    113       QMessageBox::warning (qApp->activeWindow (),
    114                             tr ("Error Adding Torrent"),
    115                             QString::fromLatin1 ("<p><b>%1</b></p><p>%2</p>").arg (text).arg (myName));
    116     }
    117 
    118   deleteLater ();
    119 }
    120 
    121 /***
    122 ****
    123 ***/
     55
     56  // If this object is passed as "ids" (compared by address), then recently active torrents are queried.
     57  const QSet<int> recentlyActiveIds = QSet<int>() << -1;
     58
     59  // If this object is passed as "ids" (compared by being empty), then all torrents are queried.
     60  const QSet<int> allIds;
     61}
    12462
    12563void
     
    13472      case QVariant::Double: tr_variantDictAddReal (&args, key, value.toDouble ()); break;
    13573      case QVariant::String: tr_variantDictAddStr (&args, key, value.toString ().toUtf8 ().constData ()); break;
    136       default:               assert ("unknown type");
     74      default:               assert (false);
    13775    }
    13876
     
    14381Session::portTest ()
    14482{
    145   exec ("port-test", nullptr, TAG_PORT_TEST);
     83  RpcQueue * q = new RpcQueue ();
     84
     85  q->add (
     86    [this] ()
     87    {
     88      return exec ("port-test", nullptr);
     89    });
     90
     91  q->add (
     92    [this] (const RpcResponse& r)
     93    {
     94      bool isOpen = false;
     95      if (r.success)
     96        tr_variantDictFindBool (r.args.get (), TR_KEY_port_is_open, &isOpen);
     97
     98      emit portTested (isOpen);
     99    });
     100
     101  q->run ();
    146102}
    147103
     
    154110  tr_variantListAddStr (tr_variantDictAddList (&args, TR_KEY_fields, 1), "magnetLink");
    155111
    156   exec (TR_KEY_torrent_get, &args, TAG_MAGNET_LINK);
     112  RpcQueue * q = new RpcQueue ();
     113
     114  q->add (
     115    [this, &args] ()
     116    {
     117      return exec (TR_KEY_torrent_get, &args);
     118    });
     119
     120  q->add (
     121    [this] (const RpcResponse& r)
     122    {
     123      tr_variant * torrents;
     124      tr_variant * child;
     125      const char * str;
     126
     127      if (tr_variantDictFindList (r.args.get (), TR_KEY_torrents, &torrents)
     128          && (child = tr_variantListChild (torrents, 0))
     129          && tr_variantDictFindStr (child, TR_KEY_magnetLink, &str, NULL))
     130        qApp->clipboard ()->setText (QString::fromUtf8 (str));
     131    });
     132
     133  q->run ();
    157134}
    158135
     
    278255  myConfigDir (configDir),
    279256  myPrefs (prefs),
    280   nextUniqueTag (FIRST_UNIQUE_TAG),
    281257  myBlocklistSize (-1),
    282258  mySession (0)
     
    291267
    292268  connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (updatePref (int)));
    293 
    294   connect (&myRpc, SIGNAL (executed (int64_t, QString, tr_variant *)), this, SLOT (responseReceived (int64_t, QString, tr_variant *)));
    295 
    296269  connect (&myRpc, SIGNAL (httpAuthenticationRequired ()), this, SIGNAL (httpAuthenticationRequired ()));
    297270  connect (&myRpc, SIGNAL (dataReadProgress ()), this, SIGNAL (dataReadProgress ()));
    298271  connect (&myRpc, SIGNAL (dataSendProgress ()), this, SIGNAL (dataSendProgress ()));
    299   connect (&myRpc, SIGNAL (error (QNetworkReply::NetworkError)), this, SIGNAL (error (QNetworkReply::NetworkError)));
    300   connect (&myRpc, SIGNAL (errorMessage (QString)), this, SIGNAL (errorMessage (QString)));
     272  connect (&myRpc, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)), this, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)));
    301273}
    302274
     
    388360  addOptionalIds (tr_variant * args, const QSet<int>& ids)
    389361  {
    390     if (!ids.isEmpty ())
     362    if (&ids == &recentlyActiveIds)
     363      {
     364        tr_variantDictAddStr (args, TR_KEY_ids, "recently-active");
     365      }
     366    else if (!ids.isEmpty ())
    391367      {
    392368        tr_variant * idList (tr_variantDictAddList (args, TR_KEY_ids, ids.size ()));
     
    440416    tr_variantListAddStr (list, str.toUtf8 ().constData ());
    441417
    442   exec(TR_KEY_torrent_set, &args);
     418  exec (TR_KEY_torrent_set, &args);
    443419}
    444420
     
    490466  tr_variantDictAddStr (&args, TR_KEY_name, newname.toUtf8 ().constData ());
    491467
    492   exec ("torrent-rename-path", &args, TAG_RENAME_PATH);
    493 }
    494 
    495 void
    496 Session::refreshTorrents (const QSet<int>& ids)
    497 {
    498   if (ids.empty ())
    499     {
    500       refreshAllTorrents ();
    501     }
    502   else
    503     {
    504       tr_variant args;
    505       tr_variantInitDict (&args, 2);
    506       addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
    507       addOptionalIds (&args, ids);
    508 
    509       exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
    510     }
     468  RpcQueue * q = new RpcQueue ();
     469
     470  q->add (
     471    [this, &args] ()
     472    {
     473      return exec ("torrent-rename-path", &args);
     474    },
     475    [this] (const RpcResponse& r)
     476    {
     477      const char * path = "(unknown)";
     478      const char * name = "(unknown)";
     479      tr_variantDictFindStr (r.args.get (), TR_KEY_path, &path, nullptr);
     480      tr_variantDictFindStr (r.args.get (), TR_KEY_name, &name, nullptr);
     481
     482      QMessageBox * d = new QMessageBox (QMessageBox::Information,
     483                                         tr ("Error Renaming Path"),
     484                                         tr ("<p><b>Unable to rename \"%1\" as \"%2\": %3.</b></p> "
     485                                             "<p>Please correct the errors and try again.</p>")
     486                                           .arg (QString::fromUtf8 (path))
     487                                           .arg (QString::fromUtf8 (name))
     488                                           .arg (r.result),
     489                                         QMessageBox::Close,
     490                                         qApp->activeWindow ());
     491      connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
     492      d->show ();
     493    });
     494
     495  q->add (
     496    [this] (const RpcResponse& r)
     497    {
     498      int64_t id = 0;
     499
     500      if (tr_variantDictFindInt (r.args.get (), TR_KEY_id, &id)
     501          && id != 0)
     502        refreshTorrents (QSet<int> () << id,
     503                         KeyList () << TR_KEY_fileStats << TR_KEY_files << TR_KEY_id << TR_KEY_name);
     504    });
     505
     506  q->run ();
     507}
     508
     509void
     510Session::refreshTorrents (const QSet<int>& ids, const KeyList& keys)
     511{
     512  tr_variant args;
     513  tr_variantInitDict (&args, 2);
     514  addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), keys);
     515  addOptionalIds (&args, ids);
     516
     517  RpcQueue * q = new RpcQueue ();
     518
     519  q->add (
     520    [this, &args] ()
     521    {
     522      return exec (TR_KEY_torrent_get, &args);
     523    });
     524
     525  const bool allTorrents = ids.empty ();
     526  q->add (
     527    [this, allTorrents] (const RpcResponse& r)
     528    {
     529      tr_variant * torrents;
     530      if (tr_variantDictFindList (r.args.get (), TR_KEY_torrents, &torrents))
     531        emit torrentsUpdated (torrents, allTorrents);
     532      if (tr_variantDictFindList (r.args.get (), TR_KEY_removed, &torrents))
     533        emit torrentsRemoved (torrents);
     534    });
     535
     536  q->run ();
    511537}
    512538
     
    514540Session::refreshExtraStats (const QSet<int>& ids)
    515541{
    516   tr_variant args;
    517   tr_variantInitDict (&args, 3);
    518   addOptionalIds (&args, ids);
    519   addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys () + getExtraStatKeys ());
    520 
    521   exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
     542  refreshTorrents (ids, getStatKeys () + getExtraStatKeys ());
    522543}
    523544
     
    529550  addOptionalIds (&args, ids);
    530551
    531   exec (request, &args);
    532 
    533   refreshTorrents (ids);
     552  RpcQueue * q = new RpcQueue ();
     553
     554  q->add (
     555    [this, request, &args] ()
     556    {
     557      return exec (request, &args);
     558    });
     559
     560  q->add (
     561    [this, ids] ()
     562    {
     563      refreshTorrents (ids, getStatKeys ());
     564    });
     565
     566  q->run ();
    534567}
    535568
     
    545578Session::refreshActiveTorrents ()
    546579{
    547   tr_variant args;
    548   tr_variantInitDict (&args, 2);
    549   tr_variantDictAddStr (&args, TR_KEY_ids, "recently-active");
    550   addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
    551 
    552   exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
     580  refreshTorrents (recentlyActiveIds, getStatKeys ());
    553581}
    554582
     
    556584Session::refreshAllTorrents ()
    557585{
    558   tr_variant args;
    559   tr_variantInitDict (&args, 1);
    560   addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
    561 
    562   exec (TR_KEY_torrent_get, &args, TAG_ALL_TORRENTS);
     586  refreshTorrents (allIds, getStatKeys ());
    563587}
    564588
     
    566590Session::initTorrents (const QSet<int>& ids)
    567591{
    568   tr_variant args;
    569   tr_variantInitDict (&args, 2);
    570   addOptionalIds (&args, ids);
    571   addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ()+getInfoKeys ());
    572 
    573   exec ("torrent-get", &args, ids.isEmpty () ? TAG_ALL_TORRENTS : TAG_SOME_TORRENTS);
     592  refreshTorrents (ids, getStatKeys () + getInfoKeys ());
    574593}
    575594
     
    577596Session::refreshSessionStats ()
    578597{
    579   exec ("session-stats", nullptr, TAG_SESSION_STATS);
     598  RpcQueue * q = new RpcQueue ();
     599
     600  q->add (
     601    [this] ()
     602    {
     603      return exec ("session-stats", nullptr);
     604    });
     605
     606  q->add (
     607    [this] (const RpcResponse& r)
     608    {
     609      updateStats (r.args.get ());
     610    });
     611
     612  q->run ();
    580613}
    581614
     
    583616Session::refreshSessionInfo ()
    584617{
    585   exec ("session-get", nullptr, TAG_SESSION_INFO);
     618  RpcQueue * q = new RpcQueue ();
     619
     620  q->add (
     621    [this] ()
     622    {
     623      return exec ("session-get", nullptr);
     624    });
     625
     626  q->add (
     627    [this] (const RpcResponse& r)
     628    {
     629      updateInfo (r.args.get ());
     630    });
     631
     632  q->run ();
    586633}
    587634
     
    589636Session::updateBlocklist ()
    590637{
    591   exec ("blocklist-update", nullptr, TAG_BLOCKLIST_UPDATE);
     638  RpcQueue * q = new RpcQueue ();
     639
     640  q->add (
     641    [this] ()
     642    {
     643      return exec ("blocklist-update", nullptr);
     644    });
     645
     646  q->add (
     647    [this] (const RpcResponse& r)
     648    {
     649      int64_t blocklistSize;
     650      if (tr_variantDictFindInt (r.args.get (), TR_KEY_blocklist_size, &blocklistSize))
     651        setBlocklistSize (blocklistSize);
     652    });
     653
     654  q->run ();
    592655}
    593656
     
    596659***/
    597660
    598 void
    599 Session::exec (tr_quark method, tr_variant * args, int64_t tag)
    600 {
    601   myRpc.exec (method, args, tag);
    602 }
    603 
    604 void
    605 Session::exec (const char* method, tr_variant * args, int64_t tag)
    606 {
    607   myRpc.exec (method, args, tag);
    608 }
    609 
    610 void
    611 Session::responseReceived (int64_t tag, const QString& result, tr_variant * args)
    612 {
    613   emit executed (tag, result, args);
    614 
    615   if (tag < 0)
    616     return;
    617 
    618   switch (tag)
    619     {
    620       case TAG_SOME_TORRENTS:
    621       case TAG_ALL_TORRENTS:
    622         if (args != nullptr)
    623           {
    624             tr_variant * torrents;
    625             if (tr_variantDictFindList (args, TR_KEY_torrents, &torrents))
    626                 emit torrentsUpdated (torrents, tag==TAG_ALL_TORRENTS);
    627             if (tr_variantDictFindList (args, TR_KEY_removed, &torrents))
    628                 emit torrentsRemoved (torrents);
    629           }
    630         break;
    631 
    632       case TAG_SESSION_STATS:
    633         if (args != nullptr)
    634           updateStats (args);
    635         break;
    636 
    637       case TAG_SESSION_INFO:
    638         if (args != nullptr)
    639           updateInfo (args);
    640         break;
    641 
    642       case TAG_BLOCKLIST_UPDATE:
    643         {
    644           int64_t intVal = 0;
    645           if (args != nullptr)
    646             {
    647               if (tr_variantDictFindInt (args, TR_KEY_blocklist_size, &intVal))
    648                 setBlocklistSize (intVal);
    649             }
    650           break;
    651         }
    652 
    653       case TAG_RENAME_PATH:
    654         {
    655           int64_t id = 0;
    656           if (result != QLatin1String ("success"))
    657             {
    658               const char * path = "";
    659               const char * name = "";
    660               tr_variantDictFindStr (args, TR_KEY_path, &path, 0);
    661               tr_variantDictFindStr (args, TR_KEY_name, &name, 0);
    662               const QString title = tr ("Error Renaming Path");
    663               const QString text = tr ("<p><b>Unable to rename \"%1\" as \"%2\": %3.</b></p> <p>Please correct the errors and try again.</p>").arg (QString::fromUtf8 (path)).arg (QString::fromUtf8 (name)).arg (result);
    664               QMessageBox * d = new QMessageBox (QMessageBox::Information, title, text,
    665                                                  QMessageBox::Close,
    666                                                  qApp->activeWindow ());
    667               connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
    668               d->show ();
    669             }
    670           else if (tr_variantDictFindInt (args, TR_KEY_id, &id) && id)
    671             {
    672               tr_variant args;
    673               tr_variantInitDict (&args, 2);
    674               tr_variantDictAddInt (&args, TR_KEY_ids, id);
    675               addList (tr_variantDictAddList (&args, TR_KEY_fields, 0),
    676                        KeyList () << TR_KEY_fileStats << TR_KEY_files << TR_KEY_id << TR_KEY_name);
    677               exec ("torrent-get", &args, TAG_SOME_TORRENTS);
    678             }
    679 
    680           break;
    681       }
    682 
    683       case TAG_PORT_TEST:
    684         {
    685           bool isOpen;
    686           if (args == nullptr ||
    687               !tr_variantDictFindBool (args, TR_KEY_port_is_open, &isOpen))
    688             isOpen = false;
    689           emit portTested (isOpen);
    690           break;
    691         }
    692 
    693       case TAG_MAGNET_LINK:
    694         {
    695           tr_variant * torrents;
    696           tr_variant * child;
    697           const char * str;
    698           if (args != nullptr
    699               && tr_variantDictFindList (args, TR_KEY_torrents, &torrents)
    700               && ( (child = tr_variantListChild (torrents, 0)))
    701               && tr_variantDictFindStr (child, TR_KEY_magnetLink, &str, NULL))
    702             qApp->clipboard ()->setText (QString::fromUtf8 (str));
    703           break;
    704         }
    705 
    706       case TAG_ADD_TORRENT:
    707         {
    708           const char * str = "";
    709           if (result != QLatin1String ("success"))
    710             {
    711               QMessageBox * d = new QMessageBox (QMessageBox::Information,
    712                                                  tr ("Add Torrent"),
    713                                                  QString::fromUtf8 (str),
    714                                                  QMessageBox::Close,
    715                                                  qApp->activeWindow ());
    716               connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
    717               d->show ();
    718             }
    719           break;
    720         }
    721     }
     661RpcResponseFuture
     662Session::exec (tr_quark method, tr_variant * args)
     663{
     664  return myRpc.exec (method, args);
     665}
     666
     667RpcResponseFuture
     668Session::exec (const char * method, tr_variant * args)
     669{
     670  return myRpc.exec (method, args);
    722671}
    723672
     
    893842    }
    894843
    895   const int64_t tag = getUniqueTag ();
    896 
    897   // maybe delete the source .torrent
    898   FileAdded * fileAdded = new FileAdded (tag, addMe.readableName ());
     844  RpcQueue * q = new RpcQueue ();
     845
     846  q->add (
     847    [this, args] ()
     848    {
     849      return exec ("torrent-add", args);
     850    },
     851    [this, addMe] (const RpcResponse& r)
     852    {
     853      QMessageBox * d = new QMessageBox (QMessageBox::Warning,
     854                                         tr ("Error Adding Torrent"),
     855                                         QString::fromLatin1 ("<p><b>%1</b></p><p>%2</p>")
     856                                           .arg (r.result)
     857                                           .arg (addMe.readableName ()),
     858                                         QMessageBox::Close,
     859                                         qApp->activeWindow ());
     860      connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
     861      d->show ();
     862    });
     863
     864  q->add (
     865    [this, addMe] (const RpcResponse& r)
     866    {
     867      tr_variant * dup;
     868      const char * str;
     869
     870      if (tr_variantDictFindDict (r.args.get (), TR_KEY_torrent_duplicate, &dup) &&
     871          tr_variantDictFindStr (dup, TR_KEY_name, &str, NULL))
     872        {
     873          const QString name = QString::fromUtf8 (str);
     874          QMessageBox * d = new QMessageBox (QMessageBox::Warning,
     875                                             tr ("Add Torrent"),
     876                                             tr ("<p><b>Unable to add \"%1\".</b></p>"
     877                                                 "<p>It is a duplicate of \"%2\" which is already added.</p>")
     878                                               .arg (addMe.readableShortName ())
     879                                               .arg (name),
     880                                             QMessageBox::Close,
     881                                             qApp->activeWindow ());
     882          connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
     883          d->show ();
     884        }
     885    });
     886
    899887  if (trashOriginal && addMe.type == AddData::FILENAME)
    900     fileAdded->setFileToDelete (addMe.filename);
    901   connect (this, SIGNAL (executed (int64_t, QString, tr_variant *)),
    902            fileAdded, SLOT (executed (int64_t, QString, tr_variant *)));
    903 
    904   exec ("torrent-add", args, tag);
     888    q->add (
     889      [this, addMe] ()
     890      {
     891        QFile original (addMe.filename);
     892        original.setPermissions (QFile::ReadOwner | QFile::WriteOwner);
     893        original.remove ();
     894      });
     895
     896  q->run ();
    905897}
    906898
  • trunk/qt/Session.h

    r14724 r14732  
    11/*
    2  * This file Copyright (C) 2009-2015 Mnemosyne LLC
     2 * This file Copyright (C) 2009-2016 Mnemosyne LLC
    33 *
    44 * It may be used under the GNU GPL versions 2 or 3
     
    1919
    2020#include "RpcClient.h"
     21#include "Torrent.h"
    2122
    2223class AddData;
     
    2728  struct tr_variant;
    2829}
    29 
    30 class FileAdded: public QObject
    31 {
    32     Q_OBJECT
    33 
    34   public:
    35     FileAdded (int64_t tag, const QString& name): myTag (tag), myName (name) {}
    36     virtual ~FileAdded () {}
    37 
    38     void setFileToDelete (const QString& file) { myDelFile = file; }
    39 
    40   public slots:
    41     void executed (int64_t tag, const QString& result, tr_variant * arguments);
    42 
    43   private:
    44     const int64_t myTag;
    45     const QString myName;
    46 
    47     QString myDelFile;
    48 };
    4930
    5031class Session: public QObject
     
    7657    bool isLocal () const;
    7758
    78     void exec (tr_quark method, tr_variant * args, int64_t tag = -1);
    79     void exec (const char * method, tr_variant * args, int64_t tag = -1);
    80 
    81     int64_t getUniqueTag () { return nextUniqueTag++; }
     59    RpcResponseFuture exec (tr_quark method, tr_variant * args);
     60    RpcResponseFuture exec (const char * method, tr_variant * args);
    8261
    8362    void torrentSet (const QSet<int>& ids, const tr_quark key, bool val);
     
    11695
    11796  signals:
    118     void executed (int64_t tag, const QString& result, tr_variant * arguments);
    11997    void sourceChanged ();
    12098    void portTested (bool isOpen);
     
    126104    void dataReadProgress ();
    127105    void dataSendProgress ();
    128     void error (QNetworkReply::NetworkError);
    129     void errorMessage (const QString&);
     106    void networkResponse (QNetworkReply::NetworkError code, const QString& message);
    130107    void httpAuthenticationRequired ();
    131108
     
    139116    void pumpRequests ();
    140117    void sendTorrentRequest (const char * request, const QSet<int>& torrentIds);
    141     void refreshTorrents (const QSet<int>& torrentIds);
     118    void refreshTorrents (const QSet<int>& torrentIds, const Torrent::KeyList& keys);
    142119
    143120    static void updateStats (tr_variant * d, tr_session_stats * stats);
    144 
    145   private slots:
    146     void responseReceived (int64_t tag, const QString& result, tr_variant * args);
    147121
    148122  private:
     
    150124    Prefs& myPrefs;
    151125
    152     int64_t nextUniqueTag;
    153126    int64_t myBlocklistSize;
    154127    tr_session * mySession;
  • trunk/qt/qtr.pro

    r14629 r14732  
    101101           RelocateDialog.cc \
    102102           RpcClient.cc \
     103           RpcQueue.cc \
    103104           Session.cc \
    104105           SessionDialog.cc \
Note: See TracChangeset for help on using the changeset viewer.