neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
event.hpp
Go to the documentation of this file.
1// event.hpp
2/*
3 * Copyright (c) 2021 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <vector>
44
45namespace neolib
46{
47 class async_event_queue : public lifetime<i_async_event_queue>
48 {
49 private:
50 struct queue_entry
51 {
52 void const* event;
53 destroyed_flag eventDestroyed;
54 void const* slot;
55 destroyed_flag slotDestroyed;
56 std::function<void()> callback;
57 };
58 public:
60 static async_event_queue& instance(std::thread::id aThreadId);
61 public:
64 public:
65 template <typename... Args>
66 void enqueue(i_slot<Args...>& aSlot, bool aNoDuplicates, Args... aArgs)
67 {
68 std::scoped_lock lock{ event_mutex() };
69 auto& event = aSlot.event();
70 std::tuple<Args...> args{ aArgs... };
71 auto callback = [&, args]()
72 {
73 std::apply([&](Args... aArgs) { aSlot.call(aArgs...); }, args);
74 };
75 if (aNoDuplicates || aSlot.stateless())
76 {
77 auto existing = std::find_if(iQueue.begin(), iQueue.end(), [&](auto const& e) { return e.event == &event && e.slot == &aSlot; });
78 if (existing != iQueue.end())
79 {
80 existing->callback = callback;
81 return;
82 }
83 }
84 iQueue.emplace_back(&event, event, &aSlot, aSlot, callback);
85 }
86 public:
87 void register_with_task(i_async_task& aTask) final;
88 bool pump_events() final;
89 private:
90 i_async_task* iTask = nullptr;
91 std::optional<destroyed_flag> iTaskDestroyed;
92 std::vector<queue_entry> iQueue;
93 };
94
95 template <typename... Args>
96 class event : public lifetime<i_event<Args...>>
97 {
98 typedef event<Args...> self_type;
99 typedef lifetime<i_event<Args...>> base_type;
100 public:
101 using typename base_type::abstract_type;
102 private:
103 typedef std::vector<ref_ptr<i_slot<Args...>>> slot_list;
104 struct work_list
105 {
106 slot_list slots;
107 bool accepted = false;
108 };
109 public:
111 {
112 }
114 {
115 }
116 public:
118 {
119 return iTriggerType;
120 }
121 void set_trigger_type(neolib::trigger_type aTriggerType) final
122 {
123 iTriggerType = aTriggerType;
124 }
125 public:
126 trigger_result sync_trigger(Args... aArgs) const final
127 {
128 std::unique_lock lock{ event_mutex() };
129 destroyed_flag destroyed{ *this };
130 thread_local std::size_t stack;
131 scoped_counter<std::size_t> stackCounter{ stack };
132 thread_local std::vector<std::unique_ptr<work_list>> workLists;
133 if (workLists.size() < stack)
134 workLists.push_back(std::make_unique<work_list>());
135 auto& workList = *workLists[stack - 1];
136 scoped_pointer<work_list> activeWorkList{ iActiveWorkList, &workList };
137 workList.slots = iSlots;
138 lock.unlock();
139 for (auto slot : workList.slots)
140 {
141 if (slot->call_in_emitter_thread() || slot->call_thread() == std::this_thread::get_id())
142 slot->call(aArgs...);
143 else
145 if (destroyed)
147 if (workList.accepted)
149 }
150 workList.slots.clear();
151 workList.accepted = false;
153 }
154 void async_trigger(Args... aArgs) const final
155 {
156 std::unique_lock lock{ event_mutex() };
157 for (auto slot : iSlots)
159 }
160 void accept() const final
161 {
162 std::unique_lock lock{ event_mutex() };
163 if (iActiveWorkList)
164 iActiveWorkList->accepted = true;
165 }
166 public:
167 bool has_slots() const final
168 {
169 std::scoped_lock lock{ event_mutex() };
170 return !iSlots.empty();
171 }
172 void add_slot(i_slot<Args...>& aSlot) const final
173 {
174 std::scoped_lock lock{ event_mutex() };
175 iSlots.push_back(&aSlot);
176 }
177 void remove_slot(i_slot<Args...>& aSlot) const final
178 {
179 std::scoped_lock lock{ event_mutex() };
180 auto existing = std::find_if(iSlots.begin(), iSlots.end(), [&](auto const& s) { return &aSlot == s.ptr(); });
181 if (existing != iSlots.end())
182 iSlots.erase(existing);
183 }
184 private:
185 void async_trigger(async_event_queue& aQueue, i_slot<Args...>& aSlot, bool aNoDuplicates, Args... aArgs) const
186 {
187 aQueue.enqueue<Args...>(aSlot, aNoDuplicates, aArgs...);
188 }
189 private:
191 mutable slot_list iSlots;
192 mutable work_list* iActiveWorkList = nullptr;
193 };
194
195 #define define_declared_event( name, declName, ... ) \
196 neolib::event<__VA_ARGS__> name; \
197 const neolib::i_event<__VA_ARGS__>& ev_##declName() const final { return name; };\
198 neolib::i_event<__VA_ARGS__>& ev_##declName() final { return name; };
199
200 #define define_event( name, declName, ... ) \
201 neolib::event<__VA_ARGS__> name; \
202 const neolib::i_event<__VA_ARGS__>& ev_##declName() const { return name; };\
203 neolib::i_event<__VA_ARGS__>& ev_##declName() { return name; };\
204 const neolib::i_event<__VA_ARGS__>& declName() const { return ev_##declName(); }\
205 neolib::i_event<__VA_ARGS__>& declName() { return ev_##declName(); }\
206 detail_event_subscribe(declName, __VA_ARGS__)
207}
void register_with_task(i_async_task &aTask) final
static async_event_queue & instance()
static async_event_queue & instance(std::thread::id aThreadId)
void enqueue(i_slot< Args... > &aSlot, bool aNoDuplicates, Args... aArgs)
Definition event.hpp:66
void remove_slot(i_slot< Args... > &aSlot) const final
Definition event.hpp:177
void set_trigger_type(neolib::trigger_type aTriggerType) final
Definition event.hpp:121
neolib::trigger_type trigger_type() const final
Definition event.hpp:117
void async_trigger(Args... aArgs) const final
Definition event.hpp:154
bool has_slots() const final
Definition event.hpp:167
void accept() const final
Definition event.hpp:160
void add_slot(i_slot< Args... > &aSlot) const final
Definition event.hpp:172
trigger_result sync_trigger(Args... aArgs) const final
Definition event.hpp:126
virtual i_event< Args... > const & event() const =0
virtual bool stateless() const =0
void call(Args... aArgs) const final
Definition i_event.hpp:217
bool call_in_emitter_thread() const final
Definition i_event.hpp:227
std::thread::id call_thread() const final
Definition i_event.hpp:221
switchable_mutex & event_mutex()
Definition i_event.hpp:46
auto destroyed(Object &aObject, const Handler aHandler)
Definition i_object.hpp:73
trigger_result
Definition i_event.hpp:109
Definition plf_hive.h:79