inspircd/include/event.h

236 lines
7.1 KiB
C
Raw Permalink Normal View History

2015-02-11 16:38:40 +01:00
/*
* InspIRCd -- Internet Relay Chat Daemon
*
2024-06-07 10:37:56 +01:00
* Copyright (C) 2018-2023 Sadie Powell <sadie@witchery.services>
2020-01-11 22:02:47 +00:00
* Copyright (C) 2015, 2018 Attila Molnar <attilamolnar@hush.com>
2015-02-11 16:38:40 +01:00
*
* 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
* License as published by the Free Software Foundation, version 2.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "base.h"
2015-02-11 16:38:40 +01:00
namespace Events
{
class ModuleEventListener;
class ModuleEventProvider;
}
/** Provider of one or more cross-module events.
* Modules who wish to provide events for other modules create instances of this class and use
* one of the macros below to fire the event, passing the instance of the event provider class
* to the macro.
* Event providers are identified using a unique identifier string.
*/
class Events::ModuleEventProvider
: public ServiceProvider
, private dynamic_reference_base::CaptureHook
2015-02-11 16:38:40 +01:00
{
public:
struct Comp final
{
bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
};
struct ElementComp final
{
bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
};
typedef insp::flat_multiset<ModuleEventListener*, Comp, ElementComp> SubscriberList;
2015-02-11 16:38:40 +01:00
/** Constructor
* @param mod Module providing the event(s)
* @param eventid Identifier of the event or event group provided, must be unique
*/
ModuleEventProvider(Module* mod, const std::string& eventid)
: ServiceProvider(mod, eventid, SERVICE_DATA)
, prov(mod, eventid)
{
prov.SetCaptureHook(this);
}
/** Retrieves the module which created this listener. */
const Module* GetModule() const { return prov.creator; }
2015-02-11 16:38:40 +01:00
/** Get list of objects subscribed to this event
* @return List of subscribed objects
*/
const SubscriberList& GetSubscribers() const { return prov->subscribers; }
/** 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);
}
2015-02-11 16:38:40 +01:00
/** Run the given hook provided by a module. */
template<typename Class, typename... FunArgs, typename... FwdArgs>
inline void Call(void (Class::*function)(FunArgs...), FwdArgs&&... args) const;
/**
2022-08-20 18:13:18 +01:00
* Run the given hook provided by a module until some module returns MOD_RES_ALLOW or
* MOD_RES_DENY. If no module does that the result is set to MOD_RES_PASSTHRU.
*/
template<typename Class, typename... FunArgs, typename... FwdArgs>
inline ModResult FirstResult(ModResult (Class::*function)(FunArgs...), FwdArgs&&... args) const;
private:
void OnCapture() override
2015-02-11 16:38:40 +01:00
{
// If someone else holds the list from now on, clear mine. See below for more info.
if (*prov != this)
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) { }
2015-02-11 16:38:40 +01:00
/** 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
* where the dynref points to.
*/
dynamic_reference_nocheck<ModuleEventProvider> prov;
/** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
* exist with the same name and we are not the ones holding the list.
*/
SubscriberList subscribers;
};
/** Base class for abstract classes describing cross-module events.
* Subscribers should NOT inherit directly from this class.
*/
class Events::ModuleEventListener
: private dynamic_reference_base::CaptureHook
2015-02-11 16:38:40 +01:00
{
private:
2015-02-11 16:38:40 +01:00
/** Reference to the provider, can be NULL if none of the provider modules are loaded
*/
dynamic_reference_nocheck<ModuleEventProvider> prov;
const unsigned int eventpriority;
2015-02-11 16:38:40 +01:00
/** Called by the dynref when the event provider becomes available
*/
void OnCapture() override
2015-02-11 16:38:40 +01:00
{
prov->Subscribe(this);
2015-02-11 16:38:40 +01:00
}
protected:
2015-02-11 16:38:40 +01:00
/** Constructor
* @param mod Module subscribing
* @param eventid Identifier of the event to subscribe to
2018-10-21 19:18:08 +01:00
* @param eventprio The priority to give this event listener
2015-02-11 16:38:40 +01:00
*/
ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio)
2015-02-11 16:38:40 +01:00
: prov(mod, eventid)
, eventpriority(eventprio)
2015-02-11 16:38:40 +01:00
{
prov.SetCaptureHook(this);
// If the dynamic_reference resolved at construction our capture handler wasn't called
if (prov)
ModuleEventListener::OnCapture();
}
public:
static constexpr unsigned int DefaultPriority = 100;
2015-02-11 16:38:40 +01:00
~ModuleEventListener()
{
if (prov)
prov->Unsubscribe(this);
2015-02-11 16:38:40 +01:00
}
/** 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; }
2015-02-11 16:38:40 +01:00
};
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<>()(lhs, rhs);
}
template<typename Class, typename... FunArgs, typename... FwdArgs>
inline void Events::ModuleEventProvider::Call(void (Class::*function)(FunArgs...), FwdArgs&&... args) const
{
2021-01-07 13:13:58 +00:00
if (GetModule() && GetModule()->dying)
2020-12-21 16:40:32 +00:00
return;
2023-01-10 23:02:45 +00:00
for (auto* subscriber : GetSubscribers())
{
const Module* mod = subscriber->GetModule();
if (!mod || mod->dying)
continue;
Class* klass = static_cast<Class*>(subscriber);
(klass->*function)(std::forward<FwdArgs>(args)...);
}
}
2015-02-11 16:38:40 +01:00
template<typename Class, typename... FunArgs, typename... FwdArgs>
inline ModResult Events::ModuleEventProvider::FirstResult(ModResult (Class::*function)(FunArgs...), FwdArgs&&... args) const
{
2021-01-07 13:13:58 +00:00
if (GetModule() && GetModule()->dying)
2020-12-21 16:40:32 +00:00
return MOD_RES_PASSTHRU;
ModResult result;
2023-01-10 23:02:45 +00:00
for (auto* subscriber : GetSubscribers())
{
const Module* mod = subscriber->GetModule();
if (!mod || mod->dying)
continue;
Class* klass = static_cast<Class*>(subscriber);
result = (klass->*function)(std::forward<FwdArgs>(args)...);
if (result != MOD_RES_PASSTHRU)
break;
}
return result;
}