[tor-commits] [vidalia/alpha] Make TorControl work without threads

chiiph at torproject.org chiiph at torproject.org
Mon Mar 5 13:01:51 UTC 2012


commit a3b901fa122bc8f4b82a48d3eb3fae1fede86898
Author: Tomás Touceda <chiiph at torproject.org>
Date:   Fri Feb 24 17:13:51 2012 -0300

    Make TorControl work without threads
---
 src/torcontrol/ControlConnection.cpp |   98 +++++++++++-----------------------
 src/torcontrol/ControlConnection.h   |    3 +-
 src/torcontrol/SendCommandEvent.cpp  |   18 +-----
 src/torcontrol/TorControl.cpp        |    4 ++
 src/vidalia/MainWindow.cpp           |   56 +++++++++----------
 5 files changed, 67 insertions(+), 112 deletions(-)

diff --git a/src/torcontrol/ControlConnection.cpp b/src/torcontrol/ControlConnection.cpp
index 365da32..21cb527 100644
--- a/src/torcontrol/ControlConnection.cpp
+++ b/src/torcontrol/ControlConnection.cpp
@@ -29,21 +29,19 @@
 
 /** Default constructor. */
 ControlConnection::ControlConnection(ControlMethod::Method method, TorEvents *events)
+  : QObject()
 {
   _events = events;
   _status = Unset;
   _sock = 0;
   _sendWaiter = new SendCommandEvent::SendWaiter();
   _method = method;
+  _connected = false;
 }
 
 /** Destructor. */
 ControlConnection::~ControlConnection()
 {
-  /* Exit the event loop */
-  exit();
-  /* Wait for the thread to finish */
-  wait();
   /* Clean up after the send waiter */
   delete _sendWaiter;
 }
