From b30780406eabbcfdc313721e961ef063ab06c8d4 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Sat, 3 Nov 2007 23:28:12 +0000 Subject: [PATCH] - New SignalProxy which is kinda sorta based on the idea of the QxtRPCPeer, though suits more our needs and is suited for high 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 | 2 +- src/client/client.cpp | 22 +- src/client/client.h | 1 + src/common/signalproxy.cpp | 489 +++++++++++++++++++++++++++------ src/common/signalproxy.h | 174 +++++++----- src/common/synchronizer.cpp | 4 +- src/core/coresession.cpp | 2 +- src/core/ircserverhandler.cpp | 37 ++- src/core/sqlitestorage.cpp | 22 +- src/core/sqlitestorage.h | 1 + 10 files changed, 577 insertions(+), 177 deletions(-) diff --git a/src/client/buffertreemodel.cpp b/src/client/buffertreemodel.cpp index 23e36886..1066c894 100644 --- a/src/client/buffertreemodel.cpp +++ b/src/client/buffertreemodel.cpp @@ -286,7 +286,7 @@ bool BufferTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, 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)); diff --git a/src/client/client.cpp b/src/client/client.cpp index f2d1ddd8..1d44fd60 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -133,7 +133,7 @@ SignalProxy *Client::signalProxy() { 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) @@ -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(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()); @@ -242,8 +242,9 @@ void Client::connectToCore(const QVariantMap &conn) { void Client::disconnectFromCore() { socket->close(); - if(clientMode == LocalCore) + if(clientMode == LocalCore) { coreSocketDisconnected(); + } } void Client::coreSocketConnected() { @@ -264,14 +265,15 @@ void Client::coreSocketDisconnected() { /* Clear internal data. Hopefully nothing relies on it at this point. */ _bufferModel->clear(); - foreach(Buffer *buffer, _buffers.values()) { buffer->deleteLater(); } + _buffers.clear(); foreach(NetworkInfo *networkinfo, _networkInfo.values()) { networkinfo->deleteLater(); } + _networkInfo.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(destroyed()), this, SLOT(networkInfoDestroyed())); _networkInfo[netid] = netinfo; } @@ -452,7 +455,16 @@ void Client::updateBufferInfo(BufferInfo id) { void Client::bufferDestroyed() { Buffer *buffer = static_cast(sender()); - _buffers.remove(_buffers.key(buffer)); + uint bufferUid = buffer->uid(); + if(_buffers.contains(bufferUid)) + _buffers.remove(bufferUid); +} + +void Client::networkInfoDestroyed() { + NetworkInfo *netinfo = static_cast(sender()); + uint networkId = netinfo->networkId(); + if(_networkInfo.contains(networkId)) + _networkInfo.remove(networkId); } void Client::recvMessage(const Message &msg) { diff --git a/src/client/client.h b/src/client/client.h index ec9d8d69..349b5ab2 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -125,6 +125,7 @@ private slots: void updateBufferInfo(BufferInfo); void bufferDestroyed(); + void networkInfoDestroyed(); void layoutMsg(); diff --git a/src/common/signalproxy.cpp b/src/common/signalproxy.cpp index 77527427..3238f79c 100644 --- a/src/common/signalproxy.cpp +++ b/src/common/signalproxy.cpp @@ -1,119 +1,448 @@ -/*************************************************************************** - * 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. + ** + ** + ** + ****************************************************************************/ #include "signalproxy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include +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 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 &argTypes = proxy->argTypes(caller, _id); + QVariantList params; + int n = argTypes.size(); + for(int i=0; i::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() { - //qDebug() << "peers:" << peers.count(); - foreach(Connection conn, peers) { - conn.peer->deleteLater(); conn.device->deleteLater(); + QList 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(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; } - 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 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(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 p = obj->metaObject()->method(methodId).parameterTypes(); + QList argTypes; + int ct = p.count(); + for(int i=0; imetaObject()->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 &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) { - //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 ¶ms) { + 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 sigs; - foreach(SignalDesc desc, attachedSignals) { - if(desc.sender != obj) sigs << desc; +} + +void SignalProxy::dataAvailable() { + QIODevice* ioDev = qobject_cast(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 slot; - foreach(SlotDesc desc, attachedSlots) { - if(desc.recv != obj) slot << desc; +} + +void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item) { + QAbstractSocket* sock = qobject_cast(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; } + diff --git a/src/common/signalproxy.h b/src/common/signalproxy.h index f44db23e..1e013f7c 100644 --- a/src/common/signalproxy.h +++ b/src/common/signalproxy.h @@ -1,88 +1,128 @@ -/*************************************************************************** - * 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 - -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. + ** + ** + ** + ****************************************************************************/ + +#ifndef _SIGNALPROXY_H_ +#define _SIGNALPROXY_H_ + +#include +#include +#include +#include +#include +#include +#include + +class ClassIntrospector; 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 peer; - QPointer 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 ¶ms); - 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 &argTypes(QObject* obj, int methodId); + const QByteArray &methodName(QObject* obj, int methodId); + + typedef QHash > ArgHash; + typedef QHash MethodNameHash; + struct ClassInfo { + ArgHash argTypes; + MethodNameHash methodNames; + // QHash 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 _classInfo; + + // we use one introSpector per QObject + QHash _specHash; - SlotDesc(const QByteArray& func, QObject* r, const char* s) : rpcFunction(func), recv(r), slot(s) {} - }; + // RPC function -> (object, slot ID) + typedef QPair MethodId; + typedef QMultiHash SlotHash; + SlotHash _attachedSlots; + - ProxyType type; - QList peers; - QList attachedSignals; - QList attachedSlots; + // Hash of used QIODevices + QHash _peerByteCount; + int _rpcType; + int _maxClients; }; + #endif diff --git a/src/common/synchronizer.cpp b/src/common/synchronizer.cpp index 179cda11..de2b8462 100644 --- a/src/common/synchronizer.cpp +++ b/src/common/synchronizer.cpp @@ -242,7 +242,7 @@ QList Synchronizer::getMethodByName(const QString &methodname) { } void Synchronizer::attach() { - if(proxy()->proxyType() == SignalProxy::Server) + if(proxy()->rpcType() == SignalProxy::Server) 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); } + + diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index 726ddb89..0cec934e 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -38,7 +38,7 @@ CoreSession::CoreSession(UserId uid, Storage *_storage, QObject *parent) _signalProxy(new SignalProxy(SignalProxy::Server, 0, this)), storage(_storage) { - + QSettings s; s.beginGroup(QString("SessionData/%1").arg(user)); mutex.lock(); diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index dab5882d..0fa0372f 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -257,30 +257,29 @@ void IrcServerHandler::handlePing(QString prefix, QStringList params) { void IrcServerHandler::handlePrivmsg(QString prefix, QStringList params) { networkInfo()->updateNickFromMask(prefix); - Q_ASSERT(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) { diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index f148db94..21d67cd1 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -369,10 +369,12 @@ MsgId SqliteStorage::logMessage(Message msg) { if(logMessageQuery->lastError().number() == 19) { addSenderQuery->bindValue(":sender", msg.sender()); addSenderQuery->exec(); + watchQuery(addSenderQuery); logMessageQuery->exec(); - Q_ASSERT(!logMessageQuery->lastError().isValid()); + if(!watchQuery(logMessageQuery)) + return 0; } 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; +} diff --git a/src/core/sqlitestorage.h b/src/core/sqlitestorage.h index c8265d13..74efe5ab 100644 --- a/src/core/sqlitestorage.h +++ b/src/core/sqlitestorage.h @@ -81,6 +81,7 @@ class SqliteStorage : public Storage { private: void initDb(); void createBuffer(UserId user, const QString &network, const QString &buffer); + bool watchQuery(QSqlQuery *query); QSqlQuery *logMessageQuery; QSqlQuery *addSenderQuery; QSqlQuery *getLastMessageIdQuery; -- 2.20.1