- New SignalProxy which is kinda sorta based on the idea of the QxtRPCPeer, though...
authorMarcus Eggenberger <egs@quassel-irc.org>
Sat, 3 Nov 2007 23:28:12 +0000 (23:28 +0000)
committerMarcus Eggenberger <egs@quassel-irc.org>
Sat, 3 Nov 2007 23:28:12 +0000 (23:28 +0000)
performance as in lots of attached signals.
- Had some weird errors on the sqlitestorage so I added some debugging infos there

src/client/buffertreemodel.cpp
src/client/client.cpp
src/client/client.h
src/common/signalproxy.cpp
src/common/signalproxy.h
src/common/synchronizer.cpp
src/core/coresession.cpp
src/core/ircserverhandler.cpp
src/core/sqlitestorage.cpp
src/core/sqlitestorage.h

index 23e3688..1066c89 100644 (file)
@@ -286,7 +286,7 @@ bool BufferTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
   uint bufferId = bufferList.first().second;
 
   // no self merges (would kill us)
   uint bufferId = bufferList.first().second;
 
   // no self merges (would kill us)
-  if(bufferId == parent.data(BufferUidRole).toInt())
+  if(bufferId == parent.data(BufferUidRole).toUInt())
     return false; 
   
   Q_ASSERT(rootItem->childById(netId));
     return false; 
   
   Q_ASSERT(rootItem->childById(netId));
index f2d1ddd..1d44fd6 100644 (file)
@@ -133,7 +133,7 @@ SignalProxy *Client::signalProxy() {
 Client::Client(QObject *parent)
   : QObject(parent),
     socket(0),
 Client::Client(QObject *parent)
   : QObject(parent),
     socket(0),
-    _signalProxy(new SignalProxy(SignalProxy::Client, 0, this)),
+    _signalProxy(new SignalProxy(SignalProxy::Client, this)),
     mainUi(0),
     _bufferModel(0),
     connectedToCore(false)
     mainUi(0),
     _bufferModel(0),
     connectedToCore(false)
@@ -233,7 +233,7 @@ void Client::connectToCore(const QVariantMap &conn) {
     connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData()));
     connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected()));
     connect(sock, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
     connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData()));
     connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected()));
     connect(sock, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
-    connect(signalProxy(), SIGNAL(peerDisconnected()), this, SLOT(coreSocketDisconnected()));
+    connect(signalProxy(), SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
     //connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(coreSocketStateChanged(QAbstractSocket::SocketState)));
     connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError)));
     sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt());
     //connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(coreSocketStateChanged(QAbstractSocket::SocketState)));
     connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError)));
     sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt());
@@ -242,8 +242,9 @@ void Client::connectToCore(const QVariantMap &conn) {
 
 void Client::disconnectFromCore() {
   socket->close();
 
 void Client::disconnectFromCore() {
   socket->close();
-  if(clientMode == LocalCore)
+  if(clientMode == LocalCore) {
     coreSocketDisconnected();
     coreSocketDisconnected();
+  }
 }
 
 void Client::coreSocketConnected() {
 }
 
 void Client::coreSocketConnected() {
@@ -264,14 +265,15 @@ void Client::coreSocketDisconnected() {
 
   /* Clear internal data. Hopefully nothing relies on it at this point. */
   _bufferModel->clear();
 
   /* Clear internal data. Hopefully nothing relies on it at this point. */
   _bufferModel->clear();
-
   foreach(Buffer *buffer, _buffers.values()) {
     buffer->deleteLater();
   }
   foreach(Buffer *buffer, _buffers.values()) {
     buffer->deleteLater();
   }
+  _buffers.clear();
 
   foreach(NetworkInfo *networkinfo, _networkInfo.values()) {
     networkinfo->deleteLater();
   }
 
   foreach(NetworkInfo *networkinfo, _networkInfo.values()) {
     networkinfo->deleteLater();
   }
+  _networkInfo.clear();
 
   coreConnectionInfo.clear();
   sessionData.clear();
 
   coreConnectionInfo.clear();
   sessionData.clear();
@@ -427,6 +429,7 @@ void Client::networkConnected(uint netid) {
   connect(netinfo, SIGNAL(initDone()), this, SLOT(updateCoreConnectionProgress()));
   connect(netinfo, SIGNAL(ircUserInitDone()), this, SLOT(updateCoreConnectionProgress()));
   connect(netinfo, SIGNAL(ircChannelInitDone()), this, SLOT(updateCoreConnectionProgress()));
   connect(netinfo, SIGNAL(initDone()), this, SLOT(updateCoreConnectionProgress()));
   connect(netinfo, SIGNAL(ircUserInitDone()), this, SLOT(updateCoreConnectionProgress()));
   connect(netinfo, SIGNAL(ircChannelInitDone()), this, SLOT(updateCoreConnectionProgress()));
+  connect(netinfo, SIGNAL(destroyed()), this, SLOT(networkInfoDestroyed()));
   _networkInfo[netid] = netinfo;
 }
 
   _networkInfo[netid] = netinfo;
 }
 
@@ -452,7 +455,16 @@ void Client::updateBufferInfo(BufferInfo id) {
 
 void Client::bufferDestroyed() {
   Buffer *buffer = static_cast<Buffer *>(sender());
 
 void Client::bufferDestroyed() {
   Buffer *buffer = static_cast<Buffer *>(sender());
-  _buffers.remove(_buffers.key(buffer));
+  uint bufferUid = buffer->uid();
+  if(_buffers.contains(bufferUid))
+    _buffers.remove(bufferUid);
+}
+
+void Client::networkInfoDestroyed() {
+  NetworkInfo *netinfo = static_cast<NetworkInfo *>(sender());
+  uint networkId = netinfo->networkId();
+  if(_networkInfo.contains(networkId))
+    _networkInfo.remove(networkId);
 }
 
 void Client::recvMessage(const Message &msg) {
 }
 
 void Client::recvMessage(const Message &msg) {
index ec9d8d6..349b5ab 100644 (file)
@@ -125,6 +125,7 @@ private slots:
   void updateBufferInfo(BufferInfo);
 
   void bufferDestroyed();
   void updateBufferInfo(BufferInfo);
 
   void bufferDestroyed();
+  void networkInfoDestroyed();
 
   void layoutMsg();
 
 
   void layoutMsg();
 
index 7752742..3238f79 100644 (file)
-/***************************************************************************
- *   Copyright (C) 2005-07 by The Quassel IRC Development Team             *
- *   devel@quassel-irc.org                                                 *
- *                                                                         *
- *   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 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
+/****************************************************************************
+ **
+ ** Copyright (C) Qxt Foundation. Some rights reserved.
+ **
+ ** This file is part of the QxtNetwork module of the Qt eXTension library
+ **
+ ** This library is free software; you can redistribute it and/or modify it
+ ** under the terms of th Common Public License, version 1.0, as published by
+ ** IBM.
+ **
+ ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
+ ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+ ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
+ ** FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ** You should have received a copy of the CPL along with this file.
+ ** See the LICENSE file and the cpl1.0.txt file included with the source
+ ** distribution for more information. If you did not receive a copy of the
+ ** license, contact the Qxt Foundation.
+ **
+ ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
+ **
+ ****************************************************************************/
 
 #include "signalproxy.h"
 
 #include "signalproxy.h"
+#include <QObject>
+#include <QIODevice>
+#include <QAbstractSocket>
+#include <QHash>
+#include <QMultiHash>
+#include <QList>
+#include <QSet>
+#include <QDebug>
+#include <QMetaMethod>
 
 
-#include <QxtRPCPeer>
+class ClassIntrospector: public QObject {
+// This class MANUALLY implements the necessary parts of QObject.
+// Do NOT add the Q_OBJECT macro. As this class isn't intended
+// for direct use, it doesn't offer any sort of useful meta-object.
+public:
+  ClassIntrospector(SignalProxy* parent, QObject* source);
+  int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
 
 
-SignalProxy::SignalProxy(ProxyType _type, QIODevice *dev, QObject *parent) : QObject(parent), type(_type) {
-  if(dev) {
-    if(type != Client) {
-      qWarning() << tr("Device given for ProxyType == Server, ignoring...").toAscii();
-    } else {
-      addPeer(dev);
+  void attachSignal(int methodId, const QByteArray &func);
+  
+private:
+  SignalProxy* proxy;
+  QObject* caller;
+  QMultiHash<int, QByteArray> rpcFunction;
+};
+
+ClassIntrospector::ClassIntrospector(SignalProxy* parent, QObject* source)
+  : QObject(parent),
+    proxy(parent),
+    caller(source)
+{
+  QObject::connect(source, SIGNAL(destroyed()), parent, SLOT(detachSender()));
+}
+
+int ClassIntrospector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
+  _id = QObject::qt_metacall(_c, _id, _a);
+  if(_id < 0)
+    return _id;
+  if(_c == QMetaObject::InvokeMetaMethod) {
+    if(rpcFunction.contains(_id)) {
+      const QList<int> &argTypes = proxy->argTypes(caller, _id);
+      QVariantList params;
+      int n = argTypes.size();
+      for(int i=0; i<n; i++)
+       params.append(QVariant(argTypes[i], _a[i+1]));
+      QMultiHash<int, QByteArray>::const_iterator funcIter = rpcFunction.constFind(_id);
+      while(funcIter != rpcFunction.constEnd() && funcIter.key() == _id) {
+       proxy->call(funcIter.value(), params);
+       funcIter++;
+      }
     }
     }
+    _id -= 1;
   }
   }
+  return _id;
 }
 
 }
 
+void ClassIntrospector::attachSignal(int methodId, const QByteArray &func) {
+  // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
+  // all connected methodIds are offset by the standard methodCount of QObject
+  if(!rpcFunction.contains(methodId))
+    QMetaObject::connect(caller, methodId, this, QObject::staticMetaObject.methodCount() + methodId);
+
+  QByteArray fn;
+  if(!func.isEmpty()) {
+    fn = QMetaObject::normalizedSignature(func);
+  } else {
+    fn = QByteArray("2") + caller->metaObject()->method(methodId).signature();
+  }
+  rpcFunction.insert(methodId, fn);
+}
+// ====================
+// END INTROSPECTOR
+// ====================
+
+
+// ====================
+//  SignalProxy
+// ====================
+SignalProxy::SignalProxy(QObject* parent)
+  : QObject(parent),
+    _rpcType(Peer),
+    _maxClients(-1)
+{
+}
+
+SignalProxy::SignalProxy(RPCTypes type, QObject* parent)
+  : QObject(parent),
+    _rpcType(type),
+    _maxClients(-1)
+{
+}
+
+SignalProxy::SignalProxy(RPCTypes type, QIODevice* device, QObject* parent)
+  : QObject(parent),
+    _rpcType(type),
+    _maxClients(-1)
+{
+  addPeer(device);
+}  
+
 SignalProxy::~SignalProxy() {
 SignalProxy::~SignalProxy() {
-  //qDebug() << "peers:" << peers.count();
-  foreach(Connection conn, peers) {
-    conn.peer->deleteLater(); conn.device->deleteLater();
+  QList<QObject*> senders = _specHash.keys();
+  foreach(QObject* sender, senders)
+    detachObject(sender);
+}
+
+void SignalProxy::setRPCType(RPCTypes type) {
+  foreach(QIODevice* peer, _peerByteCount.keys()) {
+    if(peer->isOpen()) {
+      qWarning() << "SignalProxy: Cannot change RPC types while connected";
+      return;
+    }
+  }
+  _rpcType = type;
+}
+
+
+SignalProxy::RPCTypes SignalProxy::rpcType() const {
+  return (RPCTypes)(_rpcType);
+}
+
+bool SignalProxy::maxPeersReached() {
+  if(_peerByteCount.empty())
+    return false;
+  if(rpcType() != Server)
+    return true;
+  if(_maxClients == -1)
+    return false;
+
+  return (_maxClients <= _peerByteCount.count());
+}
+
+bool SignalProxy::addPeer(QIODevice* iodev) {
+  if(!iodev)
+    return false;
+  
+  if(_peerByteCount.contains(iodev))
+    return true;
+
+  if(maxPeersReached()) {
+    qWarning("SignalProxy: max peercount reached");
+    return false;
+  }
+     
+  if(!iodev->isOpen())
+    qWarning("SignalProxy::the device you passed is not open!");
+  
+  connect(iodev, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
+  connect(iodev, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
+  
+  QAbstractSocket* sock  = qobject_cast<QAbstractSocket*>(iodev);
+  if(sock) {
+    connect(sock, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
   }
   }
+
+  _peerByteCount[iodev] = 0;
+
+  if(_peerByteCount.count() == 1)
+    emit connected();
+  
+  return true;
 }
 
 }
 
-void SignalProxy::addPeer(QIODevice *dev) {
-  if(type == Client && peers.count()) {
-    qWarning() << tr("Cannot add more than one peer to a SignalProxy in client mode!").toAscii();
+void SignalProxy::removePeerBySender() {
+  // OK we're brutal here... but since it's a private slot we know what we've got connected to it...
+  QIODevice *ioDev = (QIODevice *)(sender());
+  removePeer(ioDev);
+}
+
+void SignalProxy::removePeer(QIODevice* iodev) {
+  if(_peerByteCount.isEmpty()) {
+    qWarning() << "No Peers in use!";
     return;
   }
     return;
   }
-  Connection conn;
-  conn.device = dev;
-  conn.peer = new QxtRPCPeer(dev, QxtRPCPeer::Peer, this);
-  connect(conn.peer, SIGNAL(peerDisconnected()), this, SLOT(socketDisconnected()));
+     
+  if(_rpcType == Server && !iodev) {
+    // disconnect all
+    QList<QIODevice *> peers = _peerByteCount.keys();
+    foreach(QIODevice *peer, peers)
+      removePeer(peer);
+  }
 
 
-  foreach(SlotDesc slot, attachedSlots) {
-    conn.peer->attachSlot(slot.rpcFunction, slot.recv, slot.slot);
+  if(_rpcType != Server && !iodev)
+    iodev = _peerByteCount.keys().first();
+  
+  Q_ASSERT(iodev);
+     
+  if(!_peerByteCount.contains(iodev)) {
+    qWarning() << "SignalProxy: unknown QIODevice" << iodev;
+    return;
   }
   }
-  foreach(SignalDesc sig, attachedSignals) {
-    conn.peer->attachSignal(sig.sender, sig.signal, sig.rpcFunction);
+     
+  // take a last gasp
+  while(true) {
+    QVariant var;
+    if(readDataFromDevice(iodev, _peerByteCount[iodev], var))
+      receivePeerSignal(var);
+    else
+      break;
   }
   }
-  peers.append(conn);
+  _peerByteCount.remove(iodev);
+
+  disconnect(iodev, 0, this, 0);
+  emit peerRemoved(iodev);
 
 
+  if(_peerByteCount.isEmpty())
+    emit disconnected();
 }
 
 }
 
-void SignalProxy::socketDisconnected() {
-  for(int i = 0; i < peers.count(); i++) {
-    Connection conn = peers[i];
-    QAbstractSocket *sock = qobject_cast<QAbstractSocket*>(conn.device);
-    if(!sock) continue;
-    if(sock->state() == QAbstractSocket::UnconnectedState) {
-      peers[i].peer->deleteLater(); peers[i].device->deleteLater();
-      peers.removeAt(i);
-      emit peerDisconnected();
-      i--;
-    }
-  }
+void SignalProxy::setArgTypes(QObject* obj, int methodId) {
+  QList<QByteArray> p = obj->metaObject()->method(methodId).parameterTypes();
+  QList<int> argTypes;
+  int ct = p.count();
+  for(int i=0; i<ct; i++)
+    argTypes.append(QMetaType::type(p.value(i)));
+
+  const QByteArray &className(obj->metaObject()->className());
+  Q_ASSERT(_classInfo.contains(className));
+  Q_ASSERT(!_classInfo[className]->argTypes.contains(methodId));
+  _classInfo[className]->argTypes[methodId] = argTypes;
 }
 
 }
 
-void SignalProxy::attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction) {
-  disconnect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
-  connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
+const QList<int> &SignalProxy::argTypes(QObject *obj, int methodId) {
+  const QByteArray &className(obj->metaObject()->className());
+  Q_ASSERT(_classInfo.contains(className));
+  if(!_classInfo[className]->argTypes.contains(methodId))
+    setArgTypes(obj, methodId);
+  return _classInfo[className]->argTypes[methodId];
+}
+
+void SignalProxy::setMethodName(QObject *obj, int methodId) {
+  const QByteArray &className(obj->metaObject()->className());
+  QByteArray method = obj->metaObject()->method(methodId).signature();
+  method = method.left(method.indexOf('('));
 
 
-  foreach(Connection conn, peers) {
-    conn.peer->attachSignal(sender, signal, rpcFunction);
+  Q_ASSERT(_classInfo.contains(className));
+  Q_ASSERT(!_classInfo[className]->methodNames.contains(methodId));
+  _classInfo[className]->methodNames[methodId] = method;
+}
+
+const QByteArray &SignalProxy::methodName(QObject *obj, int methodId) {
+  QByteArray className(obj->metaObject()->className());
+  Q_ASSERT(_classInfo.contains(className));
+  if(!_classInfo[className]->methodNames.contains(methodId))
+    setMethodName(obj, methodId);
+  return _classInfo[className]->methodNames[methodId];
+}
+
+
+void SignalProxy::createClassInfo(QObject *obj) {
+  QByteArray className(obj->metaObject()->className());
+  if(!_classInfo.contains(className))
+    _classInfo[className] = new ClassInfo();
+}
+
+bool SignalProxy::attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction) {
+  const QMetaObject* meta = sender->metaObject();
+  QByteArray sig(meta->normalizedSignature(signal).mid(1));
+  int methodId = meta->indexOfMethod(sig.constData());
+  if(methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
+    qWarning() << "SignalProxy::attachSignal: No such signal " << signal;
+    return false;
   }
   }
-  attachedSignals.append(SignalDesc(sender, signal, rpcFunction));
+
+  createClassInfo(sender);
+
+  ClassIntrospector* spec;
+  if(_specHash.contains(sender))
+    spec = _specHash[sender];
+  else
+    spec = _specHash[sender] = new ClassIntrospector(this, sender);
+
+  spec->attachSignal(methodId, rpcFunction);
+
+  return true;
 }
 
 }
 
-void SignalProxy::attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot) {
-  disconnect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
-  connect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
 
 
-  foreach(Connection conn, peers) {
-    conn.peer->attachSlot(rpcFunction, recv, slot);
+bool SignalProxy::attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot) {
+  const QMetaObject* meta = recv->metaObject();
+  int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
+  if(methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
+    qWarning() << "SignalProxy::attachSlot: No such slot " << slot;
+    return false;
   }
   }
-  attachedSlots.append(SlotDesc(rpcFunction, recv, slot));
+
+  createClassInfo(recv);
+  
+  QByteArray funcName = QMetaObject::normalizedSignature(rpcFunction.constData());
+  _attachedSlots.insert(funcName, qMakePair(recv, methodId));
+
+  QObject::disconnect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
+  QObject::connect(recv, SIGNAL(destroyed()), this, SLOT(detachSender()));
+  return true;
 }
 
 }
 
+
+void SignalProxy::detachSender() {
+  // this is a slot so we can bypass the QueuedConnection
+  _detachSignals(sender());
+  _detachSlots(sender());
+}
+
+// detachObject/Signals/Slots() can be called as a result of an incomming call
+// this might destroy our the iterator used for delivery
+// thus we wrap the actual disconnection by using QueuedConnections
 void SignalProxy::detachObject(QObject* obj) {
 void SignalProxy::detachObject(QObject* obj) {
-  //Q_ASSERT(false); // not done yet
-  foreach(Connection conn, peers) {
-    conn.peer->detachObject(obj);
+  detachSignals(obj);
+  detachSlots(obj);
+}
+
+void SignalProxy::detachSignals(QObject* sender) {
+  QMetaObject::invokeMethod(this, "_detachSignals",
+                           Qt::QueuedConnection,
+                           Q_ARG(QObject*, sender));
+}
+
+void SignalProxy::_detachSignals(QObject* sender) {
+  if(!_specHash.contains(sender))
+    return;
+  _specHash.take(sender)->deleteLater();
+}
+
+void SignalProxy::detachSlots(QObject* receiver) {
+  QMetaObject::invokeMethod(this, "_detachSlots",
+                           Qt::QueuedConnection,
+                           Q_ARG(QObject*, receiver));
+}
+
+void SignalProxy::_detachSlots(QObject* receiver) {
+  SlotHash::iterator slotIter = _attachedSlots.begin();
+  while(slotIter != _attachedSlots.end()) {
+    if(slotIter.value().first == receiver) {
+      slotIter = _attachedSlots.erase(slotIter);
+    } else
+      slotIter++;
+  }
+}
+
+
+void SignalProxy::call(const char*  signal , QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) {
+  QByteArray sig=QMetaObject::normalizedSignature(signal);
+  QVariantList params;
+  params << p1 << p2 << p3 << p4 << p5
+        << p6 << p7 << p8 << p9;
+  call(sig, params);
+}
+
+void SignalProxy::call(const QByteArray &funcName, const QVariantList &params) {
+  QVariantList packedFunc;
+  packedFunc << funcName;
+  packedFunc << params;
+  foreach(QIODevice* dev, _peerByteCount.keys())
+    writeDataToDevice(dev, QVariant(packedFunc));
+}
+
+void SignalProxy::receivePeerSignal(const QVariant &packedFunc) {
+  QVariantList params(packedFunc.toList());
+  QByteArray funcName = params.takeFirst().toByteArray();
+  int numParams, methodId;
+  QObject* receiver;
+
+  SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
+  while(slot != _attachedSlots.constEnd() && slot.key() == funcName) {
+    receiver = (*slot).first;
+    methodId = (*slot).second;
+    numParams = argTypes(receiver, methodId).count();
+    QGenericArgument args[9];
+    for(int i = 0; i < numParams; i++)
+      args[i] = QGenericArgument(params[i].typeName(), params[i].constData());
+    if(!QMetaObject::invokeMethod(receiver, methodName(receiver, methodId),
+                                 args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])) {
+      qWarning("SignalProxy::receivePeerSignal: invokeMethod for \"%s\" failed ", methodName(receiver, methodId).constData());
+    }
+    slot++;
   }
   }
-  QList<SignalDesc> sigs;
-  foreach(SignalDesc desc, attachedSignals) {
-    if(desc.sender != obj) sigs << desc;
+}
+
+void SignalProxy::dataAvailable() {
+  QIODevice* ioDev = qobject_cast<QIODevice* >(sender());
+  Q_ASSERT(ioDev);
+  if(!_peerByteCount.contains(ioDev)) {
+    qWarning() << "SignalProxy: Unrecognized client object connected to dataAvailable";
+    return;
+  } else {
+    QVariant var;
+    while(readDataFromDevice(ioDev, _peerByteCount[ioDev], var))
+      receivePeerSignal(var);
   }
   }
-  attachedSignals = sigs;
-  QList<SlotDesc> slot;
-  foreach(SlotDesc desc, attachedSlots) {
-    if(desc.recv != obj) slot << desc;
+}
+
+void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item) {
+  QAbstractSocket* sock  = qobject_cast<QAbstractSocket*>(dev);
+  if(!dev->isOpen() || (sock && sock->state()!=QAbstractSocket::ConnectedState)) {
+    qWarning("can't call on a closed device");
+    return;
   }
   }
-  attachedSlots = slot;
-  // FIXME: test this!
+  QByteArray block;
+  QDataStream out(&block, QIODevice::WriteOnly);
+  out.setVersion(QDataStream::Qt_4_2);
+  out << (quint32)0 << item;
+  out.device()->seek(0);
+  out << (quint32)(block.size() - sizeof(quint32));
+  dev->write(block);
 }
 
 }
 
-void SignalProxy::sendSignal(const char *signal, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) {
-  foreach(Connection conn, peers) {
-    conn.peer->call(signal, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) {
+  QDataStream in(dev);
+  in.setVersion(QDataStream::Qt_4_2);
+
+  if(blockSize == 0) {
+    if(dev->bytesAvailable() < (int)sizeof(quint32)) return false;
+    in >> blockSize;
   }
   }
+
+  if(dev->bytesAvailable() < blockSize)
+    return false;
+  in >> item;
+  blockSize = 0;
+  return true;
 }
 
 }
 
+
index f44db23..1e013f7 100644 (file)
-/***************************************************************************
- *   Copyright (C) 2005-07 by The Quassel IRC Development Team             *
- *   devel@quassel-irc.org                                                 *
- *                                                                         *
- *   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 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-
-#ifndef _RPCPEER_H_
-#define _RPCPEER_H_
-
-#include <QtCore>
-
-class QxtRPCPeer;
+/****************************************************************************
+ **
+ ** Copyright (C) Qxt Foundation. Some rights reserved.
+ **
+ ** This file is part of the QxtNetwork module of the Qt eXTension library
+ **
+ ** This library is free software; you can redistribute it and/or modify it
+ ** under the terms of th Common Public License, version 1.0, as published by
+ ** IBM.
+ **
+ ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
+ ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+ ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
+ ** FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ** You should have received a copy of the CPL along with this file.
+ ** See the LICENSE file and the cpl1.0.txt file included with the source
+ ** distribution for more information. If you did not receive a copy of the
+ ** license, contact the Qxt Foundation.
+ **
+ ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
+ **
+ ****************************************************************************/
+
+#ifndef _SIGNALPROXY_H_
+#define _SIGNALPROXY_H_
+
+#include <QObject>
+#include <QList>
+#include <QHash>
+#include <QVariant>
+#include <QPair>
+#include <QString>
+#include <QByteArray>
+
+class ClassIntrospector;
 
 class SignalProxy : public QObject {
   Q_OBJECT
 
 
 class SignalProxy : public QObject {
   Q_OBJECT
 
-  public:
+public:
+  enum RPCTypes {
+    Server,
+    Client,
+    Peer
+  };
 
 
-    enum ProxyType { Client, Server };
+  SignalProxy(QObject* parent);
+  SignalProxy(RPCTypes type, QObject* parent);
+  SignalProxy(RPCTypes type, QIODevice* device, QObject* parent);
+  virtual ~SignalProxy();
 
 
-    SignalProxy(ProxyType type, QIODevice *device = 0, QObject *parent = 0);
-    ~SignalProxy();
+  void setRPCType(RPCTypes type);
+  RPCTypes rpcType() const;
 
 
-  ProxyType proxyType() const { return type; }
-    void attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction = QByteArray());
-    void attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot);
-
-  public slots:
-    void addPeer(QIODevice *device);
-
-    void sendSignal(const char *signal, QVariant p1 = QVariant(), QVariant p2 = QVariant(), QVariant p3 = QVariant(), QVariant p4 = QVariant(),
-              QVariant p5 = QVariant(), QVariant p6 = QVariant(), QVariant p7 = QVariant(), QVariant p8 = QVariant(), QVariant p9 = QVariant());
+  bool maxPeersReached();
   
   
-    //void detachSender();
-    void detachObject(QObject *);
-
-  signals:
-    void peerDisconnected();
+  bool addPeer(QIODevice *iodev);
+  void removePeer(QIODevice *iodev = 0);
 
 
-  private slots:
-    void socketDisconnected();
+  bool attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction = QByteArray());
+  bool attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot);
 
 
-  private:
-    struct Connection {
-      QPointer<QxtRPCPeer> peer;
-      QPointer<QIODevice> device;
-    };
+  void detachObject(QObject *obj);
+  void detachSignals(QObject *sender);
+  void detachSlots(QObject *receiver);
+  
+  void call(const char *signal , QVariant p1, QVariant p2, QVariant p3, QVariant p4,
+           QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9);
+  void call(const QByteArray &funcName, const QVariantList &params);
 
 
-    struct SignalDesc {
-      QObject *sender;
-      QByteArray signal;
-      QByteArray rpcFunction;
+  static void writeDataToDevice(QIODevice *dev, const QVariant &item);
+  static bool readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item);
+  
+  const QList<int> &argTypes(QObject* obj, int methodId);
+  const QByteArray &methodName(QObject* obj, int methodId);
+  
+  typedef QHash<int, QList<int> > ArgHash;
+  typedef QHash<int, QByteArray> MethodNameHash;
+  struct ClassInfo {
+    ArgHash argTypes;
+    MethodNameHash methodNames;
+    // QHash<int, int> syncMap
+  };
+  
+private slots:
+  void dataAvailable();
+  void detachSender();
+  void removePeerBySender();
+
+signals:
+  void peerRemoved(QIODevice* obj);
+  void connected();
+  void disconnected();
+  
+private:
+  void createClassInfo(QObject *obj);
+  void setArgTypes(QObject* obj, int methodId);
+  void setMethodName(QObject* obj, int methodId);
+  
+  void receivePeerSignal(const QVariant &packedFunc);
 
 
-      SignalDesc(QObject *sndr, const char *sig, const QByteArray &func) : sender(sndr), signal(sig), rpcFunction(func) {}
-    };
+  void _detachSignals(QObject *sender);
+  void _detachSlots(QObject *receiver);
 
 
-    struct SlotDesc {
-      QByteArray rpcFunction;
-      QObject *recv;
-      QByteArray slot;
+  // containg a list of argtypes for fast access
+  QHash<QByteArray, ClassInfo*> _classInfo;
+  
+  // we use one introSpector per QObject
+  QHash<QObject*, ClassIntrospector*> _specHash;
 
 
-      SlotDesc(const QByteArray& func, QObject* r, const char* s) : rpcFunction(func), recv(r), slot(s) {}
-    };
+  // RPC function -> (object, slot ID)
+  typedef QPair<QObject*, int> MethodId;
+  typedef QMultiHash<QByteArray, MethodId> SlotHash;
+  SlotHash _attachedSlots;
+  
 
 
-    ProxyType type;
-    QList<Connection> peers;
-    QList<SignalDesc> attachedSignals;
-    QList<SlotDesc> attachedSlots;
+  // Hash of used QIODevices
+  QHash<QIODevice*, quint32> _peerByteCount;
 
 
+  int _rpcType;
+  int _maxClients;
 };
 
 
 
 };
 
 
 
