Plasma Engine  2.0
Loading...
Searching...
No Matches
Event_inl.h
1#pragma once
2
3#include <Foundation/Types/ScopeExit.h>
4
5template <typename EventData, typename MutexType, plEventType EventType>
7 : m_EventHandlers(pAllocator)
8{
9#if PL_ENABLED(PL_COMPILE_FOR_DEVELOPMENT)
10 m_pSelf = this;
11#endif
12}
13
14template <typename EventData, typename MutexType, plEventType EventType>
16{
17#if PL_ENABLED(PL_COMPILE_FOR_DEVELOPMENT)
18 PL_ASSERT_ALWAYS(m_pSelf == this, "The plEvent was relocated in memory. This is not allowed, as it breaks the Unsubscribers.");
19#endif
20}
21
24template <typename EventData, typename MutexType, plEventType EventType>
26{
27 PL_LOCK(m_Mutex);
28
29 if constexpr (std::is_same_v<MutexType, plNoMutex>)
30 {
31 if constexpr (EventType == plEventType::Default)
32 {
33 PL_ASSERT_DEV(m_uiRecursionDepth == 0, "Can't add or remove event handlers while broadcasting (without a mutex). Either enable the use of a mutex on this event, or switch to plCopyOnBroadcastEvent if this should be allowed. Since this event does not have a mutex, this error can also happen due to multi-threaded access.");
34 }
35 }
36
37 PL_ASSERT_DEV(!handler.IsComparable() || !HasEventHandler(handler), "The same event handler cannot be added twice");
38
39 auto& item = m_EventHandlers.ExpandAndGetRef();
40 item.m_Handler = std::move(handler);
41 item.m_SubscriptionID = ++m_NextSubscriptionID;
42
43 return item.m_SubscriptionID;
44}
45
46template <typename EventData, typename MutexType, plEventType EventType>
48{
49 PL_LOCK(m_Mutex);
50
51 if constexpr (std::is_same_v<MutexType, plNoMutex>)
52 {
53 if constexpr (EventType == plEventType::Default)
54 {
55 PL_ASSERT_DEV(m_uiRecursionDepth == 0, "Can't add or remove event handlers while broadcasting (without a mutex). Either enable the use of a mutex on this event, or switch to plCopyOnBroadcastEvent if this should be allowed. Since this event does not have a mutex, this error can also happen due to multi-threaded access.");
56 }
57 }
58
59 inout_unsubscriber.Unsubscribe();
60 inout_unsubscriber.m_pEvent = this;
61 inout_unsubscriber.m_SubscriptionID = AddEventHandler(std::move(handler));
62}
63
64
67template <typename EventData, typename MutexType, plEventType EventType>
69{
70 PL_ASSERT_DEV(handler.IsComparable(), "Lambdas that capture data cannot be removed via function pointer. Use an plEventSubscriptionID instead.");
71
72 PL_LOCK(m_Mutex);
73
74 if constexpr (EventType == plEventType::Default)
75 {
76 if constexpr (std::is_same_v<MutexType, plNoMutex>)
77 {
78 PL_ASSERT_DEV(m_uiRecursionDepth == 0, "Can't add or remove event handlers while broadcasting (without a mutex). Either enable the use of a mutex on this event, or switch to plCopyOnBroadcastEvent if this should be allowed. Since this event does not have a mutex, this error can also happen due to multi-threaded access.");
79 }
80 }
81
82 for (plUInt32 idx = 0; idx < m_EventHandlers.GetCount(); ++idx)
83 {
84 if (m_EventHandlers[idx].m_Handler.IsEqualIfComparable(handler))
85 {
86 if constexpr (EventType == plEventType::Default)
87 {
88 // if this event does not copy the handlers, and we are currently broadcasting
89 // we can't shrink the size of the array, however, we can replace elements (the check above says that we have a mutex, so this is fine)
90
91 if (m_uiRecursionDepth > 0)
92 {
93 // we just write an invalid handler here, and let the broadcast function clean it up for us
94 m_EventHandlers[idx].m_Handler = {};
95 m_EventHandlers[idx].m_SubscriptionID = {};
96 return;
97 }
98 }
99
100 // if we are not broadcasting, or the broadcast uses a copy anyway, we can just modify the handler array directly
101 m_EventHandlers.RemoveAtAndCopy(idx);
102 return;
103 }
104 }
105
106 PL_ASSERT_DEV(false, "plEvent::RemoveEventHandler: Handler has not been registered or already been unregistered.");
107}
108
109template <typename EventData, typename MutexType, plEventType EventType>
111{
112 if (inout_id == 0)
113 return;
114
115 const plEventSubscriptionID subId = inout_id;
116 inout_id = 0;
117
118 PL_LOCK(m_Mutex);
119
120 if constexpr (EventType == plEventType::Default)
121 {
122 if constexpr (std::is_same_v<MutexType, plNoMutex>)
123 {
124 PL_ASSERT_DEV(m_uiRecursionDepth == 0, "Can't add or remove event handlers while broadcasting (without a mutex). Either enable the use of a mutex on this event, or switch to plCopyOnBroadcastEvent if this should be allowed. Since this event does not have a mutex, this error can also happen due to multi-threaded access.");
126 }
127
128 for (plUInt32 idx = 0; idx < m_EventHandlers.GetCount(); ++idx)
129 {
130 if (m_EventHandlers[idx].m_SubscriptionID == subId)
131 {
132 if constexpr (EventType == plEventType::Default)
133 {
134 // if this event does not copy the handlers, and we are currently broadcasting
135 // we can't shrink the size of the array, however, we can replace elements (the check above says that we have a mutex, so this is fine)
136
137 if (m_uiRecursionDepth > 0)
138 {
139 // we just write an invalid handler here, and let the broadcast function clean it up for us
140 m_EventHandlers[idx].m_Handler = {};
141 m_EventHandlers[idx].m_SubscriptionID = {};
142 return;
144 }
145
146 // if we are not broadcasting, or the broadcast uses a copy anyway, we can just modify the handler array directly
147 m_EventHandlers.RemoveAtAndCopy(idx);
148 return;
149 }
150 }
151
152 PL_ASSERT_DEV(false, "plEvent::RemoveEventHandler: Invalid subscription ID '{0}'.", (plInt32)subId);
153}
154
155template <typename EventData, typename MutexType, plEventType EventType>
157{
158 PL_ASSERT_DEV(handler.IsComparable(), "Lambdas that capture data cannot be checked via function pointer. Use an plEventSubscriptionID instead.");
159
160 PL_LOCK(m_Mutex);
161
162 for (plUInt32 i = 0; i < m_EventHandlers.GetCount(); ++i)
163 {
164 if (m_EventHandlers[i].m_Handler.IsEqualIfComparable(handler))
165 return true;
166 }
167
168 return false;
169}
170
171template <typename EventData, typename MutexType, plEventType EventType>
173{
174 PL_LOCK(m_Mutex);
175 m_EventHandlers.Clear();
176}
177
178template <typename EventData, typename MutexType, plEventType EventType>
180{
181 PL_LOCK(m_Mutex);
182 return m_EventHandlers.IsEmpty();
183}
184
186template <typename EventData, typename MutexType, plEventType EventType>
187void plEventBase<EventData, MutexType, EventType>::Broadcast(EventData eventData, plUInt8 uiMaxRecursionDepth)
188{
189 if constexpr (EventType == plEventType::Default)
190 {
191 PL_LOCK(m_Mutex);
192
193 PL_ASSERT_DEV(m_uiRecursionDepth <= uiMaxRecursionDepth, "The event has been triggered recursively or from several threads simultaneously.");
194
195 if (m_uiRecursionDepth > uiMaxRecursionDepth)
196 return;
197
198#if PL_ENABLED(PL_COMPILE_FOR_DEVELOPMENT)
199 PL_ASSERT_ALWAYS(m_pSelf == this, "The plEvent was relocated in memory. This is not allowed, as it breaks the Unsubscribers.");
200#endif
201
202 m_uiRecursionDepth++;
203
204 // RAII to ensure correctness in case exceptions are used
205 auto scopeExit = plMakeScopeExit([&]()
206 { m_uiRecursionDepth--; });
207
208 // don't execute handlers that are added while we are broadcasting
209 plUInt32 uiMaxHandlers = m_EventHandlers.GetCount();
210
211 for (plUInt32 ui = 0; ui < uiMaxHandlers;)
212 {
213 if (m_EventHandlers[ui].m_Handler.IsValid())
214 {
215 m_EventHandlers[ui].m_Handler(eventData);
216 ++ui;
217 }
218 else
219 {
220 m_EventHandlers.RemoveAtAndCopy(ui);
221 --uiMaxHandlers;
222 }
223 }
224 }
225 else
226 {
227 plHybridArray<HandlerData, 16> eventHandlers;
228 {
229 PL_LOCK(m_Mutex);
230
231 if constexpr (RecursionDepthSupported)
232 {
233 PL_ASSERT_DEV(m_uiRecursionDepth <= uiMaxRecursionDepth, "The event has been triggered recursively or from several threads simultaneously.");
234
235 if (m_uiRecursionDepth > uiMaxRecursionDepth)
236 return;
237
238#if PL_ENABLED(PL_COMPILE_FOR_DEVELOPMENT)
239 PL_ASSERT_ALWAYS(m_pSelf == this, "The plEvent was relocated in memory. This is not allowed, as it breaks the Unsubscribers.");
240#endif
241
242 m_uiRecursionDepth++;
243 }
244 else
245 {
246 PL_ASSERT_DEV(uiMaxRecursionDepth == 255, "uiMaxRecursionDepth is not supported if plEventType::CopyOnBroadcast is used and the event needs to be threadsafe.");
247 }
248
249#if PL_ENABLED(PL_COMPILE_FOR_DEVELOPMENT)
250 PL_ASSERT_ALWAYS(m_pSelf == this, "The plEvent was relocated in memory. This is not allowed, as it breaks the Unsubscribers.");
251#endif
252
253 eventHandlers = m_EventHandlers;
254 }
255
256 // RAII to ensure correctness in case exceptions are used
257 auto scopeExit = plMakeScopeExit([&]()
258 {
259 // Bug in MSVC 2017. Can't use if constexpr.
260#if PL_ENABLED(PL_COMPILER_MSVC) && _MSC_VER < 1920
261 if (RecursionDepthSupported)
262 {
263 m_uiRecursionDepth--;
264 }
265#else
266 if constexpr (RecursionDepthSupported)
267 {
268 m_uiRecursionDepth--;
269 }
270#endif
271 });
272
273 const plUInt32 uiHandlerCount = eventHandlers.GetCount();
274 for (plUInt32 ui = 0; ui < uiHandlerCount; ++ui)
275 {
276 eventHandlers[ui].m_Handler(eventData);
277 }
278 }
279}
280
281
282template <typename EventData, typename MutexType, typename AllocatorWrapper, plEventType EventType>
284 : plEventBase<EventData, MutexType, EventType>(AllocatorWrapper::GetAllocator())
285{
286}
287
288template <typename EventData, typename MutexType, typename AllocatorWrapper, plEventType EventType>
290 : plEventBase<EventData, MutexType, EventType>(pAllocator)
291{
292}
Base class for all memory allocators.
Definition Allocator.h:23
plUInt32 GetCount() const
Returns the number of active elements in the array.
Definition ArrayBase_inl.h:172
An object that can be passed to plEvent::AddEventHandler to store the subscription information and au...
Definition Event.h:50
void Unsubscribe()
If the unsubscriber holds a valid subscription, it will be removed from the target plEvent.
Definition Event.h:73
This class propagates event information to registered event handlers.
Definition Event.h:37
bool IsEmpty() const
Returns true, if no event handlers are registered.
Definition Event_inl.h:179
plEventBase(plAllocator *pAllocator)
Constructor.
Definition Event_inl.h:6
bool HasEventHandler(const Handler &handler) const
Checks whether an event handler has already been registered.
Definition Event_inl.h:156
void Clear()
Removes all registered event handlers.
Definition Event_inl.h:172
plEventSubscriptionID AddEventHandler(Handler handler) const
Adds a function as an event handler. All handlers will be notified in the order that they were regist...
Definition Event_inl.h:25
void RemoveEventHandler(const Handler &handler) const
Removes a previously registered handler. It is an error to remove a handler that was not registered.
Definition Event_inl.h:68
void Broadcast(EventData pEventData, plUInt8 uiMaxRecursionDepth=MaxRecursionDepthDefault)
This function will broadcast to all registered users, that this event has just happened....
Definition Event_inl.h:187
Definition Event.h:177
A hybrid array uses in-place storage to handle the first few elements without any allocation....
Definition HybridArray.h:12