@@ -52,7 +50,7 @@ ControlConnection::~ControlConnection()
 void
 ControlConnection::connect(const QHostAddress &addr, quint16 port)
 {
-  if (isRunning()) {
+  if (_connected) {
     tc::error("Bug: Tried to call ControlConnection::connect() when the "
               "control thread is already running.");
     return;
@@ -65,15 +63,14 @@ ControlConnection::connect(const QHostAddress &addr, quint16 port)
   _connectAttempt = 0;
   setStatus(Connecting);
 
-  /* Kick off the thread in which the control socket will live */
-  QThread::start();
+  run();
 }
 
 /** Connect to the specified Tor control socket interface. */
 void
 ControlConnection::connect(const QString &addr)
 {
-  if (isRunning()) {
+  if (_connected) {
     tc::error("Bug: Tried to call ControlConnection::connect() when the "
               "control thread is already running.");
     return;
@@ -83,8 +80,7 @@ ControlConnection::connect(const QString &addr)
   _connectAttempt = 0;
   setStatus(Connecting);
 
-  /* Kick off the thread in which the control socket will live */
-  QThread::start();
+  run();
 }
 
 /** Attempt to establish a connection to Tor's control interface. We will try
@@ -97,7 +93,6 @@ ControlConnection::connect()
   tc::debug("Connecting to Tor (Attempt %1 of %2)").arg(_connectAttempt)
                                                    .arg(MAX_CONNECT_ATTEMPTS);
   
-  _connMutex.lock();
   switch(_method) {
     case ControlMethod::Socket:
       _sock->connectToServer(_path);
@@ -108,15 +103,26 @@ ControlConnection::connect()
       _sock->connectToHost(_addr, _port);
     break;
   }
-  _connMutex.unlock();
 }
 
 /** Disconnect from Tor's control interface. */
 void
 ControlConnection::disconnect()
 {
+  /** If there are any messages waiting for a response, clear them. */
+  if (_sendWaiter->status() == SendCommandEvent::SendWaiter::Waiting)
+    _sendWaiter->setResult(false, tr("Control socket is not connected."));
+
+  while (!_recvQueue.isEmpty()) {
+    ReceiveWaiter *w = _recvQueue.dequeue();
+    w->setResult(false, ControlReply(), 
+                 tr("Control socket is not connected."));
+
+    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
+  }
+
   setStatus(Disconnecting);
-  _connMutex.lock();
+
   switch(_method) {
     case ControlMethod::Socket:
       _sock->disconnectFromServer();
@@ -127,7 +133,11 @@ ControlConnection::disconnect()
       _sock->disconnectFromHost();
     break;
   }
-  _connMutex.unlock();
+
+  _sock->disconnect(this);
+  delete _sock;
+  delete _connectTimer;
+  _sock = 0;
 }
 
 /** Called when the control socket is connected. This method checks that the
@@ -136,6 +146,7 @@ void
 ControlConnection::onConnected()
 {
   setStatus(Connected);
+  _connected = true;
   emit connected();
 }
 
@@ -145,8 +156,8 @@ void
 ControlConnection::onDisconnected()
 {
   setStatus(Disconnected);
+  _connected = false;
   emit disconnected();
-  exit(0);
 }
 
 /** Called when the control socket encounters <b>error</b>. */
@@ -186,9 +197,9 @@ ControlConnection::onError(QAbstractSocket::SocketError error)
 void
 ControlConnection::cancelConnect()
 {
+  _connected = false;
   tc::warn("Control connection attempt cancelled.");
   setStatus(Disconnected);
-  exit(0);
 }
 
 /** Returns true if the control socket is connected to Tor. */
@@ -202,7 +213,6 @@ ControlConnection::isConnected()
 ControlConnection::Status
 ControlConnection::status()
 {
-  QMutexLocker locker(&_statusMutex);
   return _status;
 }
 
@@ -227,7 +237,6 @@ ControlConnection::statusString(Status status)
 void
 ControlConnection::setStatus(Status status)
 {
-  QMutexLocker locker(&_statusMutex);
   tc::debug("Control connection status changed from '%1' to '%2'")
                                        .arg(statusString(_status))
                                        .arg(statusString(status));
@@ -242,12 +251,10 @@ ControlConnection::send(const ControlCommand &cmd,
   bool result = false;
   QString errstr;
 
-  _recvMutex.lock();
   if (send(cmd, &errstr)) {
     /* Create and enqueue a new receive waiter */
     ReceiveWaiter *w = new ReceiveWaiter();
     _recvQueue.enqueue(w);
-    _recvMutex.unlock();
 
     /* Wait for and get the result, clean up, and return */
     result = w->getResult(&reply, &errstr);
@@ -257,7 +264,6 @@ ControlConnection::send(const ControlCommand &cmd,
   } else {
     tc::error("Failed to send control command (%1): %2").arg(cmd.keyword())
                                                         .arg(errstr);
-    _recvMutex.unlock();
   }
 
   if (!result && errmsg)
@@ -271,13 +277,10 @@ ControlConnection::send(const ControlCommand &cmd,
 bool
 ControlConnection::send(const ControlCommand &cmd, QString *errmsg)
 {
-  _connMutex.lock();
   if (!_sock || !_sock->isConnected()) {
-    _connMutex.unlock();
     return err(errmsg, tr("Control socket is not connected.")); 
   }
   QCoreApplication::postEvent(_sock, new SendCommandEvent(cmd, _sendWaiter));
-  _connMutex.unlock();
   
   return _sendWaiter->getResult(errmsg);
 }
@@ -286,7 +289,6 @@ ControlConnection::send(const ControlCommand &cmd, QString *errmsg)
 void
 ControlConnection::onReadyRead()
 {
-  QMutexLocker locker(&_connMutex);
   ReceiveWaiter *waiter;
   QString errmsg;
  
@@ -304,12 +306,10 @@ ControlConnection::onReadyRead()
         /* Response to a previous command */
         tc::debug("Control Reply: %1").arg(reply.toString());
         
-        _recvMutex.lock();
         if (!_recvQueue.isEmpty()) {
           waiter = _recvQueue.dequeue();
           waiter->setResult(true, reply);
         }
-        _recvMutex.unlock();
       }
     } else {
       tc::error("Unable to read control reply: %1").arg(errmsg);
@@ -323,7 +323,6 @@ void
 ControlConnection::run()
 {
   /* Create a new control socket */
-  _connMutex.lock();
   _sock = new ControlSocket(_method);
 
   _connectTimer = new QTimer();
@@ -341,33 +340,8 @@ ControlConnection::run()
   QObject::connect(_connectTimer, SIGNAL(timeout()), this, SLOT(connect()),
                    Qt::DirectConnection);
 
-  _connMutex.unlock();
-  
   /* Attempt to connect to Tor */
   connect();
-  tc::debug("Starting control connection event loop.");
-  exec();
-  tc::debug("Exited control connection event loop.");
-
-  /* Clean up the socket */
-  _connMutex.lock();
-  _sock->disconnect(this);
-  delete _sock;
-  delete _connectTimer;
-  _sock = 0;
-  _connMutex.unlock();
-
-  /* If there are any messages waiting for a response, clear them. */
-  if (_sendWaiter->status() == SendCommandEvent::SendWaiter::Waiting)
-    _sendWaiter->setResult(false, tr("Control socket is not connected."));
-
-  _recvMutex.lock();
-  while (!_recvQueue.isEmpty()) {
-    ReceiveWaiter *w = _recvQueue.dequeue();
-    w->setResult(false, ControlReply(), 
-                 tr("Control socket is not connected."));
-  }
-  _recvMutex.unlock();
 }
 
 
@@ -379,16 +353,9 @@ bool
 ControlConnection::ReceiveWaiter::getResult(ControlReply *reply, 
                                             QString *errmsg)
 {
-  forever {
-    _mutex.lock();
-    if (_status == Waiting) {
-      _waitCond.wait(&_mutex);
-      _mutex.unlock();
-    } else {
-      _mutex.unlock();
-      break;
-    }
-  }
+  while(_status == Waiting)
+    QCoreApplication::processEvents();
+
   if (errmsg) {
     *errmsg = _errmsg;
   }
@@ -402,11 +369,8 @@ ControlConnection::ReceiveWaiter::setResult(bool success,
                                             const ControlReply &reply, 
                                             const QString &errmsg)
 {
-  _mutex.lock();
   _status = (success ? Success : Failed);
-  _reply = reply; 
+  _reply = reply;
   _errmsg = errmsg;
-  _mutex.unlock();
-  _waitCond.wakeAll();
 }
 
diff --git a/src/torcontrol/ControlConnection.h b/src/torcontrol/ControlConnection.h
index 5a56ced..d2d2965 100644
--- a/src/torcontrol/ControlConnection.h
+++ b/src/torcontrol/ControlConnection.h
@@ -29,7 +29,7 @@
 #include <QHostAddress>
 
 
-class ControlConnection : public QThread
+class ControlConnection : public QObject
 {
   Q_OBJECT
 
@@ -127,6 +127,7 @@ private:
   };
   QQueue<ReceiveWaiter *> _recvQueue; /**< Objects waiting for a reply. */
   SendCommandEvent::SendWaiter* _sendWaiter;
+  bool _connected;
 };
 
 #endif
diff --git a/src/torcontrol/SendCommandEvent.cpp b/src/torcontrol/SendCommandEvent.cpp
index 8472324..cf66a1a 100644
--- a/src/torcontrol/SendCommandEvent.cpp
+++ b/src/torcontrol/SendCommandEvent.cpp
@@ -16,8 +16,7 @@
 
 #include "SendCommandEvent.h"
 
-#include <QMutexLocker>
-
+#include <QCoreApplication>
 
 SendCommandEvent::SendCommandEvent(const ControlCommand &cmd, SendWaiter *w)
   : QEvent(QEvent::User)
@@ -30,26 +29,16 @@ SendCommandEvent::SendCommandEvent(const ControlCommand &cmd, SendWaiter *w)
 void
 SendCommandEvent::SendWaiter::setResult(bool success, const QString &errmsg)
 {
-  _mutex.lock();
   _status = (success ? Success : Failed);
   _errmsg = errmsg;
-  _mutex.unlock();
-  _waitCond.wakeAll();
 }
 
 /** Waits for and gets the result of the send operation. */
 bool 
 SendCommandEvent::SendWaiter::getResult(QString *errmsg)
 {
-  forever {
-    _mutex.lock();
-    if (_status == Waiting) {
-      _waitCond.wait(&_mutex);
-      _mutex.unlock();
-    } else {
-      _mutex.unlock();
-      break;
-    }
+  while(_status == Waiting) {
+    QCoreApplication::processEvents();
   }
   if (errmsg) {
     *errmsg = _errmsg;
@@ -61,6 +50,5 @@ SendCommandEvent::SendWaiter::getResult(QString *errmsg)
 SendCommandEvent::SendWaiter::SenderStatus
 SendCommandEvent::SendWaiter::status()
 {
-  QMutexLocker locker(&_mutex);
   return _status;
 }
diff --git a/src/torcontrol/TorControl.cpp b/src/torcontrol/TorControl.cpp
index e95f9c7..603ba94 100644
--- a/src/torcontrol/TorControl.cpp
+++ b/src/torcontrol/TorControl.cpp
@@ -101,6 +101,7 @@ TorControl::~TorControl()
   if (isVidaliaRunningTor()) {
     stop();
   }
+  _controlConn->disconnect();
   delete _controlConn;
 }
 
@@ -132,6 +133,9 @@ TorControl::stop(QString *errmsg)
 {
   bool rc = false;
 
+  if(!errmsg)
+    errmsg = new QString();
+
   if (!isVidaliaRunningTor()) {
     *errmsg = tr("Vidalia has not started Tor. "
         "You need to stop Tor through the interface you started it.");
diff --git a/src/vidalia/MainWindow.cpp b/src/vidalia/MainWindow.cpp
index 65e1e3c..1c4241f 100644
--- a/src/vidalia/MainWindow.cpp
+++ b/src/vidalia/MainWindow.cpp
@@ -557,7 +557,6 @@ MainWindow::aboutToQuit()
   QObject::disconnect(_torControl, 0, 0, 0);
 }
 
-
 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
  * called with an error message containing the reason. */
 void 
@@ -892,34 +891,6 @@ void
 MainWindow::connected()
 {
   authenticate();
-  if(_torControl->isVidaliaRunningTor()) {
-    QString err;
-    if(!_torControl->takeOwnership(&err))
-      vWarn(err);
-  }
-
-  TorSettings settings;
-  if(settings.autoControlPort()) {
-    // We want to remember the ports if it's on auto
-    QString control_str = "", socks_str = "";
-    if(_torControl->getInfo("net/listeners/control", control_str)) {
-      QStringList control_parts = control_str.split(":");
-      if(control_parts.size() > 1)
-        control_str = control_parts[1];
-    }
-    if(_torControl->getInfo("net/listeners/socks", socks_str)) {
-      QStringList socks_parts = socks_str.split(":");
-      if(socks_parts.size() > 1)
-        socks_str = socks_parts[1];
-    }
-    
-    _previousControlPort = control_str;
-    _previousSocksPort = socks_str;
-  } else {
-    // Otherwise we want to clear the remembered ports
-    _previousControlPort = "";
-    _previousSocksPort = "";
-  }
 }
 
 /** Called when the connection to the control socket fails. The reason will be
@@ -952,6 +923,29 @@ MainWindow::connectFailed(QString errmsg)
 void
 MainWindow::authenticated()
 {
+  TorSettings settings;
+  if(settings.autoControlPort()) {
+    // We want to remember the ports if it's on auto
+    QString control_str = "", socks_str = "";
+    if(_torControl->getInfo("net/listeners/control", control_str)) {
+      QStringList control_parts = control_str.split(":");
+      if(control_parts.size() > 1)
+        control_str = control_parts[1];
+    }
+    if(_torControl->getInfo("net/listeners/socks", socks_str)) {
+      QStringList socks_parts = socks_str.split(":");
+      if(socks_parts.size() > 1)
+        socks_str = socks_parts[1];
+    }
+    
+    _previousControlPort = control_str;
+    _previousSocksPort = socks_str;
+  } else {
+    // Otherwise we want to clear the remembered ports
+    _previousControlPort = "";
+    _previousSocksPort = "";
+  }
+
   ServerSettings serverSettings(_torControl);
   QString errmsg;
 
@@ -1143,6 +1137,10 @@ MainWindow::bootstrapStatusChanged(const BootstrapStatus &bs)
     case BootstrapStatus::BootstrappingDone:
       description = tr("Connected to the Tor network!");
       warn = false; /* probably false anyway */
+      // There could be a condition where the circuitEstablished
+      // signal is not emitted, so we want to assure that the green
+      // onion appears at the end
+      circuitEstablished();
       break;
     default:
       description = tr("Unrecognized startup status");





More information about the tor-commits mailing list