Skip to content

Commit 84ae3a5

Browse files
authored
Merge pull request #99 from supermarsx/smx/implement-thread-safe-queue-for-badge-spawning-2025-11-01
Route badge spawns through a thread-safe queue
2 parents 8b12fcf + c3e5762 commit 84ae3a5

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

src/app/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ int main(int argc, char **argv) {
259259
bx = cx;
260260
by = cy;
261261
}
262-
overlay.spawn_badge(bx, by);
262+
overlay.enqueue_spawn(bx, by);
263263
}
264264
}
265265
},

src/overlay/overlay.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void stbi_image_free(void *);
3333
#include <deque>
3434
#include <mutex>
3535
#include <atomic>
36+
#include <queue>
3637

3738
#include <climits>
3839
#ifdef _WIN32
@@ -101,6 +102,8 @@ class Overlay {
101102
void shutdown();
102103
void spawn_badge(int sprite, float x, float y);
103104
void spawn_badge(float x, float y);
105+
void enqueue_spawn(int sprite, float x, float y);
106+
void enqueue_spawn(float x, float y);
104107
void run(std::stop_token st);
105108
void stop();
106109
void refresh_from_config(const app::Config &cfg);
@@ -122,6 +125,7 @@ class Overlay {
122125
void render();
123126
void update_frame_interval();
124127
void apply_pending_config();
128+
void process_spawn_queue();
125129
struct AtlasData {
126130
std::vector<Sprite> sprites;
127131
std::unordered_map<std::string, int> lookup;
@@ -134,6 +138,12 @@ class Overlay {
134138
static std::optional<std::filesystem::path>
135139
normalize_path(const std::optional<std::filesystem::path> &path);
136140

141+
struct SpawnRequest {
142+
std::optional<int> sprite;
143+
float x;
144+
float y;
145+
};
146+
137147
struct PendingConfig {
138148
std::string spawn_strategy;
139149
int badge_min_px = 60;
@@ -178,6 +188,8 @@ class Overlay {
178188
std::optional<PendingConfig> m_pending_config;
179189
std::atomic<bool> m_has_pending_config{false};
180190
std::mutex m_spawn_config_mutex;
191+
std::mutex m_spawn_queue_mutex;
192+
std::queue<SpawnRequest> m_spawn_queue;
181193
};
182194

183195
void Overlay::update_frame_interval() {
@@ -759,6 +771,16 @@ void Overlay::spawn_badge(int sprite, float x, float y) {
759771
spawn_badge_locked(sprite, x, y);
760772
}
761773

774+
void Overlay::enqueue_spawn(float x, float y) {
775+
std::lock_guard<std::mutex> lock(m_spawn_queue_mutex);
776+
m_spawn_queue.push(SpawnRequest{std::nullopt, x, y});
777+
}
778+
779+
void Overlay::enqueue_spawn(int sprite, float x, float y) {
780+
std::lock_guard<std::mutex> lock(m_spawn_queue_mutex);
781+
m_spawn_queue.push(SpawnRequest{std::make_optional(sprite), x, y});
782+
}
783+
762784
int Overlay::select_sprite_locked() {
763785
if (m_selector_indices.empty()) {
764786
return 0;
@@ -827,6 +849,23 @@ void Overlay::spawn_badge_locked(int sprite, float x, float y) {
827849
m_spawn_times.push_back(now);
828850
}
829851

852+
void Overlay::process_spawn_queue() {
853+
std::queue<SpawnRequest> local;
854+
{
855+
std::lock_guard<std::mutex> lock(m_spawn_queue_mutex);
856+
std::swap(local, m_spawn_queue);
857+
}
858+
while (!local.empty()) {
859+
auto &request = local.front();
860+
if (request.sprite.has_value()) {
861+
spawn_badge(request.sprite.value(), request.x, request.y);
862+
} else {
863+
spawn_badge(request.x, request.y);
864+
}
865+
local.pop();
866+
}
867+
}
868+
830869
void Overlay::stop() { m_running = false; }
831870

832871
void Overlay::update(float dt) {
@@ -900,6 +939,7 @@ void Overlay::run(std::stop_token st) {
900939
if (m_has_pending_config.exchange(false, std::memory_order_acq_rel)) {
901940
apply_pending_config();
902941
}
942+
process_spawn_queue();
903943
auto frame = std::chrono::microseconds(m_frame_interval_us.load());
904944
if (m_paused.load()) {
905945
std::this_thread::sleep_for(frame);

0 commit comments

Comments
 (0)