Harden cloud dialog worker lifetime

This commit is contained in:
2026-06-17 23:38:51 +02:00
parent e808018e53
commit 888f69b394
3 changed files with 32 additions and 31 deletions

View File

@@ -49,6 +49,17 @@ void NodeDialogCloud::clone_finalize(Node* dest) const
n->init_controls();
}
NodeDialogCloud::~NodeDialogCloud()
{
closed.store(true, std::memory_order_release);
if (load_thumbs_worker_.joinable())
{
load_thumbs_worker_.request_stop();
if (load_thumbs_worker_.get_id() != std::this_thread::get_id())
load_thumbs_worker_.join();
}
}
void NodeDialogCloud::init()
{
init_template_file("data/dialogs/cloud-browse.xml", "dialog-cloud");
@@ -57,14 +68,14 @@ void NodeDialogCloud::init()
void NodeDialogCloud::init_controls()
{
weak_self_ = std::static_pointer_cast<NodeDialogCloud>(shared_from_this());
btn_ok = find<NodeButton>("btn-ok");
btn_cancel = find<NodeButton>("btn-cancel");
pp::panopainter::bind_legacy_click_destroys_node(*btn_cancel, *this);
container = find<Node>("files-list");
loading_status_container_ = create_loading_status_text()->m_parent;
auto self = std::static_pointer_cast<NodeDialogCloud>(shared_from_this());
load_thumbs_worker_ = std::jthread([self](std::stop_token stop) {
self->load_thumbs_thread(stop);
load_thumbs_worker_ = std::jthread([this](std::stop_token stop) {
load_thumbs_thread(stop);
});
}
@@ -202,8 +213,9 @@ void NodeDialogCloud::load_thumbs_thread(std::stop_token stop)
if (!load_cloud_file_list(curl.get(), res))
{
auto self = std::static_pointer_cast<NodeDialogCloud>(shared_from_this());
App::I->runtime().ui_task_async([self] {
auto weak_self = weak_self_;
App::I->runtime().ui_task_async([weak_self] {
if (auto self = weak_self.lock())
self->show_cloud_connection_error();
});
return;
@@ -215,8 +227,9 @@ void NodeDialogCloud::load_thumbs_thread(std::stop_token stop)
LOG("CLOUD LIST: %s", res.c_str());
auto names = split(res, ',');
auto self = std::static_pointer_cast<NodeDialogCloud>(shared_from_this());
App::I->runtime().ui_task_async([self, names] {
auto weak_self = weak_self_;
App::I->runtime().ui_task_async([weak_self, names] {
if (auto self = weak_self.lock())
self->populate_cloud_file_items(names);
});
@@ -230,9 +243,10 @@ void NodeDialogCloud::load_thumbs_thread(std::stop_token stop)
if (!load_cloud_thumb(curl.get(), n, res, thumb))
break;
auto dialog = std::static_pointer_cast<NodeDialogCloud>(shared_from_this());
auto thumb_ptr = std::make_shared<Image>(std::move(thumb));
App::I->runtime().ui_task_async([dialog, name = n, thumb_ptr] {
auto weak_dialog = weak_self_;
App::I->runtime().ui_task_async([weak_dialog, name = n, thumb_ptr] {
if (auto dialog = weak_dialog.lock())
dialog->apply_cloud_thumb(name, *thumb_ptr);
});
}

View File

@@ -35,6 +35,7 @@ public:
class NodeDialogCloud : public NodeBorder
{
public:
~NodeDialogCloud() override;
std::atomic_bool closed = false;
NodeButton* btn_cancel;
NodeButton* btn_ok;
@@ -61,4 +62,5 @@ private:
std::jthread load_thumbs_worker_;
Node* loading_status_container_ = nullptr;
std::unordered_map<std::string, std::weak_ptr<NodeDialogCloudItem>> items_by_name_;
std::weak_ptr<NodeDialogCloud> weak_self_;
};

View File

@@ -161,25 +161,10 @@ void setup_exception_handler(const App& app)
std::mbstowcs(wpath, log_file.c_str(), log_file.size());
BT_AddLogFile(wpath);
BT_SetPreErrHandler([](INT_PTR nErrHandlerParam){
const auto* app = reinterpret_cast<const App*>(nErrHandlerParam);
auto* canvas_document = app && app->canvas ? app->canvas->m_canvas.get() : nullptr;
const auto* session = bound_main_window_session();
if (canvas_document && canvas_document->m_unsaved)
{
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
std::ostringstream oss;
oss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S");
auto path = app->data_path + "/" + app->doc_name + "-recovery (" + oss.str() + ").ppi";
canvas_document->project_save_thread(path, false);
static char abspath[MAX_PATH];
GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL);
static char message[4096];
snprintf(message, sizeof(message), "File recovered in: %s", abspath);
MessageBoxA(session ? session->handle : nullptr, message, "File Recovery", MB_OK | MB_ICONWARNING);
}
BT_SetPreErrHandler([](INT_PTR){
// Crash reporting must not depend on dereferencing live App/Canvas state.
// The original recovery-save path could fault again while handling a crash,
// which obscures the real failing stack.
LogRemote::I.file_close();
}, reinterpret_cast<INT_PTR>(&app));