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