From 88b350153eb364853e75d237d3eed2dfaf839d59 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Wed, 15 Jun 2011 00:45:39 +0200 Subject: [PATCH] Changing the behavior how Quassel Events are processed. They are no longer scheduled individualy but processed immediately when they are generated. this should prohibit quassel from messing with the space time continuum. --- src/common/eventmanager.cpp | 60 +++++++++++++++----------- src/common/eventmanager.h | 10 +---- src/core/corenetwork.cpp | 3 +- src/core/corenetwork.h | 3 ++ src/core/coresession.cpp | 19 +++++--- src/core/coresession.h | 7 ++- src/core/coresessioneventprocessor.cpp | 9 ++-- src/core/coresessioneventprocessor.h | 3 ++ src/core/ctcpparser.cpp | 6 ++- src/core/ctcpparser.h | 3 ++ src/core/eventstringifier.cpp | 9 ++-- src/core/eventstringifier.h | 4 +- src/core/ircparser.cpp | 8 ++-- src/core/ircparser.h | 3 ++ 14 files changed, 91 insertions(+), 56 deletions(-) diff --git a/src/common/eventmanager.cpp b/src/common/eventmanager.cpp index 3fe581cf..2709b557 100644 --- a/src/common/eventmanager.cpp +++ b/src/common/eventmanager.cpp @@ -27,13 +27,21 @@ #include "event.h" #include "ircevent.h" -EventManager::EventManager(QObject *parent) : QObject(parent) { - -} - -EventManager::~EventManager() { - // pending events won't be delivered anymore, but we do need to delete them - qDeleteAll(_eventQueue); +// ============================================================ +// QueuedEvent +// ============================================================ +class QueuedQuasselEvent : public QEvent { +public: + QueuedQuasselEvent(Event *event) + : QEvent(QEvent::User), event(event) {} + Event *event; +}; + +// ============================================================ +// EventManager +// ============================================================ +EventManager::EventManager(QObject *parent) + : QObject(parent) { } QMetaEnum EventManager::eventEnum() const { @@ -157,31 +165,35 @@ void EventManager::registerEventHandler(QList events, QObject *object } } -// not threadsafe! if we should want that, we need to add a mutexed queue somewhere in this general area. -void EventManager::sendEvent(Event *event) { - // qDebug() << "Sending" << event; - _eventQueue.append(event); - if(_eventQueue.count() == 1) // we're not currently processing another event - processEvents(); +void EventManager::postEvent(Event *event) { + if(sender() && sender()->thread() != this->thread()) { + QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event); + QCoreApplication::postEvent(this, queuedEvent); + } else { + if(_eventQueue.isEmpty()) + // we're currently not processing events + processEvent(event); + else + _eventQueue.append(event); + } } void EventManager::customEvent(QEvent *event) { if(event->type() == QEvent::User) { - processEvents(); + QueuedQuasselEvent *queuedEvent = static_cast(event); + processEvent(queuedEvent->event); event->accept(); } } -void EventManager::processEvents() { - // we only process one event at a time for now, and let Qt's own event processing come in between - if(_eventQueue.isEmpty()) - return; - dispatchEvent(_eventQueue.first()); - _eventQueue.removeFirst(); - if(_eventQueue.count()) - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - else - emit eventQueueEmptied(); +void EventManager::processEvent(Event *event) { + Q_ASSERT(_eventQueue.isEmpty()); + dispatchEvent(event); + // dispatching the event might cause new events to be generated. we process those afterwards. + while(!_eventQueue.isEmpty()) { + dispatchEvent(_eventQueue.first()); + _eventQueue.removeFirst(); + } } void EventManager::dispatchEvent(Event *event) { diff --git a/src/common/eventmanager.h b/src/common/eventmanager.h index 9b6e5a04..e01b594d 100644 --- a/src/common/eventmanager.h +++ b/src/common/eventmanager.h @@ -111,7 +111,6 @@ public: }; EventManager(QObject *parent = 0); - virtual ~EventManager(); EventType eventTypeByName(const QString &name) const; EventType eventGroupByName(const QString &name) const; @@ -133,13 +132,9 @@ public slots: //! Send an event to the registered handlers /** The EventManager takes ownership of the event and will delete it once it's processed. - NOTE: This method is not threadsafe! @param event The event to be dispatched */ - void sendEvent(Event *event); - -signals: - void eventQueueEmptied(); + void postEvent(Event *event); protected: virtual void customEvent(QEvent *event); @@ -172,7 +167,7 @@ private: int findEventType(const QString &methodSignature, const QString &methodPrefix) const; - void processEvents(); + void processEvent(Event *event); void dispatchEvent(Event *event); //! @return the EventType enum @@ -181,7 +176,6 @@ private: HandlerHash _registeredHandlers; HandlerHash _registeredFilters; mutable QMetaEnum _enum; - QList _eventQueue; }; diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 534199e3..d18ddca5 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -77,6 +77,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) connect(&socket, SIGNAL(encrypted()), this, SLOT(socketInitialized())); connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); #endif + connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); } CoreNetwork::~CoreNetwork() { @@ -330,7 +331,7 @@ void CoreNetwork::socketHasData() { #else event->setTimestamp(QDateTime::currentDateTime().toUTC()); #endif - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); } } diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h index f4744d92..c1f96f20 100644 --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@ -43,6 +43,7 @@ class CoreIdentity; class CoreUserInputHandler; class CoreIgnoreListManager; +class Event; class CoreNetwork : public Network { SYNCABLE_OBJECT @@ -144,6 +145,8 @@ signals: void quitRequested(NetworkId networkId); void sslErrors(const QVariant &errorData); + void newEvent(Event *event); + protected: inline virtual IrcChannel *ircChannelFactory(const QString &channelname) { return new CoreIrcChannel(channelname, this); } inline virtual IrcUser *ircUserFactory(const QString &hostmask) { return new CoreIrcUser(hostmask, this); } diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index 0173a957..3fd539ea 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -68,6 +68,7 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent) _ctcpParser(new CtcpParser(this)), _ircParser(new IrcParser(this)), scriptEngine(new QScriptEngine(this)), + _processMessages(false), _ignoreListManager(this) { SignalProxy *p = signalProxy(); @@ -96,7 +97,6 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent) loadSettings(); initScriptEngine(); - connect(eventManager(), SIGNAL(eventQueueEmptied()), SLOT(processMessages())); eventManager()->registerObject(ircParser(), EventManager::NormalPriority); eventManager()->registerObject(sessionEventProcessor(), EventManager::HighPriority); // needs to process events *before* the stringifier! eventManager()->registerObject(ctcpParser(), EventManager::NormalPriority); @@ -223,9 +223,6 @@ void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg) { CoreNetwork *net = network(bufinfo.networkId()); if(net) { net->userInput(bufinfo, msg); - // FIXME as soon as user input is event-based - // until then, user input doesn't trigger a message queue flush! - processMessages(); } else { qWarning() << "Trying to send to unconnected network:" << msg; } @@ -250,6 +247,10 @@ void CoreSession::recvMessageFromServer(NetworkId networkId, Message::Type type, return; _messageQueue << rawMsg; + if(!_processMessages) { + _processMessages = true; + QCoreApplication::postEvent(this, new ProcessMessagesEvent()); + } } void CoreSession::recvStatusMsgFromServer(QString msg) { @@ -270,10 +271,15 @@ QList CoreSession::buffers() const { return Core::requestBuffers(user()); } -void CoreSession::processMessages() { - if(_messageQueue.isEmpty()) +void CoreSession::customEvent(QEvent *event) { + if(event->type() != QEvent::User) return; + processMessages(); + event->accept(); +} + +void CoreSession::processMessages() { if(_messageQueue.count() == 1) { const RawMessage &rawMsg = _messageQueue.first(); bool createBuffer = !(rawMsg.flags & Message::Redirected); @@ -329,6 +335,7 @@ void CoreSession::processMessages() { emit displayMsg(messages[i]); } } + _processMessages = false; _messageQueue.clear(); } diff --git a/src/core/coresession.h b/src/core/coresession.h index 9d8ba559..8b4305e3 100644 --- a/src/core/coresession.h +++ b/src/core/coresession.h @@ -150,12 +150,14 @@ signals: void networkRemoved(NetworkId); void networkDisconnected(NetworkId); +protected: + virtual void customEvent(QEvent *event); + private slots: void removeClient(QIODevice *dev); void recvStatusMsgFromServer(QString msg); void recvMessageFromServer(NetworkId networkId, Message::Type, BufferInfo::Type, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None); - void processMessages(); void destroyNetwork(NetworkId); @@ -169,6 +171,8 @@ private slots: void saveSessionState() const; private: + void processMessages(); + void loadSettings(); void initScriptEngine(); @@ -200,6 +204,7 @@ private: QScriptEngine *scriptEngine; QList _messageQueue; + bool _processMessages; CoreIgnoreListManager _ignoreListManager; }; diff --git a/src/core/coresessioneventprocessor.cpp b/src/core/coresessioneventprocessor.cpp index 59565a08..2eb7f6d9 100644 --- a/src/core/coresessioneventprocessor.cpp +++ b/src/core/coresessioneventprocessor.cpp @@ -35,6 +35,7 @@ CoreSessionEventProcessor::CoreSessionEventProcessor(CoreSession *session) _coreSession(session) { connect(coreSession(), SIGNAL(networkDisconnected(NetworkId)), this, SLOT(destroyNetsplits(NetworkId))); + connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); } bool CoreSessionEventProcessor::checkParamCount(IrcEvent *e, int minParams) { @@ -63,7 +64,7 @@ void CoreSessionEventProcessor::tryNextNick(NetworkEvent *e, const QString &errn MessageEvent *msgEvent = new MessageEvent(Message::Error, e->network(), tr("No free and valid nicks in nicklist found. use: /nick to continue"), QString(), QString(), Message::None, e->timestamp()); - coreSession()->eventManager()->sendEvent(msgEvent); + emit newEvent(msgEvent); return; } else { nextNick = errnick + "_"; @@ -747,12 +748,12 @@ void CoreSessionEventProcessor::handleNetsplitJoin(Network *net, ircChannel->joinIrcUsers(ircUsers, newModes); NetworkSplitEvent *event = new NetworkSplitEvent(EventManager::NetworkSplitJoin, net, channel, newUsers, quitMessage); - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); } void CoreSessionEventProcessor::handleNetsplitQuit(Network *net, const QString &channel, const QStringList &users, const QString& quitMessage) { NetworkSplitEvent *event = new NetworkSplitEvent(EventManager::NetworkSplitQuit, net, channel, users, quitMessage); - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); foreach(QString user, users) { IrcUser *iu = net->ircUser(nickFromMask(user)); if(iu) @@ -784,7 +785,7 @@ void CoreSessionEventProcessor::handleEarlyNetsplitJoin(Network *net, const QStr ircChannel->joinIrcUsers(ircUsers, newModes); foreach(NetworkEvent *event, events) { event->setFlag(EventManager::Fake); // ignore this in here! - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); } } diff --git a/src/core/coresessioneventprocessor.h b/src/core/coresessioneventprocessor.h index e0219e3f..b2ba39c2 100644 --- a/src/core/coresessioneventprocessor.h +++ b/src/core/coresessioneventprocessor.h @@ -94,6 +94,9 @@ public: Q_INVOKABLE void handleCtcpVersion(CtcpEvent *event); Q_INVOKABLE void defaultHandler(const QString &ctcpCmd, CtcpEvent *event); +signals: + void newEvent(Event *event); + protected: bool checkParamCount(IrcEvent *event, int minParams); inline CoreNetwork *coreNetwork(NetworkEvent *e) const { return qobject_cast(e->network()); } diff --git a/src/core/ctcpparser.cpp b/src/core/ctcpparser.cpp index 1aa8c0ea..f4177c6e 100644 --- a/src/core/ctcpparser.cpp +++ b/src/core/ctcpparser.cpp @@ -39,6 +39,8 @@ CtcpParser::CtcpParser(CoreSession *coreSession, QObject *parent) QByteArray XQUOTE = QByteArray("\134"); _ctcpXDelimDequoteHash[XQUOTE + XQUOTE] = XQUOTE; _ctcpXDelimDequoteHash[XQUOTE + QByteArray("a")] = XDELIM; + + connect(this, SIGNAL(newEvent(Event *)), _coreSession->eventManager(), SLOT(postEvent(Event *))); } void CtcpParser::displayMsg(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender, @@ -49,7 +51,7 @@ void CtcpParser::displayMsg(NetworkEvent *event, Message::Type msgType, const QS MessageEvent *msgEvent = new MessageEvent(msgType, event->network(), msg, sender, target, msgFlags); msgEvent->setTimestamp(event->timestamp()); - coreSession()->eventManager()->sendEvent(msgEvent); + emit newEvent(msgEvent); } QByteArray CtcpParser::lowLevelQuote(const QByteArray &message) { @@ -193,7 +195,7 @@ void CtcpParser::parse(IrcEventRawMessage *e, Message::Type messagetype) { ctcptype, "INVALID", QString(), e->timestamp(), uuid); ctcpEvents << flushEvent; foreach(CtcpEvent *event, ctcpEvents) { - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); } } diff --git a/src/core/ctcpparser.h b/src/core/ctcpparser.h index 10915969..e68e737b 100644 --- a/src/core/ctcpparser.h +++ b/src/core/ctcpparser.h @@ -46,6 +46,9 @@ public: Q_INVOKABLE void sendCtcpEvent(CtcpEvent *event); +signals: + void newEvent(Event *event); + protected: inline CoreNetwork *coreNetwork(NetworkEvent *e) const { return qobject_cast(e->network()); } diff --git a/src/core/eventstringifier.cpp b/src/core/eventstringifier.cpp index 1b817afd..7276edfd 100644 --- a/src/core/eventstringifier.cpp +++ b/src/core/eventstringifier.cpp @@ -28,7 +28,7 @@ EventStringifier::EventStringifier(CoreSession *parent) : BasicHandler("handleCt _coreSession(parent), _whois(false) { - + connect(this, SIGNAL(newMessageEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); } void EventStringifier::displayMsg(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender, @@ -37,7 +37,8 @@ void EventStringifier::displayMsg(NetworkEvent *event, Message::Type msgType, co return; MessageEvent *msgEvent = createMessageEvent(event, msgType, msg, sender, target, msgFlags); - sendMessageEvent(msgEvent); + //sendMessageEvent(msgEvent); + emit newMessageEvent(msgEvent); } MessageEvent *EventStringifier::createMessageEvent(NetworkEvent *event, Message::Type msgType, const QString &msg, const QString &sender, @@ -47,10 +48,6 @@ MessageEvent *EventStringifier::createMessageEvent(NetworkEvent *event, Message: return msgEvent; } -void EventStringifier::sendMessageEvent(MessageEvent *event) { - coreSession()->eventManager()->sendEvent(event); -} - bool EventStringifier::checkParamCount(IrcEvent *e, int minParams) { if(e->params().count() < minParams) { if(e->type() == EventManager::IrcEventNumeric) { diff --git a/src/core/eventstringifier.h b/src/core/eventstringifier.h index 1c317c86..69d11c34 100644 --- a/src/core/eventstringifier.h +++ b/src/core/eventstringifier.h @@ -109,9 +109,11 @@ public slots: const QString &target = QString(), Message::Flags msgFlags = Message::None); +signals: + void newMessageEvent(Event *event); + private: bool checkParamCount(IrcEvent *event, int minParams); - void sendMessageEvent(MessageEvent *event); CoreSession *_coreSession; bool _whois; diff --git a/src/core/ircparser.cpp b/src/core/ircparser.cpp index b372e351..605dd2e8 100644 --- a/src/core/ircparser.cpp +++ b/src/core/ircparser.cpp @@ -34,7 +34,7 @@ IrcParser::IrcParser(CoreSession *session) : QObject(session), _coreSession(session) { - + connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *))); } bool IrcParser::checkParamCount(const QString &cmd, const QList ¶ms, int minParams) { @@ -136,16 +136,18 @@ void IrcParser::processNetworkIncoming(NetworkDataEvent *e) { QList events; EventManager::EventType type = EventManager::Invalid; - // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this! uint num = cmd.toUInt(); if(num > 0) { + // numeric reply if(params.count() == 0) { qWarning() << "Message received from server violates RFC and is ignored!" << msg; return; } + // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this! target = net->serverDecode(params.takeFirst()); type = EventManager::IrcEventNumeric; } else { + // any other irc command QString typeName = QLatin1String("IrcEvent") + cmd.at(0).toUpper() + cmd.mid(1).toLower(); type = eventManager()->eventTypeByName(typeName); if(type == EventManager::Invalid) { @@ -300,6 +302,6 @@ void IrcParser::processNetworkIncoming(NetworkDataEvent *e) { } foreach(Event *event, events) { - coreSession()->eventManager()->sendEvent(event); + emit newEvent(event); } } diff --git a/src/core/ircparser.h b/src/core/ircparser.h index c16c8980..402e656b 100644 --- a/src/core/ircparser.h +++ b/src/core/ircparser.h @@ -37,6 +37,9 @@ public: inline CoreSession *coreSession() const { return _coreSession; } inline EventManager *eventManager() const { return coreSession()->eventManager(); } +signals: + void newEvent(Event *); + protected: Q_INVOKABLE void processNetworkIncoming(NetworkDataEvent *e); -- 2.20.1