]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/event.h
Don't call events provided by dying or dead modules.
[user/henk/code/inspircd.git] / include / event.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2015, 2018 Attila Molnar <attilamolnar@hush.com>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #pragma once
22
23 namespace Events
24 {
25         class ModuleEventListener;
26         class ModuleEventProvider;
27 }
28
29 /** Provider of one or more cross-module events.
30  * Modules who wish to provide events for other modules create instances of this class and use
31  * one of the macros below to fire the event, passing the instance of the event provider class
32  * to the macro.
33  * Event providers are identified using a unique identifier string.
34  */
35 class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
36 {
37  public:
38         struct Comp
39         {
40                 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
41         };
42
43         struct ElementComp
44         {
45                 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
46         };
47
48         typedef insp::flat_multiset<ModuleEventListener*, Comp, ElementComp> SubscriberList;
49
50         /** Constructor
51          * @param mod Module providing the event(s)
52          * @param eventid Identifier of the event or event group provided, must be unique
53          */
54         ModuleEventProvider(Module* mod, const std::string& eventid)
55                 : ServiceProvider(mod, eventid, SERVICE_DATA)
56                 , prov(mod, eventid)
57         {
58                 prov.SetCaptureHook(this);
59         }
60
61         /** Retrieves the module which created this listener. */
62         const Module* GetModule() const { return prov.creator; }
63
64         /** Get list of objects subscribed to this event
65          * @return List of subscribed objects
66          */
67         const SubscriberList& GetSubscribers() const { return prov->subscribers; }
68
69         /** Subscribes a listener to this event.
70          * @param subscriber The listener to subscribe.
71          */
72         void Subscribe(ModuleEventListener* subscriber)
73         {
74                 subscribers.insert(subscriber);
75                 OnSubscribe(subscriber);
76         }
77
78         /** Unsubscribes a listener from this event.
79          * @param subscriber The listener to unsubscribe.
80          */
81         void Unsubscribe(ModuleEventListener* subscriber)
82         {
83                 subscribers.erase(subscriber);
84                 OnUnsubscribe(subscriber);
85         }
86
87  private:
88         void OnCapture() CXX11_OVERRIDE
89         {
90                 // If someone else holds the list from now on, clear mine. See below for more info.
91                 if (*prov != this)
92                         subscribers.clear();
93         }
94
95         /** Called when a listener subscribes to this event.
96          * @param subscriber The listener which subscribed.
97          */
98         virtual void OnSubscribe(ModuleEventListener* subscriber) { }
99
100         /** Called when a listener unsubscribes from this event.
101          * @param subscriber The listener which unsubscribed.
102          */
103         virtual void OnUnsubscribe(ModuleEventListener* subscriber) { }
104
105         /** Reference to the active provider for this event. In case multiple event providers
106          * exist for the same event, only one of them contains the list of subscribers.
107          * To handle the case when we are not the ones with the list, we get it from the provider
108          * where the dynref points to.
109          */
110         dynamic_reference_nocheck<ModuleEventProvider> prov;
111
112         /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
113          * exist with the same name and we are not the ones holding the list.
114          */
115         SubscriberList subscribers;
116 };
117
118 /** Base class for abstract classes describing cross-module events.
119  * Subscribers should NOT inherit directly from this class.
120  */
121 class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
122 {
123         /** Reference to the provider, can be NULL if none of the provider modules are loaded
124          */
125         dynamic_reference_nocheck<ModuleEventProvider> prov;
126
127         const unsigned int eventpriority;
128
129         /** Called by the dynref when the event provider becomes available
130          */
131         void OnCapture() CXX11_OVERRIDE
132         {
133                 prov->Subscribe(this);
134         }
135
136  public:
137         static const unsigned int DefaultPriority = 100;
138
139         /** Constructor
140          * @param mod Module subscribing
141          * @param eventid Identifier of the event to subscribe to
142          * @param eventprio The priority to give this event listener
143          */
144         ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
145                 : prov(mod, eventid)
146                 , eventpriority(eventprio)
147         {
148                 prov.SetCaptureHook(this);
149                 // If the dynamic_reference resolved at construction our capture handler wasn't called
150                 if (prov)
151                         ModuleEventListener::OnCapture();
152         }
153
154         ~ModuleEventListener()
155         {
156                 if (prov)
157                         prov->Unsubscribe(this);
158         }
159
160         /** Retrieves the module which created this listener. */
161         const Module* GetModule() const { return prov.creator; }
162
163         /** Retrieves the priority of this event. */
164         unsigned int GetPriority() const { return eventpriority; }
165 };
166
167 inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
168 {
169         return (lhs->GetPriority() < rhs->GetPriority());
170 }
171
172 inline bool Events::ModuleEventProvider::ElementComp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
173 {
174         if (lhs->GetPriority() < rhs->GetPriority())
175                 return true;
176         if (lhs->GetPriority() > rhs->GetPriority())
177                 return false;
178         return std::less<ModuleEventListener*>()(lhs, rhs);
179 }
180
181 /**
182  * Run the given hook provided by a module
183  *
184  * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
185  */
186 #define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
187         if (!(prov).GetModule() || (prov).GetModule()->dying) \
188         { \
189                 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
190                 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
191                 { \
192                         listenerclass* _t = static_cast<listenerclass*>(*_i); \
193                         const Module* _m = _t->GetModule(); \
194                         if (_m && !_m->dying) \
195                         _t->func params ; \
196                 } \
197         } \
198 } while (0);
199
200 /**
201  * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
202  * If no module does that, result is set to MOD_RES_PASSTHRU.
203  *
204  * Example: ModResult MOD_RESULT;
205  * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
206  */
207 #define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
208         result = MOD_RES_PASSTHRU; \
209         if (!(prov).GetModule() || (prov).GetModule()->dying) \
210         { \
211                 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
212                 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
213                 { \
214                         listenerclass* _t = static_cast<listenerclass*>(*_i); \
215                         const Module* _m = _t->GetModule(); \
216                         if (!_m || _m->dying) \
217                                 continue; \
218                         result = _t->func params ; \
219                         if (result != MOD_RES_PASSTHRU) \
220                                 break; \
221                 } \
222         } \
223 } while (0);