From c2934be8485376571066a652e94ed16ba2bc8d81 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 2 Apr 2021 00:21:03 +0200 Subject: Cancel all tasks when pressing escape to go to previous page or when closing the window --- TODO | 2 - include/AsyncTask.hpp | 13 +++++ include/GoogleCaptcha.hpp | 5 +- include/QuickMedia.hpp | 11 ++-- src/GoogleCaptcha.cpp | 8 +-- src/QuickMedia.cpp | 126 ++++++++++++++++------------------------------ 6 files changed, 69 insertions(+), 96 deletions(-) diff --git a/TODO b/TODO index cb64535..16b3513 100644 --- a/TODO +++ b/TODO @@ -92,13 +92,11 @@ Implement our own encryption for matrix. This is also needed to make forwarded m Modify matrix sync to download and parse json but not handle it, and then add a function to handle the json. This would allow us to remove all the mutex code if we would call that new method from the main thread (similar to chromium multithreading). Fetch replies/pinned message using multiple threads. Show in room tags list when there is a message in any of the rooms in the tag. -Cancel video download when pressing escape or closing window (important in matrix). Support webp. Then switch to the youtube thumbnails from the response json instead of hqdefault, to remove the black bars. Show images while they download by showing them as scanlines starting from the top. Needed for slow websites such as 4chan. Use curl parallel download instead of downloading with multiple threads. Handle matrix groups? (which also contains join, invite, leave...). Add functionality to ignore users in matrix. This is done with an ignore request and we wont get messages and invites from that user anymore. Also add option to ignore in the invites page. -Hide invites tab when there are no invites (to make room for other tabs in the future). Add keybind to go to invites page from any page. Show marker beside pinned messages tab name if there are new pinned messages. Make /logout work everywhere, not only in room message input. diff --git a/include/AsyncTask.hpp b/include/AsyncTask.hpp index 0d9c453..abdc0f8 100644 --- a/include/AsyncTask.hpp +++ b/include/AsyncTask.hpp @@ -26,6 +26,19 @@ namespace QuickMedia { return *this; } + AsyncTask(AsyncTask &&other) { + cancel(); + thread = std::move(other.thread); + future = std::move(other.future); + } + + AsyncTask& operator=(AsyncTask &&other) { + cancel(); + thread = std::move(other.thread); + future = std::move(other.future); + return *this; + } + ~AsyncTask() { cancel(); } diff --git a/include/GoogleCaptcha.hpp b/include/GoogleCaptcha.hpp index 62e47fa..f8063f3 100644 --- a/include/GoogleCaptcha.hpp +++ b/include/GoogleCaptcha.hpp @@ -1,5 +1,6 @@ #pragma once +#include "AsyncTask.hpp" #include #include #include @@ -15,7 +16,7 @@ namespace QuickMedia { }; using RequestChallengeResponse = std::function)>; - std::future google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback); + AsyncTask google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback); using PostSolutionResponse = std::function, std::optional)>; - std::future google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array selected_images, PostSolutionResponse solution_response_callback); + AsyncTask google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array selected_images, PostSolutionResponse solution_response_callback); } \ No newline at end of file diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index 3e8e7c7..497f202 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -6,6 +6,7 @@ #include "Storage.hpp" #include "Tab.hpp" #include "MessageQueue.hpp" +#include "AsyncTask.hpp" #include #include #include @@ -65,8 +66,8 @@ namespace QuickMedia { bool fetching_next_page_running = false; int fetched_page = 0; sf::Text search_result_text; - std::future fetch_future; - std::future next_page_future; + AsyncTask fetch_future; + AsyncTask next_page_future; }; class Program { @@ -152,9 +153,9 @@ namespace QuickMedia { std::string manga_id_base64; Json::Value content_storage_json; std::unordered_set watched_videos; - std::future search_suggestion_future; - std::future autocomplete_future; - std::future image_download_future; + AsyncTask search_suggestion_future; + AsyncTask autocomplete_future; + AsyncTask image_download_future; std::thread image_upscale_thead; MessageQueue images_to_upscale_queue; std::vector image_upscale_status; diff --git a/src/GoogleCaptcha.cpp b/src/GoogleCaptcha.cpp index 3bd91eb..137eb24 100644 --- a/src/GoogleCaptcha.cpp +++ b/src/GoogleCaptcha.cpp @@ -99,8 +99,8 @@ namespace QuickMedia { return strip_html_tags(html_source.substr(start_index, end_index - start_index)); } - std::future google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback) { - return std::async(std::launch::async, [challenge_response_callback, api_key, referer]() { + AsyncTask google_captcha_request_challenge(const std::string &api_key, const std::string &referer, RequestChallengeResponse challenge_response_callback) { + return AsyncTask([challenge_response_callback, api_key, referer]() { std::string captcha_url = "https://www.google.com/recaptcha/api/fallback?k=" + api_key; std::string response; std::vector additional_args = { @@ -131,8 +131,8 @@ namespace QuickMedia { return result; } - std::future google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array selected_images, PostSolutionResponse solution_response_callback) { - return std::async(std::launch::async, [solution_response_callback, api_key, captcha_id, selected_images]() { + AsyncTask google_captcha_post_solution(const std::string &api_key, const std::string &captcha_id, std::array selected_images, PostSolutionResponse solution_response_callback) { + return AsyncTask([solution_response_callback, api_key, captcha_id, selected_images]() { std::string captcha_url = "https://www.google.com/recaptcha/api/fallback?k=" + api_key; std::string response; std::vector additional_args = { diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 5281325..7dfd8dc 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -25,7 +25,6 @@ #include "../include/NetUtils.hpp" #include "../include/SfmlFixes.hpp" #include "../include/ResourceLoader.hpp" -#include "../include/AsyncTask.hpp" #include "../include/Utils.hpp" #include "../external/hash-library/sha256.h" @@ -259,11 +258,6 @@ namespace QuickMedia { Page *search_page; }; - template - static bool is_future_ready(const std::future &future) { - return future.valid() && future.wait_for(std::chrono::seconds(0)) == std::future_status::ready; - } - static Path get_recommended_filepath(const char *plugin_name) { Path video_history_dir = get_storage_dir().join("recommended"); if(create_directory_recursive(video_history_dir) != 0) { @@ -1155,6 +1149,7 @@ namespace QuickMedia { chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter MangaImagesPage *manga_images_page = static_cast(new_tabs[0].page.get()); window.setKeyRepeatEnabled(false); + downloading_chapter_url.clear(); while(true) { if(current_page == PageType::IMAGES) { window.setFramerateLimit(20); @@ -1327,12 +1322,12 @@ namespace QuickMedia { int next_page = tab_associated_data[selected_tab].fetched_page + 1; Page *page = tabs[selected_tab].page.get(); std::string update_search_text = tab_associated_data[selected_tab].update_search_text; - tab_associated_data[selected_tab].next_page_future = std::async(std::launch::async, [update_search_text, next_page, page]() { + tab_associated_data[selected_tab].next_page_future = [update_search_text, next_page, page]() { BodyItems result_items; if(page->get_page(update_search_text, next_page, result_items) != PluginResult::OK) fprintf(stderr, "Failed to get next page (page %d)\n", next_page); return result_items; - }); + }; } } else if(event.key.code == sf::Keyboard::Up || (event.key.control && event.key.code == sf::Keyboard::K)) { tabs[selected_tab].body->select_previous_item(); @@ -1415,11 +1410,11 @@ namespace QuickMedia { tab_associated_data[selected_tab].fetch_type = FetchType::LAZY; tab_associated_data[selected_tab].search_result_text.setString("Fetching page..."); LazyFetchPage *lazy_fetch_page = static_cast(tabs[selected_tab].page.get()); - tab_associated_data[selected_tab].fetch_future = std::async(std::launch::async, [lazy_fetch_page]() { + tab_associated_data[selected_tab].fetch_future = [lazy_fetch_page]() { FetchResult fetch_result; fetch_result.result = lazy_fetch_page->lazy_fetch(fetch_result.body_items); return fetch_result; - }); + }; } for(size_t i = 0; i < tabs.size(); ++i) { @@ -1427,7 +1422,7 @@ namespace QuickMedia { tabs[i].page->update(); - if(associated_data.fetching_next_page_running && is_future_ready(associated_data.next_page_future)) { + if(associated_data.fetching_next_page_running && associated_data.next_page_future.ready()) { BodyItems new_body_items = associated_data.next_page_future.get(); fprintf(stderr, "Finished fetching page %d, num new messages: %zu\n", associated_data.fetched_page + 1, new_body_items.size()); size_t num_new_messages = new_body_items.size(); @@ -1445,14 +1440,14 @@ namespace QuickMedia { associated_data.fetch_type = FetchType::SEARCH; associated_data.search_result_text.setString("Searching..."); Page *page = tabs[i].page.get(); - associated_data.fetch_future = std::async(std::launch::async, [update_search_text, page]() { + associated_data.fetch_future = [update_search_text, page]() { FetchResult fetch_result; fetch_result.result = search_result_to_plugin_result(page->search(update_search_text, fetch_result.body_items)); return fetch_result; - }); + }; } - if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && is_future_ready(associated_data.fetch_future)) { + if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) { if(!associated_data.search_text_updated) { FetchResult fetch_result = associated_data.fetch_future.get(); tabs[i].body->items = std::move(fetch_result.body_items); @@ -1470,7 +1465,7 @@ namespace QuickMedia { associated_data.fetch_status = FetchStatus::NONE; } - if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::LAZY && is_future_ready(associated_data.fetch_future)) { + if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::LAZY && associated_data.fetch_future.ready()) { associated_data.lazy_fetch_finished = true; FetchResult fetch_result = associated_data.fetch_future.get(); tabs[i].body->items = std::move(fetch_result.body_items); @@ -1496,14 +1491,7 @@ namespace QuickMedia { } page_end: - // TODO: This is needed, because you cant terminate futures without causing an exception to be thrown and its not safe anyways. - // Need a way to solve this, we dont want to wait for a search to finish when navigating backwards - for(TabAssociatedData &associated_data : tab_associated_data) { - if(associated_data.next_page_future.valid()) - associated_data.next_page_future.get(); - if(associated_data.fetch_future.valid()) - associated_data.fetch_future.get(); - } + {} } static bool youtube_url_extract_id(const std::string &youtube_url, std::string &youtube_video_id) { @@ -1672,11 +1660,7 @@ namespace QuickMedia { TaskResult Program::run_task_with_loading_screen(std::function callback) { assert(std::this_thread::get_id() == main_thread_id); - std::promise result_promise; - std::future future = result_promise.get_future(); - std::thread task_thread([](std::promise &&promise, std::function callback) { - promise.set_value(callback()); - }, std::move(result_promise), std::move(callback)); + AsyncTask task = callback; window_size.x = window.getSize().x; window_size.y = window.getSize().y; @@ -1684,9 +1668,7 @@ namespace QuickMedia { while(window.isOpen()) { while(window.pollEvent(event)) { if(event.type == sf::Event::Closed) { - program_kill_in_thread(task_thread.get_id()); - task_thread.join(); - future.get(); + task.cancel(); current_page = PageType::EXIT; window.close(); return TaskResult::CANCEL; @@ -1696,18 +1678,16 @@ namespace QuickMedia { sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); window.setView(sf::View(visible_area)); } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) { - program_kill_in_thread(task_thread.get_id()); - task_thread.join(); - future.get(); + task.cancel(); return TaskResult::CANCEL; } } - if(is_future_ready(future)) { - task_thread.join(); - if(!future.get()) + if(task.ready()) { + if(task.get()) + return TaskResult::TRUE; + else return TaskResult::FALSE; - break; } window.clear(back_color); @@ -1765,7 +1745,7 @@ namespace QuickMedia { return; } - TaskResult download_file_result = run_task_with_loading_screen([this, &video_path, video_url]() { + TaskResult download_file_result = run_task_with_loading_screen([&video_path, video_url]() { return download_to_file(video_url, video_path.data, {}, true) == DownloadResult::OK; }); switch(download_file_result) { @@ -2209,15 +2189,12 @@ namespace QuickMedia { return; downloading_chapter_url = images_page->get_url(); - if(image_download_future.valid()) { - // TODO: Cancel download instead of waiting for the last page to finish - image_download_cancel = true; - image_download_future.get(); - image_download_cancel = false; - } + image_download_cancel = true; + image_download_future.cancel(); + image_download_cancel = false; Path content_cache_dir_ = content_cache_dir; - image_download_future = std::async(std::launch::async, [images_page, content_cache_dir_, this]() { + image_download_future = [images_page, content_cache_dir_, this]() { // TODO: Download images in parallel int page = 1; images_page->for_each_page_in_chapter([content_cache_dir_, &page, images_page, this](const std::string &url) { @@ -2326,7 +2303,7 @@ namespace QuickMedia { return true; }); - }); + }; } int Program::image_page(MangaImagesPage *images_page, Body *chapters_body) { @@ -2532,11 +2509,8 @@ namespace QuickMedia { end_of_images_page: if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) { image_download_cancel = true; - if(image_download_future.valid()) { - // TODO: Cancel download instead of waiting for the last page to finish - image_download_future.get(); - image_download_cancel = false; - } + image_download_future.cancel(); + image_download_cancel = false; images_to_upscale_queue.clear(); image_upscale_status.clear(); } @@ -2623,11 +2597,8 @@ namespace QuickMedia { if(current_page != PageType::IMAGES && current_page != PageType::IMAGES_CONTINUOUS) { image_download_cancel = true; - if(image_download_future.valid()) { - // TODO: Cancel download instead of waiting for the last page to finish - image_download_future.get(); - image_download_cancel = false; - } + image_download_future.cancel(); + image_download_cancel = false; images_to_upscale_queue.clear(); image_upscale_status.clear(); } @@ -2655,10 +2626,10 @@ namespace QuickMedia { }; NavigationStage navigation_stage = NavigationStage::VIEWING_COMMENTS; - std::future captcha_request_future; - std::future captcha_post_solution_future; - std::future post_comment_future; - std::future load_image_future; + AsyncTask captcha_request_future; + AsyncTask captcha_post_solution_future; + AsyncTask post_comment_future; + AsyncTask load_image_future; bool downloading_image = false; sf::Texture captcha_texture; sf::Sprite captcha_sprite; @@ -2692,7 +2663,7 @@ namespace QuickMedia { // TODO: Make google captcha images load texture in the main thread, otherwise high cpu usage. I guess its fine right now because of only 1 image? // TODO: Make this work with other sites than 4chan - auto request_google_captcha_image = [this, &captcha_texture, &captcha_image_mutex, &navigation_stage, &captcha_sprite, &challenge_description_text](GoogleCaptchaChallengeInfo &challenge_info) { + auto request_google_captcha_image = [&captcha_texture, &captcha_image_mutex, &navigation_stage, &captcha_sprite, &challenge_description_text](GoogleCaptchaChallengeInfo &challenge_info) { std::string payload_image_data; DownloadResult download_image_result = download_to_string(challenge_info.payload_url, payload_image_data, {}); if(download_image_result == DownloadResult::OK) { @@ -2712,7 +2683,7 @@ namespace QuickMedia { } }; - auto request_new_google_captcha_challenge = [this, &selected_captcha_images, &navigation_stage, &captcha_request_future, &request_google_captcha_image, &challenge_info]() { + auto request_new_google_captcha_challenge = [&selected_captcha_images, &navigation_stage, &captcha_request_future, &request_google_captcha_image, &challenge_info]() { fprintf(stderr, "Solving captcha!\n"); navigation_stage = NavigationStage::SOLVING_POST_CAPTCHA; for(size_t i = 0; i < selected_captcha_images.size(); ++i) { @@ -2776,17 +2747,17 @@ namespace QuickMedia { comment_to_post = std::move(text); if(!captcha_post_id.empty() && captcha_solved_time.getElapsedTime().asSeconds() < 120) { - post_comment_future = std::async(std::launch::async, [&post_comment]() -> bool { + post_comment_future = [&post_comment]() -> bool { post_comment(); return true; - }); + }; } else if(thread_page->get_pass_id().empty()) { request_new_google_captcha_challenge(); } else if(!thread_page->get_pass_id().empty()) { - post_comment_future = std::async(std::launch::async, [&post_comment]() -> bool { + post_comment_future = [&post_comment]() -> bool { post_comment(); return true; - }); + }; } return true; }; @@ -2872,11 +2843,10 @@ namespace QuickMedia { video_content_page(thread_page, thread_page, "", true, next_items, 0); redraw = true; } else { - if(downloading_image && load_image_future.valid()) - load_image_future.get(); + load_image_future.cancel(); downloading_image = true; navigation_stage = NavigationStage::VIEWING_ATTACHED_IMAGE; - load_image_future = std::async(std::launch::async, [this, &thread_body]() { + load_image_future = [&thread_body]() { std::string image_data; BodyItem *selected_item = thread_body->get_selected(); if(!selected_item || selected_item->attached_content_url.empty()) { @@ -2887,7 +2857,7 @@ namespace QuickMedia { image_data.clear(); } return image_data; - }); + }; } } } @@ -3071,7 +3041,7 @@ namespace QuickMedia { } else if(navigation_stage == NavigationStage::VIEWING_ATTACHED_IMAGE) { // TODO: Use image instead of data with string. texture->loadFromMemory creates a temporary image anyways that parses the string. std::string image_data; - if(downloading_image && is_future_ready(load_image_future)) { + if(downloading_image && load_image_future.ready()) { downloading_image = false; image_data = load_image_future.get(); @@ -3124,16 +3094,6 @@ namespace QuickMedia { } window.display(); } - - // TODO: Instead of waiting for them, kill them somehow - if(captcha_request_future.valid()) - captcha_request_future.get(); - if(captcha_post_solution_future.valid()) - captcha_post_solution_future.get(); - if(post_comment_future.valid()) - post_comment_future.get(); - if(load_image_future.valid()) - load_image_future.get(); } void Program::chat_login_page() { @@ -3232,7 +3192,7 @@ namespace QuickMedia { struct ChatTab { std::unique_ptr body; - std::future future; + AsyncTask future; sf::Text text; }; -- cgit v1.2.3