yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
wasm_loading_manager.cc
Go to the documentation of this file.
1// clang-format off
2#ifdef __EMSCRIPTEN__
3
5
6#include <emscripten.h>
7#include <emscripten/html5.h>
8
9namespace yaze {
10namespace app {
11namespace platform {
12
13// JavaScript interface functions
14// Note: These functions take uint32_t js_id, not the full 64-bit handle.
15// The JS layer only sees the low 32 bits which are unique IDs for UI elements.
16EM_JS(void, js_create_loading_indicator, (uint32_t id, const char* task_name), {
17 if (typeof window.createLoadingIndicator === 'function') {
18 window.createLoadingIndicator(id, UTF8ToString(task_name));
19 } else {
20 console.warn('createLoadingIndicator not defined. Include loading_indicator.js');
21 }
22});
23
24EM_JS(void, js_update_loading_progress, (uint32_t id, float progress, const char* message), {
25 if (typeof window.updateLoadingProgress === 'function') {
26 window.updateLoadingProgress(id, progress, UTF8ToString(message));
27 }
28});
29
30EM_JS(void, js_remove_loading_indicator, (uint32_t id), {
31 if (typeof window.removeLoadingIndicator === 'function') {
32 window.removeLoadingIndicator(id);
33 }
34});
35
36EM_JS(bool, js_check_loading_cancelled, (uint32_t id), {
37 if (typeof window.isLoadingCancelled === 'function') {
38 return window.isLoadingCancelled(id);
39 }
40 return false;
41});
42
43EM_JS(void, js_show_cancel_button, (uint32_t id), {
44 if (typeof window.showCancelButton === 'function') {
45 window.showCancelButton(id, function() {});
46 }
47});
48
49// WasmLoadingManager implementation
50WasmLoadingManager& WasmLoadingManager::GetInstance() {
51 static WasmLoadingManager instance;
52 return instance;
53}
54
55WasmLoadingManager::WasmLoadingManager() {}
56
57WasmLoadingManager::~WasmLoadingManager() {
58 std::lock_guard<std::mutex> lock(mutex_);
59 for (const auto& [handle, op] : operations_) {
60 if (op && op->active) {
61 js_remove_loading_indicator(GetJsId(handle));
62 }
63 }
64}
65
66WasmLoadingManager::LoadingHandle WasmLoadingManager::BeginLoading(const std::string& task_name) {
67 auto& instance = GetInstance();
68
69 // Generate unique JS ID and generation counter atomically
70 uint32_t js_id = instance.next_js_id_.fetch_add(1);
71 uint32_t generation = instance.generation_counter_.fetch_add(1);
72
73 // Create the full 64-bit handle
74 LoadingHandle handle = MakeHandle(js_id, generation);
75
76 auto operation = std::make_unique<LoadingOperation>();
77 operation->task_name = task_name;
78 operation->active = true;
79 operation->generation = generation;
80
81 {
82 std::lock_guard<std::mutex> lock(instance.mutex_);
83 instance.operations_[handle] = std::move(operation);
84 }
85
86 // JS functions receive only the 32-bit ID
87 js_create_loading_indicator(js_id, task_name.c_str());
88 js_show_cancel_button(js_id);
89 return handle;
90}
91
92void WasmLoadingManager::UpdateProgress(LoadingHandle handle, float progress) {
93 if (handle == kInvalidHandle) return;
94 auto& instance = GetInstance();
95 std::string message;
96 uint32_t js_id = GetJsId(handle);
97
98 {
99 std::lock_guard<std::mutex> lock(instance.mutex_);
100 auto it = instance.operations_.find(handle);
101 if (it == instance.operations_.end() || !it->second->active) return;
102 it->second->progress = progress;
103 message = it->second->message;
104 }
105
106 js_update_loading_progress(js_id, progress, message.c_str());
107}
108
109void WasmLoadingManager::UpdateMessage(LoadingHandle handle, const std::string& message) {
110 if (handle == kInvalidHandle) return;
111 auto& instance = GetInstance();
112 float progress = 0.0f;
113 uint32_t js_id = GetJsId(handle);
114
115 {
116 std::lock_guard<std::mutex> lock(instance.mutex_);
117 auto it = instance.operations_.find(handle);
118 if (it == instance.operations_.end() || !it->second->active) return;
119 it->second->message = message;
120 progress = it->second->progress;
121 }
122
123 js_update_loading_progress(js_id, progress, message.c_str());
124}
125
126bool WasmLoadingManager::IsCancelled(LoadingHandle handle) {
127 if (handle == kInvalidHandle) return false;
128 auto& instance = GetInstance();
129 uint32_t js_id = GetJsId(handle);
130
131 // Check JS cancellation state first (outside lock)
132 bool js_cancelled = js_check_loading_cancelled(js_id);
133
134 {
135 std::lock_guard<std::mutex> lock(instance.mutex_);
136 auto it = instance.operations_.find(handle);
137 if (it == instance.operations_.end() || !it->second->active) {
138 return true;
139 }
140 if (js_cancelled && !it->second->cancelled.load()) {
141 it->second->cancelled.store(true);
142 }
143 return it->second->cancelled.load();
144 }
145}
146
147void WasmLoadingManager::EndLoading(LoadingHandle handle) {
148 if (handle == kInvalidHandle) return;
149 auto& instance = GetInstance();
150 uint32_t js_id = GetJsId(handle);
151
152 {
153 std::lock_guard<std::mutex> lock(instance.mutex_);
154 auto it = instance.operations_.find(handle);
155 if (it != instance.operations_.end()) {
156 // Mark inactive and erase immediately - no async delay.
157 // The 64-bit handle with generation counter ensures that even if
158 // a new operation starts immediately with the same js_id, it will
159 // have a different generation and thus a different full handle.
160 it->second->active = false;
161
162 // Clear arena handle if it matches this handle
163 if (instance.arena_handle_ == handle) {
164 instance.arena_handle_ = kInvalidHandle;
165 }
166
167 // Erase synchronously - safe because generation counter prevents reuse
168 instance.operations_.erase(it);
169 }
170 }
171
172 // Remove the JS UI element after releasing the lock
173 js_remove_loading_indicator(js_id);
174}
175
176bool WasmLoadingManager::ReportArenaProgress(int current, int total, const std::string& item_name) {
177 auto& instance = GetInstance();
178 LoadingHandle handle;
179 uint32_t js_id;
180 float progress = 0.0f;
181 bool should_update_progress = false;
182 bool should_update_message = false;
183 bool is_cancelled = false;
184
185 {
186 std::lock_guard<std::mutex> lock(instance.mutex_);
187 handle = instance.arena_handle_;
188 if (handle == kInvalidHandle) return true;
189
190 js_id = GetJsId(handle);
191
192 // Check if the operation still exists and is active
193 auto it = instance.operations_.find(handle);
194 if (it == instance.operations_.end() || !it->second->active) {
195 // Handle is stale, clear it and return
196 instance.arena_handle_ = kInvalidHandle;
197 return true;
198 }
199
200 // Update progress if applicable
201 if (total > 0) {
202 progress = static_cast<float>(current) / static_cast<float>(total);
203 it->second->progress = progress;
204 should_update_progress = true;
205 } else {
206 progress = it->second->progress;
207 }
208
209 // Update message if applicable
210 if (!item_name.empty()) {
211 it->second->message = item_name;
212 should_update_message = true;
213 }
214
215 // Check cancellation status
216 is_cancelled = it->second->cancelled.load();
217 }
218
219 // Perform JS calls outside the lock to avoid blocking
220 if (should_update_progress || should_update_message) {
221 js_update_loading_progress(js_id, progress,
222 should_update_message ? item_name.c_str() : "");
223 }
224
225 return !is_cancelled;
226}
227
228void WasmLoadingManager::SetArenaHandle(LoadingHandle handle) {
229 auto& instance = GetInstance();
230 std::lock_guard<std::mutex> lock(instance.mutex_);
231 instance.arena_handle_ = handle;
232}
233
234void WasmLoadingManager::ClearArenaHandle() {
235 auto& instance = GetInstance();
236 std::lock_guard<std::mutex> lock(instance.mutex_);
237 instance.arena_handle_ = kInvalidHandle;
238}
239
240} // namespace platform
241} // namespace app
242} // namespace yaze
243
244#endif // __EMSCRIPTEN__
245// clang-format on
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })