Skip to content

Commit 0a00664

Browse files
committed
Refactor VideoOverlaySession to use ListenerSet and fix hang on stats.
1 parent eb8fffa commit 0a00664

File tree

2 files changed

+104
-122
lines changed

2 files changed

+104
-122
lines changed

SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlaySession.cpp

Lines changed: 102 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@ namespace PokemonAutomation{
1515

1616

1717
void VideoOverlaySession::add_listener(ContentListener& listener){
18-
WriteSpinLock lg(m_lock);
19-
m_content_listeners.insert(&listener);
18+
m_listeners.add(listener);
2019
}
2120
void VideoOverlaySession::remove_listener(ContentListener& listener){
22-
WriteSpinLock lg(m_lock);
23-
// listener.on_overlay_update_stats(nullptr);
24-
m_content_listeners.erase(&listener);
21+
m_listeners.remove(listener);
2522
}
2623

2724

@@ -53,7 +50,6 @@ void VideoOverlaySession::get(VideoOverlayOption& option){
5350
option.stats.store(stats, std::memory_order_relaxed);
5451
}
5552
void VideoOverlaySession::set(const VideoOverlayOption& option){
56-
WriteSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_boxes()");
5753
bool boxes = option.boxes.load(std::memory_order_relaxed);
5854
bool text = option.text.load(std::memory_order_relaxed);
5955
bool images = option.images.load(std::memory_order_relaxed);
@@ -64,13 +60,11 @@ void VideoOverlaySession::set(const VideoOverlayOption& option){
6460
m_option.images.store(images, std::memory_order_relaxed);
6561
m_option.log.store(log, std::memory_order_relaxed);
6662
m_option.stats.store(stats, std::memory_order_relaxed);
67-
for (ContentListener* listener : m_content_listeners){
68-
listener->on_overlay_enabled_boxes(boxes);
69-
listener->on_overlay_enabled_text(text);
70-
listener->on_overlay_enabled_images(images);
71-
listener->on_overlay_enabled_log(log);
72-
listener->on_overlay_enabled_stats(stats);
73-
}
63+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_boxes, boxes);
64+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_text, text);
65+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_images, images);
66+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_log, log);
67+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_stats, stats);
7468
}
7569

7670

@@ -95,38 +89,23 @@ void VideoOverlaySession::stats_thread(){
9589

9690
void VideoOverlaySession::set_enabled_boxes(bool enabled){
9791
m_option.boxes.store(enabled, std::memory_order_relaxed);
98-
ReadSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_boxes()");
99-
for (ContentListener* listener : m_content_listeners){
100-
listener->on_overlay_enabled_boxes(enabled);
101-
}
92+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_boxes, enabled);
10293
}
10394
void VideoOverlaySession::set_enabled_text(bool enabled){
10495
m_option.text.store(enabled, std::memory_order_relaxed);
105-
ReadSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_text()");
106-
for (ContentListener* listener : m_content_listeners){
107-
listener->on_overlay_enabled_text(enabled);
108-
}
96+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_text, enabled);
10997
}
11098
void VideoOverlaySession::set_enabled_images(bool enabled){
11199
m_option.images.store(enabled, std::memory_order_relaxed);
112-
ReadSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_images()");
113-
for (ContentListener* listener : m_content_listeners){
114-
listener->on_overlay_enabled_images(enabled);
115-
}
100+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_images, enabled);
116101
}
117102
void VideoOverlaySession::set_enabled_log(bool enabled){
118103
m_option.log.store(enabled, std::memory_order_relaxed);
119-
ReadSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_log()");
120-
for (ContentListener* listener : m_content_listeners){
121-
listener->on_overlay_enabled_log(enabled);
122-
}
104+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_log, enabled);
123105
}
124106
void VideoOverlaySession::set_enabled_stats(bool enabled){
125107
m_option.stats.store(enabled, std::memory_order_relaxed);
126-
ReadSpinLock lg(m_lock, "VideoOverlaySession::set_enabled_stats()");
127-
for (ContentListener* listener : m_content_listeners){
128-
listener->on_overlay_enabled_stats(enabled);
129-
}
108+
m_listeners.run_method_unique(&ContentListener::on_overlay_enabled_stats, enabled);
130109
}
131110

132111

