[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r1250: Run an event loop in our thread that waits for asynchronous (trunk/src/control)



Author: edmanm
Date: 2006-10-01 02:45:22 -0400 (Sun, 01 Oct 2006)
New Revision: 1250

Modified:
   trunk/src/control/controlconnection.cpp
   trunk/src/control/controlconnection.h
   trunk/src/control/controlsocket.cpp
   trunk/src/control/controlsocket.h
   trunk/src/control/eventtype.h
Log:
Run an event loop in our thread that waits for asynchronous events from Tor,
instead of essentially blocking waiting for events (Ticket #101). This is a
big change to a critical component. hipplej, please review, test, check for
rabies, etc.


Modified: trunk/src/control/controlconnection.cpp
===================================================================
--- trunk/src/control/controlconnection.cpp	2006-09-30 17:33:01 UTC (rev 1249)
+++ trunk/src/control/controlconnection.cpp	2006-10-01 06:45:22 UTC (rev 1250)
@@ -26,62 +26,91 @@
  * receiving commands and events
  */
 
-#include <QCoreApplication>
+#include <QMutexLocker>
 #include <util/string.h>
+#include <vidalia.h>
 
 #include "controlconnection.h"
 
-
-/** Maximum time to wait for new data to arrive on the socket. */
-#define WAIT_TIMEOUT          10
 /** Maximum number of times we'll try to connect to Tor before giving up.*/
 #define MAX_CONNECT_ATTEMPTS  6
 /** Time to wait between control connection attempts (in microseconds). */
 #define CONNECT_RETRY_DELAY   500*1000
 
 
-/** Constructor.
- * \param events a TorEvents object used to dispatch asynchronous events
- * received from Tor.
- */
+/** Default constructor. */
 ControlConnection::ControlConnection(TorEvents *events)
 {
   _events = events;
-  _run    = false;
-  _status = Disconnected;
+  _sock = 0;
 }
 
-/** Connects to Tor's control interface and starts a thread used to process
- * asynchronous event messages received from Tor. All socket communication is
- * done in the new thread, processing incoming and outgoing messages through the
- * send and receive queues.
- * \param addr IP address of Tor's control interface.
- * \param port port of Tor's control interface.
- * \param errmsg Stores an error message if the connection fails.
- * \return true if a connection to Tor's control interface was successfully
- * established. Otherwise, returns false and sets errmsg.
- * */
+/** Destructor. */
+ControlConnection::~ControlConnection()
+{
+  /* Exit the event loop */
+  exit();
+  /* Wait for the thread to finish. */
+  wait();
+}
+
+/** Connect to the specified Tor control interface. */
 void
 ControlConnection::connect(QHostAddress addr, quint16 port)
 {
-  /* Store the control interface address information */
+  /* Save the destination information */
   _addr = addr;
   _port = port;
-  _run  = true;
-  _status = Disconnected;
-  _cancelConnect = false;
-
-  /* Start a thread. Either connected() or connectFailed() will be emitted
-   * once the connection attempt has completed. */
+  /* Kick off the thread in which the control socket will live */
   QThread::start();
 }
 
-/** Cancels a pending connection attempt. */
+/** Attempt to establish a connection to Tor's control interface. We will try
+* a maximum of MAX_CONNECT_ATTEMPTS, waiting CONNECT_RETRY_DELAY between each
+* attempt, to give slow Tors a chance to finish binding their control port. */
+bool
+ControlConnection::connect()
+{
+  QString errmsg;
+  bool result;
+  
+  setStatus(Connecting);
+  for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) {
+    /* Check if we're supposed to cancel our attempt to connect */
+    if (status() != Connecting) {
+      return false;
+    }
+    
+    /* Try to connect */
+    _connMutex.lock();
+    result = _sock->connect(_addr, _port, &errmsg); 
+    _connMutex.unlock();
+    if (result) {
+      setStatus(Connected);
+      emit connected();
+      return true;
+    }
+    QThread::usleep(CONNECT_RETRY_DELAY);
+  }
+  setStatus(Disconnected);
+  emit connectFailed(errmsg);
+  return false;
+}
+
+/** Disconnect from Tor's control interface. */
 void
+ControlConnection::disconnect()
+{
+  /* Stop the event loop */
+  exit(0);
+}
+
+/** Cancels a pending control connection to Tor. */
+void
 ControlConnection::cancelConnect()
 {
   QMutexLocker locker(&_connMutex);
-  _cancelConnect = true;
+  _status = Disconnected;
 }
 
 /** Returns the status of the control connection. */
@@ -89,16 +118,10 @@
 ControlConnection::status()
 {
   QMutexLocker locker(&_connMutex);
-  if (_status == Connected) {
-    /* The thread must be running for us to be considered connected */
-    if (!_run || !isRunning()) {
-      return Disconnected;
-    }
-  }
   return _status;
 }
 
-/** Sets the status of the control connection. */
+/** Sets the control connection status. */
 void
 ControlConnection::setStatus(Status status)
 {
@@ -106,150 +129,71 @@
   _status = status;
 }
 
-/** Disconnects the control socket and stops all message processing. */
-void
-ControlConnection::disconnect()
+/** Sends a control command to Tor and waits for the reply. */
+bool
+ControlConnection::send(ControlCommand cmd, ControlReply &reply, QString *errmsg)
 {
-  /* Check if we're already disconnected */
-  if (status() != Connected) {
-    return;
-  }
-  _run = false;
-}
+  _recvMutex.lock();
+  if (send(cmd, errmsg)) {
+    /* Create and enqueue a new receive waiter */
+    ReceiveWaiter *w = new ReceiveWaiter();
+    _recvQueue.enqueue(w);
+    _recvMutex.unlock();
 
-/** Flushes any outstanding waiters in the send or receive queues. */
-void
-ControlConnection::flushQueues()
-{
-  /* Flush any outstanding waiters in the send queue */
-  _sendMutex.lock();
-  while (!_sendQueue.isEmpty()) {
-    SendWaiter *waiter = _sendQueue.dequeue();
-    waiter->setResult(Failed, tr("The control socket is closing."));
+    /* Wait for and get the result, clean up, and return */
+    bool result = w->getResult(&reply, errmsg);
+    delete w;
+    return result;
   }
-  _sendMutex.unlock();
-
-  /* Flush any outstanding waiters in the receive queue */
-  _recvMutex.lock();
-  while (!_recvQueue.isEmpty()) {
-    ReceiveWaiter *waiter = _recvQueue.dequeue();
-    waiter->setResult(Failed, ControlReply(), 
-                       tr("The control socket is closing."));
-  }
   _recvMutex.unlock();
+  return false;
 }
 
-/** Sends a control command to Tor and does not wait for a reply. */
+/** Sends a control command to Tor. If the current thread is not the thread
+ * that actually owns the socket, the command will be pushed to the correct
+ * thread before sending. */
 bool
 ControlConnection::send(ControlCommand cmd, QString *errmsg)
 {
-  QWaitCondition waitCond;
-  SendWaiter sendWaiter(cmd, &waitCond);
-  
-  /* Make sure we have a valid control socket before trying to send. */
-  if (status() != Connected) {
-    return err(errmsg, tr("Control socket not connected."));
-  }
- 
-  /* Add a send waiter to the outgoing message queue */
-  _sendMutex.lock();
-  _sendQueue.enqueue(&sendWaiter);
-  waitCond.wait(&_sendMutex);
-  _sendMutex.unlock();
+  QThread *socketThread;
+  bool result;
 
-  /* Check if the send failed. */
-  if (sendWaiter.status() == Failed) {
-    return err(errmsg, sendWaiter.errorString());
+  /* Check for a valid and connected socket */
+  _connMutex.lock();
+  if (!_sock || _status != Connected) {
+    _connMutex.unlock();
+    return err(errmsg, tr("Control socket is not connected."));
   }
-  return true;
-}
+  socketThread = _sock->thread();
+  _connMutex.unlock();
 
-/** Sends a control command to Tor and waits for the response.
- * \param cmd The command to send to Tor.
- * \param reply The response from Tor.
- * \param errmsg Stores an error message if the send or receive failed.
- * \return true if the command was sent and a response was received.
- */
-bool
-ControlConnection::send(ControlCommand cmd, ControlReply &reply,  QString *errmsg)
-{
-  QWaitCondition waitCond;
-  SendWaiter sendWaiter(cmd, &waitCond);
-  ReceiveWaiter recvWaiter(&reply, &waitCond);
+  if (socketThread != QThread::currentThread()) {
+    /* Push the message to the correct thread before sending */
+    SendWaiter *w = new SendWaiter();
+    Vidalia::postEvent(_sock, new SendCommandEvent(cmd, w));
   
-  /* Make sure we have a valid control socket before trying to send. */
-  if (status() != Connected) {
-    return err(errmsg, tr("Control socket not connected."));
+    /* Wait for the result, clean up, and return */
+    result = w->getResult(errmsg);
+    delete w;
+  } else {
+    /* Send the command directly on the socket */
+    _connMutex.lock();
+    result = _sock->sendCommand(cmd, errmsg);
+    _connMutex.unlock();
   }
- 
-  /* Add a send waiter to the outgoing message queue */
-  _sendMutex.lock();
-  _sendQueue.enqueue(&sendWaiter);
-    
-  /* Lock the receive mutex and wait for the message to be sent. The receive
-   * mutex is locked so the response doesn't get added to the incoming message
-   * queue before we have a waiter for it, but we don't add a waiter yet
-   * because if the send fails, then no response will ever be received. */
-  _recvMutex.lock();
-  waitCond.wait(&_sendMutex);
-  _sendMutex.unlock();
-
-  /* Check if the send failed. */
-  if (sendWaiter.status() == Failed) {
-    _recvMutex.unlock();
-    return err(errmsg, sendWaiter.errorString());
-  }
-  
-  /* The send was successful, so add a waiter for the response. */
-  _recvQueue.enqueue(&recvWaiter);
-  waitCond.wait(&_recvMutex);
-  _recvMutex.unlock();
-     
-  /* If the receive failed, then get the error string */
-  if (recvWaiter.status() == Failed) {
-    return err(errmsg, recvWaiter.errorString());
-  }
-  return true;
+  return result;
 }
 
-/** Determines if there are any messages waiting in the outgoing message queue.
- * \return true if the send queue is empty.
- */
-bool
-ControlConnection::isSendQueueEmpty()
-{
-  QMutexLocker locker(&_sendMutex);
-  return _sendQueue.isEmpty();
-}
-
-/** Processes any messages waiting in the send queue. */
+/** Called when there is data on the control socket. */
 void
-ControlConnection::processSendQueue(ControlSocket *sock)
+ControlConnection::onReadyRead()
 {
-  SendWaiter *sendWaiter;
-  bool result;
-  QString errorString;
+  QMutexLocker locker(&_connMutex);
+  ReceiveWaiter *waiter;
  
-  /* Iterate through all messages waiting in the queue */
-  _sendMutex.lock();
-  while (!_sendQueue.isEmpty()) {
-    /* Grab a waiter, try to send it, and store the result */
-    sendWaiter = _sendQueue.dequeue();
-    result = sock->sendCommand(sendWaiter->command(), &errorString);
-    sendWaiter->setResult((result ? Success : Failed), errorString);
-  }
-  _sendMutex.unlock();
-}
-
-/** Processes any messages waiting on the control socket. */
-void
-ControlConnection::processReceiveQueue(ControlSocket *sock)
-{
-  ReceiveWaiter *waiter;
-  
-  while (sock->canReadLine()) {
+  while (_sock->canReadLine()) {
     ControlReply reply;
-    if (sock->readReply(reply)) {
+    if (_sock->readReply(reply)) {
       if (reply.getStatus() == "650") {
         /* Asynchronous event message */
         if (_events) {
@@ -260,7 +204,7 @@
         _recvMutex.lock();
         if (!_recvQueue.isEmpty()) {
           waiter = _recvQueue.dequeue();
-          waiter->setResult(Success, reply);
+          waiter->setResult(true, reply);
         }
         _recvMutex.unlock();
       }
@@ -268,75 +212,150 @@
   }
 }
 
-/** Attempt to establish a connection to Tor's control interface. We will try
- * a maximum of MAX_CONNECT_ATTEMPTS, waiting CONNECT_RETRY_DELAY between each
- * attempt, to give slow Tors a chance to finish binding their control port. */
+/** Catches events for the control socket. */
 bool
-ControlConnection::connect(ControlSocket *sock, QString *errmsg)
+ControlConnection::eventFilter(QObject *obj, QEvent *event)
 {
-  setStatus(Connecting);
-  for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) {
-    /* Check if we're supposed to cancel our attempt to connect */
+  if (event->type() == CustomEventType::SendCommandEvent) {
+    /* Get the control command and waiter from the event */
+    SendCommandEvent *sce = (SendCommandEvent *)event;
+    SendWaiter *w = sce->waiter();
+    QString errmsg;
+    bool result;
+    
+    /* Send the command, if the socket exists. */
     _connMutex.lock();
-    if (_cancelConnect) {
-      _connMutex.unlock();
-      return false;
+    if (_sock) {
+      result = _sock->sendCommand(sce->command(), &errmsg);
+    } else {
+      result = false;
+      errmsg = tr("Control socket is not connected");
     }
     _connMutex.unlock();
     
-    /* Try to connect */
-    if (sock->connect(_addr, _port, errmsg)) {
-      return true;
+    /* If there is someone waiting for a result, give them one. */
+    if (w) {
+      w->setResult(result, errmsg);
     }
-    QThread::usleep(CONNECT_RETRY_DELAY);
+
+    /* Stop processing this event */
+    sce->accept();
+    return true;
   }
-  return false;
+  /* Pass other events on */
+  return QObject::eventFilter(obj, event);
 }
 
-/** Main thread implementation. Creates and connects a control socket and
- * processes any messages either waiting to be sent or waiting to be read off
- * the control socket, until either disconnect() is called or the socket gets
- * disconnected through some other means. */
+/** Main thread implementation. Creates and connects a control socket, then
+ * spins up an event loop. */
 void
 ControlConnection::run()
 {
-  QString errmsg;
+  /* Create a new control socket */
+  _connMutex.lock();
+  _sock = new ControlSocket();
+  QObject::connect(_sock, SIGNAL(readyRead()), this, SLOT(onReadyRead()),
+                   Qt::DirectConnection);
+  _sock->installEventFilter(this);
+  _connMutex.unlock();
   
-  /* Create a new control socket and try to connect to Tor */
-  ControlSocket *sock = new ControlSocket();
+  /* Attempt to connect to Tor */
+  if (connect()) {
+    /* Kick off the event loop */
+    exec();
+  }
   
-  if (!connect(sock, &errmsg)) {
-    if (!_cancelConnect) {
-      /* If the connect failed and wasn't cancelled, then emit the error */
-      emit connectFailed(errmsg);
+  /* Update the connection status */
+  setStatus(Disconnected);
+  emit disconnected();
+
+  /* Clean up the socket */
+  _connMutex.lock();
+  QObject::disconnect(_sock, 0, 0, 0);
+  delete _sock;
+  _sock = 0;
+  _connMutex.unlock();
+
+  /* If there are any messages waiting for a response, clear them. */
+  _recvMutex.lock();
+  foreach (ReceiveWaiter *w, _recvQueue) {
+    w->setResult(false, ControlReply(), 
+                 tr("Control socket is not connected."));
+  }
+  _recvMutex.unlock();
+}
+
+
+/*
+ * ControlConnection::ReceiveWaiter
+ */
+/** Waits for and gets the reply from a control command. */
+bool 
+ControlConnection::ReceiveWaiter::getResult(ControlReply *reply, 
+                                            QString *errmsg)
+{
+  forever {
+    _mutex.lock();
+    if (_status == Waiting) {
+      _waitCond.wait(&_mutex);
+      _mutex.unlock();
+    } else {
+      _mutex.unlock();
+      break;
     }
-  } else {
-    setStatus(Connected);
-    emit connected();
-    while (_run && sock->isConnected()) {
-      /* If there are messages in the send queue, then send them */
-      if (!isSendQueueEmpty()) {
-        processSendQueue(sock);
-      }
-      
-      /* Wait for some data to appear on the socket */
-      sock->waitForReadyRead(WAIT_TIMEOUT);
-      
-      /* If there are messages waiting on the socket, read them in */
-      if (sock->bytesAvailable()) {
-        processReceiveQueue(sock);
-      }
+  }
+  if (errmsg) {
+    *errmsg = _errmsg;
+  }
+  *reply = _reply;
+  return (_status == Success);
+}
+
+/** Sets the result and reply from a control command. */
+void 
+ControlConnection::ReceiveWaiter::setResult(bool success, 
+                                            ControlReply reply, 
+                                            QString errmsg)
+{ 
+  _status = (success ? Success : Failed);
+  _reply = reply; 
+  _errmsg = errmsg;
+  _waitCond.wakeAll();
+
+}
+
+
+/*
+ * ControlConnection::SendWaiter
+ */
+/** Sets the result of the send operation. */
+void
+ControlConnection::SendWaiter::setResult(bool success, 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 
+ControlConnection::SendWaiter::getResult(QString *errmsg)
+{
+  forever {
+    _mutex.lock();
+    if (_status == Waiting) {
+      _waitCond.wait(&_mutex);
+      _mutex.unlock();
+    } else {
+      _mutex.unlock();
+      break;
     }
-    if (sock->isConnected()) {
-      sock->disconnect();
-    }
-    emit disconnected();
-
-    /* Flush the send and receive queues. */
-    flushQueues();
   }
-  setStatus(Disconnected);
-  _run = false;
-  delete sock;
+  if (errmsg) {
+    *errmsg = _errmsg;
+  }
+  return (_status == Success);
 }
 

Modified: trunk/src/control/controlconnection.h
===================================================================
--- trunk/src/control/controlconnection.h	2006-09-30 17:33:01 UTC (rev 1249)
+++ trunk/src/control/controlconnection.h	2006-10-01 06:45:22 UTC (rev 1250)
@@ -2,7 +2,7 @@
  *  Vidalia is distributed under the following license:
  *
  *  Copyright (C) 2006,  Matt Edman, Justin Hipple
- *
+ * 
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
  *  as published by the Free Software Foundation; either version 2
@@ -34,10 +34,11 @@
 #include <QQueue>
 #include <QWaitCondition>
 
+#include "eventtype.h"
 #include "controlsocket.h"
 #include "torevents.h"
 
- 
+
 class ControlConnection : public QThread
 {
   Q_OBJECT
@@ -52,18 +53,20 @@
 
   /** Default constructor. */
   ControlConnection(TorEvents *events = 0);
+  /** Destructor. */
+  ~ControlConnection();
 
   /** Connect to the specified Tor control interface. */
   void connect(QHostAddress addr, quint16 port);
-  /** Cancels a pending connection to Tor. */
+  /** Cancels a pending control connection to Tor. */
   void cancelConnect();
-  /** Disconnect from Tor. */
+  /** Disconnect from Tor's control interface. */
   void disconnect();
   /** Returns the status of the control connection. */
   Status status();
-  /** Sends a control command to Tor and waits for the response. */
+  /** Sends a control command to Tor and waits for the reply. */
   bool send(ControlCommand cmd, ControlReply &reply, QString *errmsg = 0);
-  /** Sends a control command to Tor and does not wait for the response. */
+  /** Sends a control command to Tor and does not wait for a reply. */
   bool send(ControlCommand cmd, QString *errmsg = 0);
 
 signals:
@@ -74,87 +77,82 @@
   /** Emitted when a control connection fails. */
   void connectFailed(QString errmsg);
 
+protected:
+  /** Catches events for the control socket. */
+  bool eventFilter(QObject *obj, QEvent *event);
+
+private slots:
+  /** Called when there is data on the control socket. */
+  void onReadyRead();
+
 private:
+  /** Sets the control connection status. */
+  void setStatus(Status status);
+  /** Connects to Tor's control interface. */
+  bool connect();
   /** Main thread implementation. */
   void run();
-  /** Connections the given socket to Tor. */
-  bool connect(ControlSocket *socket, QString *errmsg);
-  /** Checks if there are any outoing messages waiting. */
-  bool isSendQueueEmpty();
-  /** Processes any messages waiting to be sent. */
-  void processSendQueue(ControlSocket *sock);
-  /** Processes any messages waiting on the control socket. */
-  void processReceiveQueue(ControlSocket *sock);
-  /** Flushes any outstanding waiters in the send or receive queues. */
-  void flushQueues();
-  /** Sets the control connection status */
-  void setStatus(Status status);
 
-  Status _status;      /**< The status of the control connection. */
-  QHostAddress _addr;  /**< Address of Tor's control interface. */
-  quint16 _port;       /**< Port of Tor's control interface. */
-  QMutex _sendMutex;   /**< Mutex for the send queue. */
-  QMutex _recvMutex;   /**< Mutex for the receive queue. */
-  QMutex _connMutex;   /**< Mutex for the status of the connection. */
-  bool _cancelConnect; /**< Set to true when we cancel a pending connect. */
-  /** Set to false when we are to disconnect and stop processing messages. */
-  bool _run;
-  /** Pointer to a previously constructed TorEvents list of event handlers */
-  TorEvents* _events;
+  ControlSocket* _sock; /**< Socket used to communicate with Tor. */
+  TorEvents* _events; /**< Dispatches asynchronous events from Tor. */
+  Status _status; /**< Status of the control connection. */
+  QHostAddress _addr; /**< Address of Tor's control interface. */
+  quint16 _port; /**< Port of Tor's control interface. */
+  QMutex _connMutex; /**< Mutex around the control socket. */
+  QMutex _recvMutex; /**< Mutex around the queue of ReceiveWaiters. */
 
-  
-  /** Status of a message in either the send or receive queue */
-  enum WaiterStatus {
-    Waiting, /**< Message is still in the queue. */
-    Failed,  /**< The send or receive operation failed. */
-    Success  /**< The send or receive operation completed successfully. */
+  /** Private class used to wait for a response to a control command. */
+  class ReceiveWaiter {
+    public:
+      /** Default constructor. */
+      ReceiveWaiter() { _status = Waiting; }
+      /** Waits for and gets the reply from a control command. */
+      bool getResult(ControlReply *reply, QString *errmsg = 0);
+      /** Sets the result and reply from a control command. */
+      void setResult(bool success, ControlReply reply, 
+                     QString errmsg = QString());
+    private:
+      /** Status of the receive waiter. */
+      enum ReceiveStatus { Waiting, Failed, Success } _status;
+      ControlReply _reply; /**< Reply to a previous command. */
+      QMutex _mutex; /**< Mutex around the wait condition. */
+      QWaitCondition _waitCond; /**< Waits for a control rpely. */
+      QString _errmsg; /**< Error message if the reply fails. */
   };
- 
-  /** An item in the send queue. */
-  class SendWaiter 
-  {
+  QQueue<ReceiveWaiter *> _recvQueue; /**< Objects waiting for a reply. */
+  
+  /** Object used to wait for the result of a send operation. */
+  class SendWaiter {
     public:
-      /** Create a new item for the send queue. */
-      SendWaiter(ControlCommand cmd, QWaitCondition *waitCond) 
-      { _command = cmd; _status = Waiting; _waitCond = waitCond; }
-      /** Gets the command waiting to be sent. */
-      ControlCommand command() { return _command; }
-      /** Gets the status of this item in the queue. */
-      WaiterStatus status() { return _status; }
-      /** Returns an error message if the send failed. */
-      QString errorString() { return _errmsg; }
-      /** Sets the result of sending an item in the send queue. */
-      void setResult(WaiterStatus status, QString errmsg = QString())
-      { _status = status; _errmsg = errmsg; _waitCond->wakeAll(); }
+      /** Default constructor. */
+      SendWaiter() { _status = Waiting; }
+      /** Sets the result of the send operation. */
+      void setResult(bool success, QString errmsg = QString());
+      /** Waits for and gets the result of the send operation. */
+      bool getResult(QString *errmsg = 0);
     private:
-      ControlCommand _command; /**< The command to send. */
-      QWaitCondition* _waitCond; /**< Waits for a command to be sent. */
-      WaiterStatus _status;    /**< The status of this queue item. */
-      QString _errmsg;         /**< An error message if the send failed. */
+      /** Status of the send waiter. */
+      enum SenderStatus { Waiting, Failed, Success } _status;
+      QMutex _mutex; /**< Mutex around the wait condition. */
+      QWaitCondition _waitCond; /**< Waits for the send to complete. */
+      QString _errmsg; /**< Error message if the send fails. */
   };
-  QQueue<SendWaiter *> _sendQueue; /** Queue of commands waiting to be sent. */
-
-  /** An item in the receive queue. */
-  class ReceiveWaiter
-  {
+  
+  /** Private event used to push a control command to the socket's thread */
+  class SendCommandEvent : public QEvent {
     public:
-      /** Create a new item for the receive queue. */
-      ReceiveWaiter(ControlReply *reply, QWaitCondition *waitCond) 
-      { _reply = reply; _status = Waiting; _waitCond = waitCond; }
-      /** Gets the status of this item in the queue. */
-      WaiterStatus status() { return _status; }
-      /** Returns an error message if the receive failed. */
-      QString errorString() { return _errmsg; }
-      /** Sets the result of waiting for a control response. */
-      void setResult(WaiterStatus status, ControlReply reply, QString errmsg = QString())
-      { _status = status; *_reply = reply; _errmsg = errmsg; _waitCond->wakeAll(); }
+      /** Constructor. */
+      SendCommandEvent(ControlCommand cmd, SendWaiter *waiter = 0)
+      : QEvent((QEvent::Type)CustomEventType::SendCommandEvent)
+      { _cmd = cmd; _waiter = waiter; }
+      /** Returns the control command to send to Tor. */
+      ControlCommand command() { return _cmd; }
+      /** Returns a waiter (if any) for the result of this send. */
+      SendWaiter* waiter() { return _waiter; }
     private:
-      ControlReply* _reply; /**< The response received */
-      QWaitCondition* _waitCond; /**< Waits for a reply to be received. */
-      WaiterStatus _status; /**< The status of this queue item. */
-      QString _errmsg;      /**< An error message if the receive failed. */
+      ControlCommand _cmd;  /**< Command to send to Tor. */
+      SendWaiter* _waiter; /**< Waiter for the result of this event. */
   };
-  QQueue<ReceiveWaiter *> _recvQueue; /** Queue of waiters for a response. */
 };
 
 #endif

Modified: trunk/src/control/controlsocket.cpp
===================================================================
--- trunk/src/control/controlsocket.cpp	2006-09-30 17:33:01 UTC (rev 1249)
+++ trunk/src/control/controlsocket.cpp	2006-10-01 06:45:22 UTC (rev 1250)
@@ -30,7 +30,16 @@
 
 #include "controlsocket.h"
 
+/** Give up after waiting five seconds for the control socket to connect to
+* Tor. This timeout used to be shorter (three seconds), but some Agnitum
+* OutPost users yelled at us wanting a longer timeout, for some reason. */
+#define CONN_TIMEOUT  5000
 
+/** Timeout reads in 250ms. We can set this to a short value because if there
+* isn't any data to read, we want to return anyway. */
+#define READ_TIMEOUT  250
+
+
 /** Default constructor. */
 ControlSocket::ControlSocket()
 {
@@ -58,7 +67,9 @@
   }
   
   /* Verify that Tor is speaking a protocol version we understand. */
+  blockSignals(true);
   version = protocolVersion();
+  blockSignals(false);
   if (version != Version1) {
     if (errmsg) {
       *errmsg =
@@ -132,7 +143,7 @@
 ControlSocket::sendCommand(ControlCommand cmd, QString *errmsg)
 {
   if (!isConnected()) {
-    return false;
+    return err(errmsg, tr("Control socket is not connected."));
   }
   
   /* Format the control command */
@@ -140,11 +151,8 @@
 
   /* Attempt to send the command to Tor */
   if (write(strCmd.toAscii()) != strCmd.length()) {
-    if (errmsg) {
-      *errmsg = 
-        tr("Error sending control command. [%1]").arg(errorString());
-    }
-    return false;
+    return err(errmsg, tr("Error sending control command. [%1]")
+                                            .arg(errorString()));
   }
   flush();
   return true;

Modified: trunk/src/control/controlsocket.h
===================================================================
--- trunk/src/control/controlsocket.h	2006-09-30 17:33:01 UTC (rev 1249)
+++ trunk/src/control/controlsocket.h	2006-10-01 06:45:22 UTC (rev 1250)
@@ -34,16 +34,7 @@
 #include "controlcommand.h"
 #include "controlreply.h"
 
-/** Give up after waiting five seconds for the control socket to connect to
- * Tor. This timeout used to be shorter (three seconds), but some Agnitum
- * OutPost users yelled at us wanting a longer timeout, for some reason. */
-#define CONN_TIMEOUT  5000
 
-/** Timeout reads in 250ms. We can set this to a short value because if there
- * isn't any data to read, we want to return anyway. */
-#define READ_TIMEOUT  250
-
-
 class ControlSocket : public QTcpSocket
 {
   Q_OBJECT
@@ -71,7 +62,7 @@
   bool readLineData(QString &line, QString *errmsg = 0);
   /** Reads a line of data from the socket (blocking) */
   bool readLine(QString &line, QString *errmsg = 0);
-  
+
 private:
   /** Specifies a version of Tor's Control Protocol */
   enum ProtocolVersion {

Modified: trunk/src/control/eventtype.h
===================================================================
--- trunk/src/control/eventtype.h	2006-09-30 17:33:01 UTC (rev 1249)
+++ trunk/src/control/eventtype.h	2006-10-01 06:45:22 UTC (rev 1250)
@@ -30,13 +30,23 @@
 
 #include <QEvent>
 
+
+/** Custom event type identifiers. */
 namespace CustomEventType {
+  /** Bandwidth update event. */
   const int BandwidthEvent = QEvent::User;
+  /** Circuit status event. */
   const int CircuitEvent   = QEvent::User+1;
+  /** Stream status event. */
   const int StreamEvent    = QEvent::User+2;
+  /** Log message event. */
   const int LogEvent       = QEvent::User+3;
+  /** OR connection status event. */
   const int OrConnEvent    = QEvent::User+4;
+  /** New descriptor event. */
   const int NewDescriptorEvent = QEvent::User+5;
+  /** Control command send event. */
+  const int SendCommandEvent   = QEvent::User+6;
 }
 
 #endif