]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/event.h
Fix the ordering of custom event handlers.
[user/henk/code/inspircd.git] / include / event.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018-2019 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         /** Get list of objects subscribed to this event
62          * @return List of subscribed objects
63          */
64         const SubscriberList& GetSubscribers() const { return prov->subscribers; }
65
66         /** Subscribes a listener to this event.
67          * @param subscriber The listener to subscribe.
68          */
69         void Subscribe(ModuleEventListener* subscriber)
70         {
71                 subscribers.insert(subscriber);
72                 OnSubscribe(subscriber);
73         }
74
75         /** Unsubscribes a listener from this event.
76          * @param subscriber The listener to unsubscribe.
77          */
78         void Unsubscribe(ModuleEventListener* subscriber)
79         {
80                 subscribers.erase(subscriber);
81                 OnUnsubscribe(subscriber);
82         }
83
84  private:
85         void OnCapture() CXX11_OVERRIDE
86         {
87                 // If someone else holds the list from now on, clear mine. See below for more info.
88                 if (*prov != this)
89                         subscribers.clear();
90         }
91
92         /** Called when a listener subscribes to this event.
93          * @param subscriber The listener which subscribed.
94          */
95         virtual void OnSubscribe(ModuleEventListener* subscriber) { }
96
97         /** Called when a listener unsubscribes from this event.
98          * @param subscriber The listener which unsubscribed.
99          */
100         virtual void OnUnsubscribe(ModuleEventListener* subscriber) { }
101
102         /** Reference to the active provider for this event. In case multiple event providers
103          * exist for the same event, only one of them contains the list of subscribers.
104          * To handle the case when we are not the ones with the list, we get it from the provider
105          * where the dynref points to.
106          */
107         dynamic_reference_nocheck<ModuleEventProvider> prov;
108
109         /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
110          * exist with the same name and we are not the ones holding the list.
111          */
112         SubscriberList subscribers;
113 };
114
115 /** Base class for abstract classes describing cross-module events.
116  * Subscribers should NOT inherit directly from this class.
117  */
118 class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
119 {
120         /** Reference to the provider, can be NULL if none of the provider modules are loaded
121          */
122         dynamic_reference_nocheck<ModuleEventProvider> prov;
123
124         const unsigned int eventpriority;
125
126         /** Called by the dynref when the event provider becomes available
127          */
128         void OnCapture() CXX11_OVERRIDE
129         {
130                 prov->Subscribe(this);
131         }
132
133  public:
134         static const unsigned int DefaultPriority = 100;
135
136         /** Constructor
137          * @param mod Module subscribing
138          * @param eventid Identifier of the event to subscribe to
139          * @param eventprio The priority to give this event listener
140          */
141         ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
142                 : prov(mod, eventid)
143                 , eventpriority(eventprio)
144         {
145                 prov.SetCaptureHook(this);
146                 // If the dynamic_reference resolved at construction our capture handler wasn't called
147                 if (prov)
148                         ModuleEventListener::OnCapture();
149         }
150
151         ~ModuleEventListener()
152         {
153                 if (prov)
154                         prov->Unsubscribe(this);
155         }
156
157         /** Retrieves the module which created this listener. */
158         const Module* GetModule() const { return prov.creator; }
159
160         /** Retrieves the priority of this event. */
161         unsigned int GetPriority() const { return eventpriority; }
162 };
163
164 inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
165 {
166         return (lhs->GetPriority() < rhs->GetPriority());
167 }
168
169 inline bool Events::ModuleEventProvider::ElementComp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
170 {
171         if (lhs->GetPriority() < rhs->GetPriority())
172                 return true;
173         if (lhs->GetPriority() > rhs->GetPriority())
174                 return false;
175         return std::less<ModuleEventListener*>()(lhs, rhs);
176 }
177
178 /**
179  * Run the given hook provided by a module
180  *
181  * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
182  */
183 #define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
184         const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
185         for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
186         { \
187                 listenerclass* _t = static_cast<listenerclass*>(*_i); \
188                 const Module* _m = _t->GetModule(); \
189                 if (_m && !_m->dying) \
190                         _t->func params ; \
191         } \
192 } while (0);
193
194 /**
195  * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
196  * If no module does that, result is set to MOD_RES_PASSTHRU.
197  *
198  * Example: ModResult MOD_RESULT;
199  * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
200  */
201 #define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
202         result = MOD_RES_PASSTHRU; \
203         const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
204         for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
205         { \
206                 listenerclass* _t = static_cast<listenerclass*>(*_i); \
207                 const Module* _m = _t->GetModule(); \
208                 if (!_m || _m->dying) \
209                         continue; \
210                 result = _t->func params ; \
211                 if (result != MOD_RES_PASSTHRU) \
212                         break; \
213         } \
214 } while (0);