@@ -135,30 +114,32 @@ void VideoOverlaySession::set_enabled_stats(bool enabled){
135114
//
136115

137116
void VideoOverlaySession::add_box(const OverlayBox& box){
138-
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_box()");
139-
m_boxes.insert(&box);
140-
push_box_update();
141-
}
142-
void VideoOverlaySession::remove_box(const OverlayBox& box){
143-
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_box()");
144-
m_boxes.erase(&box);
145-
push_box_update();
146-
}
117+
std::shared_ptr<std::vector<OverlayBox>> ptr = std::make_shared<std::vector<OverlayBox>>();
118+
{
119+
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_box()");
120+
m_boxes.insert(&box);
147121

148-
void VideoOverlaySession::push_box_update(){
149-
if (m_content_listeners.empty()){
150-
return;
122+
// We create a newly allocated Box vector to avoid listener accessing
123+
// `m_boxes` asynchronously.
124+
for (const auto& item : m_boxes){
125+
ptr->emplace_back(*item);
126+
}
151127
}
152-
153-
// We create a newly allocated Box vector to avoid listener accessing
154-
// `m_boxes` asynchronously.
128+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_boxes, ptr);
129+
}
130+
void VideoOverlaySession::remove_box(const OverlayBox& box){
155131
std::shared_ptr<std::vector<OverlayBox>> ptr = std::make_shared<std::vector<OverlayBox>>();
156-
for (const auto& item : m_boxes){
157-
ptr->emplace_back(*item);
158-
}
159-
for (ContentListener* listener : m_content_listeners){
160-
listener->on_overlay_update_boxes(ptr);
132+
{
133+
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_box()");
134+
m_boxes.erase(&box);
135+
136+
// We create a newly allocated Box vector to avoid listener accessing
137+
// `m_boxes` asynchronously.
138+
for (const auto& item : m_boxes){
139+
ptr->emplace_back(*item);
140+
}
161141
}
142+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_boxes, ptr);
162143
}
163144
std::vector<OverlayBox> VideoOverlaySession::boxes() const{
164145
ReadSpinLock lg(m_lock);
@@ -175,30 +156,32 @@ std::vector<OverlayBox> VideoOverlaySession::boxes() const{
175156
//
176157

177158
void VideoOverlaySession::add_text(const OverlayText& text){
178-
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_text()");
179-
m_texts.insert(&text);
180-
push_text_update();
181-
}
182-
void VideoOverlaySession::remove_text(const OverlayText& text){
183-
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_text()");
184-
m_texts.erase(&text);
185-
push_text_update();
186-
}
159+
std::shared_ptr<std::vector<OverlayText>> ptr = std::make_shared<std::vector<OverlayText>>();
160+
{
161+
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_text()");
162+
m_texts.insert(&text);
187163

188-
void VideoOverlaySession::push_text_update(){
189-
if (m_content_listeners.empty()){
190-
return;
164+
// We create a newly allocated Box vector to avoid listener accessing
165+
// `m_texts` asynchronously.
166+
for (const auto& item : m_texts){
167+
ptr->emplace_back(*item);
168+
}
191169
}
192-
193-
// We create a newly allocated Box vector to avoid listener accessing
194-
// `m_texts` asynchronously.
170+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_text, ptr);
171+
}
172+
void VideoOverlaySession::remove_text(const OverlayText& text){
195173
std::shared_ptr<std::vector<OverlayText>> ptr = std::make_shared<std::vector<OverlayText>>();
196-
for (const auto& item : m_texts){
197-
ptr->emplace_back(*item);
198-
}
199-
for (ContentListener* listener : m_content_listeners){
200-
listener->on_overlay_update_text(ptr);
174+
{
175+
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_text()");
176+
m_texts.erase(&text);
177+
178+
// We create a newly allocated Box vector to avoid listener accessing
179+
// `m_texts` asynchronously.
180+
for (const auto& item : m_texts){
181+
ptr->emplace_back(*item);
182+
}
201183
}
184+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_text, ptr);
202185
}
203186
std::vector<OverlayText> VideoOverlaySession::texts() const{
204187
ReadSpinLock lg(m_lock);
@@ -215,30 +198,32 @@ std::vector<OverlayText> VideoOverlaySession::texts() const{
215198
//
216199

217200
void VideoOverlaySession::add_image(const OverlayImage& image){
218-
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_image()");
219-
m_images.insert(&image);
220-
push_image_update();
221-
}
222-
void VideoOverlaySession::remove_image(const OverlayImage& image){
223-
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_image()");
224-
m_images.erase(&image);
225-
push_image_update();
226-
}
201+
std::shared_ptr<std::vector<OverlayImage>> ptr = std::make_shared<std::vector<OverlayImage>>();
202+
{
203+
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_image()");
204+
m_images.insert(&image);
227205

228-
void VideoOverlaySession::push_image_update(){
229-
if (m_content_listeners.empty()){
230-
return;
206+
// We create a newly allocated Box vector to avoid listener accessing
207+
// `m_images` asynchronously.
208+
for (const auto& item : m_images){
209+
ptr->emplace_back(*item);
210+
}
231211
}
232-
233-
// We create a newly allocated Box vector to avoid listener accessing
234-
// `m_images` asynchronously.
212+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_images, ptr);
213+
}
214+
void VideoOverlaySession::remove_image(const OverlayImage& image){
235215
std::shared_ptr<std::vector<OverlayImage>> ptr = std::make_shared<std::vector<OverlayImage>>();
236-
for (const auto& item : m_images){
237-
ptr->emplace_back(*item);
238-
}
239-
for (ContentListener* listener : m_content_listeners){
240-
listener->on_overlay_update_images(ptr);
216+
{
217+
WriteSpinLock lg(m_lock, "VideoOverlaySession::remove_image()");
218+
m_images.erase(&image);
219+
220+
// We create a newly allocated Box vector to avoid listener accessing
221+
// `m_images` asynchronously.
222+
for (const auto& item : m_images){
223+
ptr->emplace_back(*item);
224+
}
241225
}
226+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_images, ptr);
242227
}
243228
std::vector<OverlayImage> VideoOverlaySession::images() const{
244229
ReadSpinLock lg(m_lock);
@@ -255,35 +240,36 @@ std::vector<OverlayImage> VideoOverlaySession::images() const{
255240
//
256241

257242
void VideoOverlaySession::add_log(std::string message, Color color){
258-
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_log_text()");
259-
m_log_texts.emplace_front(color, std::move(message));
243+
std::shared_ptr<std::vector<OverlayLogLine>> ptr = std::make_shared<std::vector<OverlayLogLine>>();
244+
{
245+
WriteSpinLock lg(m_lock, "VideoOverlaySession::add_log_text()");
246+
m_log_texts.emplace_front(color, std::move(message));
260247

261-
if (m_log_texts.size() > LOG_MAX_LINES){
262-
m_log_texts.pop_back();
263-
}
248+
if (m_log_texts.size() > LOG_MAX_LINES){
249+
m_log_texts.pop_back();
250+
}
264251

265-
push_log_text_update();
252+
// We create a newly allocated Box vector to avoid listener accessing
253+
// `m_log_texts` asynchronously.
254+
for(const auto& item : m_log_texts){
255+
ptr->emplace_back(item);
256+
}
257+
}
258+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_log, ptr);
266259
}
267260
void VideoOverlaySession::clear_log(){
268-
WriteSpinLock lg(m_lock, "VideoOverlaySession::clear_log_texts()");
269-
m_log_texts.clear();
270-
push_log_text_update();
271-
}
272-
273-
void VideoOverlaySession::push_log_text_update(){
274-
if (m_content_listeners.empty()){
275-
return;
276-
}
277-
278-
// We create a newly allocated Box vector to avoid listener accessing
279-
// `m_log_texts` asynchronously.
280261
std::shared_ptr<std::vector<OverlayLogLine>> ptr = std::make_shared<std::vector<OverlayLogLine>>();
281-
for(const auto& item : m_log_texts){
282-
ptr->emplace_back(item);
283-
}
284-
for (ContentListener* listener : m_content_listeners){
285-
listener->on_overlay_update_log(ptr);
262+
{
263+
WriteSpinLock lg(m_lock, "VideoOverlaySession::clear_log_texts()");
264+
m_log_texts.clear();
265+
266+
// We create a newly allocated Box vector to avoid listener accessing
267+
// `m_log_texts` asynchronously.
268+
for(const auto& item : m_log_texts){
269+
ptr->emplace_back(item);
270+
}
286271
}
272+
m_listeners.run_method_unique(&ContentListener::on_overlay_update_log, ptr);
287273
}
288274
std::vector<OverlayLogLine> VideoOverlaySession::log_texts() const{
289275
ReadSpinLock lg(m_lock);

SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlaySession.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <thread>
2525
#include "Common/Cpp/AbstractLogger.h"
2626
#include "Common/Cpp/Color.h"
27+
#include "Common/Cpp/ListenerSet.h"
2728
#include "Common/Cpp/Concurrency/SpinLock.h"
2829
#include "VideoOverlay.h"
2930
#include "VideoOverlayOption.h"
@@ -132,11 +133,6 @@ class VideoOverlaySession : public VideoOverlay{
132133
private:
133134
void stats_thread();
134135

135-
// Push updates to the various listeners.
136-
void push_box_update();
137-
void push_text_update();
138-
void push_image_update();
139-
void push_log_text_update();
140136

141137
private:
142138
Logger& m_logger;
@@ -152,7 +148,7 @@ class VideoOverlaySession : public VideoOverlay{
152148
std::list<OverlayStat*> m_stats_order;
153149
std::map<OverlayStat*, std::list<OverlayStat*>::iterator> m_stats;
154150

155-
std::set<ContentListener*> m_content_listeners;
151+
ListenerSet<ContentListener> m_listeners;
156152

157153
bool m_stopping = false;
158154
std::vector<OverlayStatSnapshot> m_stat_lines;

0 commit comments

Comments
 (0)