+
 #endif
 #endif
index 179cda1..de2b846 100644 (file)
@@ -242,7 +242,7 @@ QList<QMetaMethod> Synchronizer::getMethodByName(const QString &methodname) {
 }
 
 void Synchronizer::attach() {
 }
 
 void Synchronizer::attach() {
-  if(proxy()->proxyType() == SignalProxy::Server)
+  if(proxy()->rpcType() == SignalProxy::Server)
     attachAsMaster();
   else
     attachAsSlave();
     attachAsMaster();
   else
     attachAsSlave();
@@ -341,3 +341,5 @@ bool Synchronizer::setInitValue(const QString &property, const QVariant &value)
   QGenericArgument param = QGenericArgument(paramtype, &value);
   return QMetaObject::invokeMethod(parent(), handlername.toAscii(), param);
 }
   QGenericArgument param = QGenericArgument(paramtype, &value);
   return QMetaObject::invokeMethod(parent(), handlername.toAscii(), param);
 }
+
+
index 726ddb8..0cec934 100644 (file)
@@ -38,7 +38,7 @@ CoreSession::CoreSession(UserId uid, Storage *_storage, QObject *parent)
     _signalProxy(new SignalProxy(SignalProxy::Server, 0, this)),
     storage(_storage)
 {
     _signalProxy(new SignalProxy(SignalProxy::Server, 0, this)),
     storage(_storage)
 {
-
+  
   QSettings s;
   s.beginGroup(QString("SessionData/%1").arg(user));
   mutex.lock();
   QSettings s;
   s.beginGroup(QString("SessionData/%1").arg(user));
   mutex.lock();
index dab5882..0fa0372 100644 (file)
@@ -257,30 +257,29 @@ void IrcServerHandler::handlePing(QString prefix, QStringList params) {
 
 void IrcServerHandler::handlePrivmsg(QString prefix, QStringList params) {
   networkInfo()->updateNickFromMask(prefix);
 
 void IrcServerHandler::handlePrivmsg(QString prefix, QStringList params) {
   networkInfo()->updateNickFromMask(prefix);
-  Q_ASSERT(params.count() >= 2);
   if(params.count()<2)
   if(params.count()<2)
-    emit displayMsg(Message::Plain, params[0], "", prefix);
-  else {
-    // it's possible to pack multiple privmsgs into one param using ctcp
-    QStringList messages = server->ctcpHandler()->parse(CtcpHandler::CtcpQuery, prefix, params[0], params[1]);
-
-    // are we the target or is it a channel?
-    if(networkInfo()->isMyNick(params[0])) {
-      foreach(QString message, messages) {
-        if(!message.isEmpty()) {
-          emit displayMsg(Message::Plain, "", message, prefix, Message::PrivMsg);
-        }
+    params << QString("");
+  
+  // it's possible to pack multiple privmsgs into one param using ctcp
+  QStringList messages = server->ctcpHandler()->parse(CtcpHandler::CtcpQuery, prefix, params[0], params[1]);
+  
+  // are we the target or is it a channel?
+  if(networkInfo()->isMyNick(params[0])) {
+    foreach(QString message, messages) {
+      if(!message.isEmpty()) {
+       emit displayMsg(Message::Plain, "", message, prefix, Message::PrivMsg);
       }
       }
-   
-    } else {
-      Q_ASSERT(isChannelName(params[0]));  // should be channel!
-      foreach(QString message, messages) {
-        if(!message.isEmpty()) {
-          emit displayMsg(Message::Plain, params[0], message, prefix);
-        }
+    }
+    
+  } else {
+    Q_ASSERT(isChannelName(params[0]));  // should be channel!
+    foreach(QString message, messages) {
+      if(!message.isEmpty()) {
+       emit displayMsg(Message::Plain, params[0], message, prefix);
       }
     }
   }
       }
     }
   }
+
 }
 
 void IrcServerHandler::handleQuit(QString prefix, QStringList params) {
 }
 
 void IrcServerHandler::handleQuit(QString prefix, QStringList params) {
index f148db9..21d67cd 100644 (file)
@@ -369,10 +369,12 @@ MsgId SqliteStorage::logMessage(Message msg) {
     if(logMessageQuery->lastError().number() == 19) { 
       addSenderQuery->bindValue(":sender", msg.sender());
       addSenderQuery->exec();
     if(logMessageQuery->lastError().number() == 19) { 
       addSenderQuery->bindValue(":sender", msg.sender());
       addSenderQuery->exec();
+      watchQuery(addSenderQuery);
       logMessageQuery->exec();
       logMessageQuery->exec();
-      Q_ASSERT(!logMessageQuery->lastError().isValid());
+      if(!watchQuery(logMessageQuery))
+       return 0;
     } else {
     } else {
-      qDebug() << "unhandled DB Error in logMessage(): Number:" << logMessageQuery->lastError().number() << "ErrMsg:" << logMessageQuery->lastError().text();
+      watchQuery(logMessageQuery);
     }
   }
 
     }
   }
 
@@ -503,4 +505,18 @@ void SqliteStorage::importOldBacklog() {
 }
 
 
 }
 
 
-
+bool SqliteStorage::watchQuery(QSqlQuery *query) {
+  if(query->lastError().isValid()) {
+    qWarning() << "unhandled Error in QSqlQuery!";
+    qWarning() << "                  last Query:" << query->lastQuery();
+    qWarning() << "              executed Query:" << query->executedQuery();
+    qWarning() << "                bound Values:" << query->boundValues();
+    qWarning() << "                Error Number:" << query->lastError().number();
+    qWarning() << "               Error Message:" << query->lastError().text();
+    qWarning() << "              Driver Message:" << query->lastError().driverText();
+    qWarning() << "                  DB Message:" << query->lastError().databaseText();
+    
+    return false;
+  }
+  return true;
+}
index c8265d1..74efe5a 100644 (file)
@@ -81,6 +81,7 @@ class SqliteStorage : public Storage {
   private:
     void initDb();
     void createBuffer(UserId user, const QString &network, const QString &buffer);
   private:
     void initDb();
     void createBuffer(UserId user, const QString &network, const QString &buffer);
+    bool watchQuery(QSqlQuery *query);
     QSqlQuery *logMessageQuery;
     QSqlQuery *addSenderQuery;
     QSqlQuery *getLastMessageIdQuery;
     QSqlQuery *logMessageQuery;
     QSqlQuery *addSenderQuery;
     QSqlQuery *getLastMessageIdQuery;