Changeset 2838 for vidalia/trunk/src

Show
Ignore:
Timestamp:
07/06/08 18:43:48 (5 months ago)
Author:
edmanm
Message:

OK, story time: So, Qt really doesn't like it if you try to send data on a
socket in one thread and read data from it in another thread, even if you do
your locking correctly. Touching the socket in a thread other than the one in
which it was instantiated == death. I started working on a patch that does
away with threads and blocking I/O and simply uses Qt's signals and slots for
dealing with socket operations asynchronously from the main thread. This patch
got really big (already 3000+ lines and I'm not yet done) and isn't something
we want to do when we're trying to put out a 'stable' Vidalia bundle. We'll save
that patch for the next version (or start playing the stable/dev branch game?).

So, here's a hack much like what was removed in r2580, except this actually
works with Qt 4.4. The basic idea is to post an event to the control socket,
which will get delivered to the socket in the correct thread. The event tells
the socket what to send, and the main thread waits for the result of the send.
Sucky, huh?

Location:
vidalia/trunk/src/torcontrol
Files:
5 modified

Legend:

Unmodified
Added
Removed
  • vidalia/trunk/src/torcontrol/CMakeLists.txt

    r2780 r2838  
    2929  routerdescriptor.cpp 
    3030  routerstatus.cpp 
     31  sendcommandevent.cpp 
    3132  serverstatusevent.cpp 
    3233  stream.cpp 
  • vidalia/trunk/src/torcontrol/controlconnection.cpp

    r2695 r2838  
    3535  _status = Unset; 
    3636  _sock = 0; 
     37  _sendWaiter = new SendCommandEvent::SendWaiter(); 
    3738} 
    3839 
     
    4243  /* Exit the event loop */ 
    4344  exit(); 
    44   /* Wait for the thread to finish. */ 
     45  /* Wait for the thread to finish */ 
    4546  wait(); 
     47  /* Clean up after the send waiter */ 
     48  delete _sendWaiter; 
    4649} 
    4750 
     
    201204                        ControlReply &reply, QString *errmsg) 
    202205{ 
    203   ReceiveWaiter w; 
    204206  bool result = false; 
    205207  QString errstr; 
    206    
    207   _connMutex.lock(); 
    208   if (!_sock) { 
    209     _connMutex.unlock(); 
    210     return err(errmsg, tr("Control socket is not connected.")); 
    211   } 
    212   if (_sock->sendCommand(cmd, &errstr)) { 
     208 
     209  _recvMutex.lock(); 
     210  if (send(cmd, &errstr)) { 
    213211    /* Create and enqueue a new receive waiter */ 
    214     _recvQueue.enqueue(&w); 
    215     _connMutex.unlock(); 
     212    ReceiveWaiter *w = new ReceiveWaiter(); 
     213    _recvQueue.enqueue(w); 
     214    _recvMutex.unlock(); 
     215 
     216    /* Wait for and get the result, clean up, and return */ 
     217    result = w->getResult(&reply, &errstr); 
     218    if (!result) 
     219      tc::error("Failed to receive control reply: %1").arg(errstr); 
     220    delete w; 
    216221  } else { 
    217     _connMutex.unlock(); 
    218222    tc::error("Failed to send control command (%1): %2").arg(cmd.keyword()) 
    219223                                                        .arg(errstr); 
    220     return err(errmsg, errstr); 
    221   } 
    222  
    223   /* Wait for and get the result, clean up, and return */ 
    224   result = w.getResult(&reply, &errstr); 
    225   if (!result) { 
    226     tc::error("Failed to receive control reply: %1").arg(errstr); 
    227     if (errmsg) 
    228       *errmsg = errstr; 
    229   } 
     224    _recvMutex.unlock(); 
     225  } 
     226 
     227  if (!result && errmsg) 
     228    *errmsg = errstr; 
    230229  return result; 
    231230} 
    232231 
    233 /** Sends a control command to Tor and returns without waiting for the 
    234  * response. */ 
     232/** Sends a control command to Tor and returns true if the command was sent 
     233 * successfully. Otherwise, returns false and <b>*errmsg</b> (if supplied) 
     234 * will be set. */ 
    235235bool 
    236236ControlConnection::send(const ControlCommand &cmd, QString *errmsg) 
    237237{ 
    238   QMutexLocker locker(&_connMutex); 
    239   if (!_sock) 
     238  _connMutex.lock(); 
     239  if (!_sock || !_sock->isConnected()) { 
     240    _connMutex.unlock(); 
    240241    return err(errmsg, tr("Control socket is not connected."));  
    241   return _sock->sendCommand(cmd, errmsg); 
     242  } 
     243  QCoreApplication::postEvent(_sock, new SendCommandEvent(cmd, _sendWaiter)); 
     244  _connMutex.unlock(); 
     245   
     246  return _sendWaiter->getResult(errmsg); 
    242247} 
    243248 
     
    264269        tc::debug("Control Reply: %1").arg(reply.toString()); 
    265270         
     271        _recvMutex.lock(); 
    266272        if (!_recvQueue.isEmpty()) { 
    267273          waiter = _recvQueue.dequeue(); 
    268274          waiter->setResult(true, reply); 
    269275        } 
     276        _recvMutex.unlock(); 
    270277      } 
    271278    } else { 
     
    312319  delete _connectTimer; 
    313320  _sock = 0; 
     321  _connMutex.unlock(); 
    314322 
    315323  /* If there are any messages waiting for a response, clear them. */ 
     324  if (_sendWaiter->status() == SendCommandEvent::SendWaiter::Waiting) 
     325    _sendWaiter->setResult(false, tr("Control socket is not connected.")); 
     326 
     327  _recvMutex.lock(); 
    316328  while (!_recvQueue.isEmpty()) { 
    317329    ReceiveWaiter *w = _recvQueue.dequeue(); 
     
    319331                 tr("Control socket is not connected.")); 
    320332  } 
    321   _connMutex.unlock(); 
     333  _recvMutex.unlock(); 
    322334} 
    323335 
  • vidalia/trunk/src/torcontrol/controlconnection.h

    r2581 r2838  
    2929#include "controlsocket.h" 
    3030#include "torevents.h" 
     31#include "sendcommandevent.h" 
    3132 
    3233 
     
    99100  quint16 _port; /**< Port of Tor's control interface. */ 
    100101  QMutex _connMutex; /**< Mutex around the control socket. */ 
     102  QMutex _recvMutex; /**< Mutex around the queue of ReceiveWaiters. */ 
    101103  QMutex _statusMutex; /**< Mutex around the connection status value. */ 
    102104  int _connectAttempt; /**< How many times we've tried to connect to Tor while 
     
    123125  }; 
    124126  QQueue<ReceiveWaiter *> _recvQueue; /**< Objects waiting for a reply. */ 
     127  SendCommandEvent::SendWaiter* _sendWaiter; 
    125128}; 
    126129 
  • vidalia/trunk/src/torcontrol/controlsocket.cpp

    r2695 r2838  
    1919 
    2020#include "controlsocket.h" 
     21#include "sendcommandevent.h" 
    2122 
    2223/** Timeout reads in 250ms. We can set this to a short value because if there 
     
    3637{ 
    3738  return (isValid() && state() == QAbstractSocket::ConnectedState); 
     39} 
     40 
     41/** Processes custom events sent to this object (e.g. SendCommandEvents) from 
     42 * other threads. */ 
     43void 
     44ControlSocket::customEvent(QEvent *event) 
     45{ 
     46  if (event->type() == CustomEventType::SendCommandEvent) { 
     47    SendCommandEvent *sce = dynamic_cast<SendCommandEvent *>(event); 
     48    if (! sce) 
     49      return; 
     50 
     51    QString errmsg; 
     52    bool result = sendCommand(sce->command(), &errmsg); 
     53    if (sce->waiter()) 
     54      sce->waiter()->setResult(result, errmsg); 
     55    sce->accept(); 
     56  } 
    3857} 
    3958 
     
    4766bool 
    4867ControlSocket::sendCommand(ControlCommand cmd, QString *errmsg) 
    49 { 
     68{   
    5069  if (!isConnected()) { 
    5170    return err(errmsg, tr("Control socket is not connected.")); 
     
    126145  } 
    127146 
    128   /* The implementation below is (loosely) based on the Java control library from Tor */ 
     147  /* The implementation below is (loosely) based on the Java control library 
     148   * from Tor */ 
    129149  do { 
    130150    /* Read a line of the response */ 
  • vidalia/trunk/src/torcontrol/controlsocket.h

    r2362 r2838  
    4545   
    4646protected: 
     47  /** Processes custom events sent to this object (e.g. SendCommandEvents) 
     48   * from other threads. */ 
     49  void customEvent(QEvent *event); 
    4750  /** Reads line data off the socket in chunks. */ 
    4851  bool readLineData(QString &line, QString *errmsg = 0);