/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2015, 2018 Attila Molnar <attilamolnar@hush.com>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
{
public:
- typedef std::vector<ModuleEventListener*> SubscriberList;
+ struct Comp
+ {
+ bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
+ };
+
+ struct ElementComp
+ {
+ bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
+ };
+
+ typedef insp::flat_multiset<ModuleEventListener*, Comp, ElementComp> SubscriberList;
/** Constructor
* @param mod Module providing the event(s)
*/
const SubscriberList& GetSubscribers() const { return prov->subscribers; }
- friend class ModuleEventListener;
+ /** Subscribes a listener to this event.
+ * @param subscriber The listener to subscribe.
+ */
+ void Subscribe(ModuleEventListener* subscriber)
+ {
+ subscribers.insert(subscriber);
+ OnSubscribe(subscriber);
+ }
+
+ /** Unsubscribes a listener from this event.
+ * @param subscriber The listener to unsubscribe.
+ */
+ void Unsubscribe(ModuleEventListener* subscriber)
+ {
+ subscribers.erase(subscriber);
+ OnUnsubscribe(subscriber);
+ }
private:
void OnCapture() CXX11_OVERRIDE
subscribers.clear();
}
+ /** Called when a listener subscribes to this event.
+ * @param subscriber The listener which subscribed.
+ */
+ virtual void OnSubscribe(ModuleEventListener* subscriber) { }
+
+ /** Called when a listener unsubscribes from this event.
+ * @param subscriber The listener which unsubscribed.
+ */
+ virtual void OnUnsubscribe(ModuleEventListener* subscriber) { }
+
/** Reference to the active provider for this event. In case multiple event providers
* exist for the same event, only one of them contains the list of subscribers.
* To handle the case when we are not the ones with the list, we get it from the provider
*/
dynamic_reference_nocheck<ModuleEventProvider> prov;
+ const unsigned int eventpriority;
+
/** Called by the dynref when the event provider becomes available
*/
void OnCapture() CXX11_OVERRIDE
{
- prov->subscribers.push_back(this);
+ prov->Subscribe(this);
}
public:
+ static const unsigned int DefaultPriority = 100;
+
/** Constructor
* @param mod Module subscribing
* @param eventid Identifier of the event to subscribe to
+ * @param eventprio The priority to give this event listener
*/
- ModuleEventListener(Module* mod, const std::string& eventid)
+ ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
: prov(mod, eventid)
+ , eventpriority(eventprio)
{
prov.SetCaptureHook(this);
// If the dynamic_reference resolved at construction our capture handler wasn't called
~ModuleEventListener()
{
if (prov)
- stdalgo::erase(prov->subscribers, this);
+ prov->Unsubscribe(this);
}
+
+ /** Retrieves the module which created this listener. */
+ const Module* GetModule() const { return prov.creator; }
+
+ /** Retrieves the priority of this event. */
+ unsigned int GetPriority() const { return eventpriority; }
};
+inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
+{
+ return (lhs->GetPriority() < rhs->GetPriority());
+}
+
+inline bool Events::ModuleEventProvider::ElementComp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
+{
+ if (lhs->GetPriority() < rhs->GetPriority())
+ return true;
+ if (lhs->GetPriority() > rhs->GetPriority())
+ return false;
+ return std::less<ModuleEventListener*>()(lhs, rhs);
+}
+
/**
* Run the given hook provided by a module
*
* FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
*/
#define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
- const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
- for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+ const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+ for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
{ \
listenerclass* _t = static_cast<listenerclass*>(*_i); \
- _t->func params ; \
+ const Module* _m = _t->GetModule(); \
+ if (_m && !_m->dying) \
+ _t->func params ; \
} \
} while (0);
*/
#define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
result = MOD_RES_PASSTHRU; \
- const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
- for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
+ const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
+ for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
{ \
listenerclass* _t = static_cast<listenerclass*>(*_i); \
+ const Module* _m = _t->GetModule(); \
+ if (!_m || _m->dying) \
+ continue; \
result = _t->func params ; \
if (result != MOD_RES_PASSTHRU) \
break; \