2021-07-25 06:37:13 +01:00
|
|
|
/*
|
|
|
|
* async_task.hpp
|
|
|
|
*
|
2024-04-12 10:47:36 +01:00
|
|
|
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
|
2021-07-25 06:37:13 +01:00
|
|
|
*
|
|
|
|
* Based on attcs' C++ implementation at:
|
|
|
|
* https://github.com/attcs/AsyncTask/blob/master/asynctask.h.
|
|
|
|
*
|
|
|
|
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
|
|
|
*
|
|
|
|
* nxdumptool is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* nxdumptool is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#ifndef __ASYNC_TASK_HPP__
|
|
|
|
#define __ASYNC_TASK_HPP__
|
|
|
|
|
|
|
|
#include <exception>
|
|
|
|
#include <future>
|
2021-07-27 16:00:09 +01:00
|
|
|
#include <mutex>
|
2021-07-25 06:37:13 +01:00
|
|
|
|
2021-07-28 00:47:12 +01:00
|
|
|
namespace nxdt::tasks
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
/* Used by AsyncTask to throw exceptions whenever required. */
|
2024-04-25 00:49:04 +01:00
|
|
|
class AsyncTaskException: std::exception
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
public:
|
2024-04-25 00:49:04 +01:00
|
|
|
enum class eEx: int
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
TaskIsAlreadyRunning, ///< Task is already running.
|
|
|
|
TaskIsAlreadyFinished, ///< Task is already finished.
|
|
|
|
TaskIsPending, ///< Task hasn't been executed.
|
|
|
|
TaskIsCancelled, ///< Task has been cancelled.
|
|
|
|
TaskWaitTimeout ///< Timed out while waiting for the task to finish.
|
|
|
|
};
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
eEx e;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
AsyncTaskException() = default;
|
|
|
|
AsyncTaskException(eEx e) : e(e) { }
|
|
|
|
};
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Used by AsyncTask to indicate the current status of the asynchronous task. */
|
2024-04-25 00:49:04 +01:00
|
|
|
enum class AsyncTaskStatus: int
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
PENDING, ///< The task hasn't been executed yet.
|
|
|
|
RUNNING, ///< The task is currently running.
|
|
|
|
FINISHED ///< The task is finished.
|
|
|
|
};
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Asynchronous task handler class. */
|
|
|
|
template<typename Progress, typename Result, typename... Params>
|
|
|
|
class AsyncTask
|
|
|
|
{
|
|
|
|
private:
|
2023-02-24 20:32:58 +00:00
|
|
|
std::recursive_mutex m_mtx{};
|
2021-07-25 06:37:13 +01:00
|
|
|
AsyncTaskStatus m_status = AsyncTaskStatus::PENDING;
|
|
|
|
Result m_result{};
|
|
|
|
std::future<Result> m_future{};
|
2021-07-27 16:00:09 +01:00
|
|
|
Progress m_progress{};
|
|
|
|
bool m_cancelled = false, m_rethrowException = false;
|
2021-07-25 06:37:13 +01:00
|
|
|
std::exception_ptr m_exceptionPtr{};
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2024-04-25 00:49:04 +01:00
|
|
|
/* Runs on the calling thread after DoInBackground() finishes execution. */
|
|
|
|
void Finish(Result&& result)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Copy result. */
|
|
|
|
this->m_result = result;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-27 16:00:09 +01:00
|
|
|
/* Update status. */
|
|
|
|
this->m_status = AsyncTaskStatus::FINISHED;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2024-04-15 00:53:43 +01:00
|
|
|
/* Run appropiate post-execution callback. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->IsCancelled())
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2024-04-25 00:49:04 +01:00
|
|
|
this->OnCancelled(this->m_result);
|
2021-07-25 06:37:13 +01:00
|
|
|
} else {
|
2024-04-25 00:49:04 +01:00
|
|
|
this->OnPostExecute(this->m_result);
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Rethrow asynchronous task exception (if available). */
|
2021-07-27 16:00:09 +01:00
|
|
|
if (this->m_rethrowException && this->m_exceptionPtr) std::rethrow_exception(this->m_exceptionPtr);
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
protected:
|
2021-07-30 22:07:26 +01:00
|
|
|
/* Set class as non-copyable and non-moveable. */
|
2021-07-25 06:37:13 +01:00
|
|
|
NON_COPYABLE(AsyncTask);
|
2021-07-30 22:07:26 +01:00
|
|
|
NON_MOVEABLE(AsyncTask);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2024-04-19 11:08:44 +01:00
|
|
|
virtual ~AsyncTask() noexcept
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
/* Return right away if the task isn't running. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->GetStatus() != AsyncTaskStatus::RUNNING) return;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Cancel task. This won't do anything if it has already been cancelled. */
|
2024-04-25 00:49:04 +01:00
|
|
|
this->Cancel();
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return right away if the result was already retrieved. */
|
|
|
|
if (!this->m_future.valid()) return;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Wait until a result is provided by the task thread. */
|
Add DataTransferTaskFrame and GameCardImageDumpTaskFrame classes
DataTransferTaskFrame is a template class that's derived from brls::AppletFrame, which automatically starts a background task using an internal object belonging to a class derived from DataTransferTask. A DataTransferProgressDisplay view is used to show progress updates. If the background task hits an error, the class takes care of switching to an ErrorFrame view with the corresponding error message.
GameCardImageDumpTaskFrame is a derived class of DataTransferTaskFrame that uses a GameCardImageDumpTask object as its background task. In layman's terms, this provides a way to fully dump gamecard images using the new UI.
DataTransferTaskFrame depends on the newly added is_base_template helper from goneskiing to check if the class for the provided task is derived from DataTransferTask.
Other changes include:
* DataTransferProgressDisplay: rename setProgress() method to SetProgress().
* DataTransferTask: move post-task-execution code into its own new private method, PostExecutionCallback(), and update both OnCancelled() and OnPostExecute() callbacks to invoke it.
* DataTransferTask: update OnProgressUpdate() to allow sending a last progress update to all event subscribers even if the background task was cancelled.
* DataTransferTask: update OnProgressUpdate() to allow sending a first progress update if no data has been transferred but the total transfer size is already known.
* DataTransferTask: update OnProgressUpdate() to avoid calculating the ETA if the speed isn't greater than 0.
* DumpOptionsFrame: remove UpdateOutputStorages() method.
* DumpOptionsFrame: update class to use the cached output storage value from our RootView.
* DumpOptionsFrame: add GenerateOutputStoragesVector() method, which is used to avoid setting dummy options while initializing the output storages SelectListItem.
* DumpOptionsFrame: update UMS task callback to add the rest of the leftover logic from UpdateOutputStorages().
* DumpOptionsFrame: update RegisterButtonListener() to use a wrapper callback around the user-provided callback to check if the USB host was selected as the output storage but no USB host connection is available.
* ErrorFrame: use const references for all input string arguments.
* FileWriter: fix a localization stirng name typo.
* FileWriter: fix an exception that was previously being thrown by a fmt::format() call because of a wrong format specifier.
* FocusableItem: add a static assert to check if the provided ViewType is derived from brls::View.
* gamecard: redefine global gamecard status variable as an atomic unsigned 8-bit integer, which fixes a "status-hopping" issue previously experienced by repeating tasks running under other threads that periodically call gamecardGetStatus().
* GameCardImageDumpOptionsFrame: define GAMECARD_TOGGLE_ITEM macro, which is used to initialize all ToggleListItem elements from the view.
* GameCardImageDumpOptionsFrame: update button callback to push a GameCardImageDumpTaskFrame view onto the borealis stack.
* GameCardImageDumpTask: move class into its own header and module files.
* GameCardImageDumpTask: update class to also take in the checksum lookup method (not yet implemented).
* GameCardImageDumpTask: update class to send its first progress update as soon as the gamecard image size is known.
* GameCardImageDumpTask: update class to avoid returning a string if the task was cancelled -- DataTransferTaskFrame offers logic to display the appropiate cancel message on its own.
* GameCardTab: update PopulateList() method to display the new version information available in TitleGameCardApplicationMetadataEntry elements as part of the generated TitlesTabItem objects.
* i18n: add new localization strings.
* OptionsTab: update background task callback logic to handle task cancellation, reflecting the changes made to DataTransferTask.
* OptionsTab: reflect changes made to DataTransferProgressDisplay.
* RootView: cache the currently selected output storage value at all times, which is propagated throughout different parts of the UI. Getter and setter helpers have been added to operate with this value.
* RootView: add GetUsbHostSpeed() helper, which can be used by child views to retrieve the USB host speed on demand.
* RootView: update UMS task callback to automatically reset the cached output storage value back to the SD card if a UMS device was previously selected.
* title: define TitleGameCardApplicationMetadataEntry struct, which also holds version-specific information retrieved from the gamecard titles.
* title: refactor titleGetGameCardApplicationMetadataEntries() to return a dynamically allocated array of TitleGameCardApplicationMetadataEntry elements.
* usb: redefine global endpoint max packet size variable as an atomic unsigned 16-bit integer, which fixes a "status-hopping" issue previously experienced by repeating tasks running under other threads that periodically call usbIsReady().
* UsbHostTask: add GetUsbHostSpeed() method.
2024-04-29 14:26:12 +01:00
|
|
|
/* Avoid rethrowing any exceptions here -- program execution could end if another exception has already been rethrown. */
|
2021-07-25 06:37:13 +01:00
|
|
|
m_future.wait();
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Asynchronous task function. */
|
2024-04-25 00:49:04 +01:00
|
|
|
/* This function should periodically call IsCancelled() to determine if it should end prematurely. */
|
|
|
|
virtual Result DoInBackground(const Params&... params) = 0;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Posts asynchronous task result. Runs on the asynchronous task thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual Result PostResult(Result&& result)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
return std::move(result);
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Cleanup function called if the task is cancelled. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual void OnCancelled(const Result& result) { }
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Post-execution function called right after the task finishes. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual void OnPostExecute(const Result& result) { }
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Pre-execution function called right before the task starts. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual void OnPreExecute(void) { }
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Progress update function. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual void OnProgressUpdate(const Progress& progress) { }
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Stores the current progress inside the class. Runs on the asynchronous task thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
virtual void PublishProgress(const Progress& progress)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Don't proceed if the task isn't running. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->GetStatus() != AsyncTaskStatus::RUNNING || this->IsCancelled()) return;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Update progress. */
|
2021-07-27 16:00:09 +01:00
|
|
|
this->m_progress = progress;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-27 16:00:09 +01:00
|
|
|
/* Returns the current progress. May run on both threads. */
|
2024-04-25 00:49:04 +01:00
|
|
|
Progress GetProgress(void)
|
2021-07-27 16:00:09 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2021-07-27 16:00:09 +01:00
|
|
|
return this->m_progress;
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
public:
|
2024-04-19 11:08:44 +01:00
|
|
|
AsyncTask() = default;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Cancels the task. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
void Cancel(void) noexcept
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return right away if the task has already completed, or if it has already been cancelled. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->GetStatus() == AsyncTaskStatus::FINISHED || this->IsCancelled()) return;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Update cancel flag. */
|
2021-07-27 16:00:09 +01:00
|
|
|
this->m_cancelled = true;
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Starts the asynchronous task. Runs on the calling thread. */
|
2024-04-25 00:49:04 +01:00
|
|
|
AsyncTask<Progress, Result, Params...>& Execute(const Params&... params)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
/* Return right away if the task was cancelled before starting. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->IsCancelled()) return *this;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Verify task status. */
|
2024-04-25 00:49:04 +01:00
|
|
|
switch(this->GetStatus())
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
case AsyncTaskStatus::RUNNING:
|
|
|
|
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyRunning);
|
|
|
|
case AsyncTaskStatus::FINISHED:
|
|
|
|
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyFinished);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Update task status. */
|
|
|
|
this->m_status = AsyncTaskStatus::RUNNING;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2024-04-15 00:53:43 +01:00
|
|
|
/* Run pre-execution callback. */
|
2024-04-25 00:49:04 +01:00
|
|
|
this->OnPreExecute();
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Start asynchronous task on a new thread. */
|
|
|
|
this->m_future = std::async(std::launch::async, [this](const Params&... params) -> Result {
|
|
|
|
/* Catch any exceptions thrown by the asynchronous task. */
|
|
|
|
try {
|
2024-04-25 00:49:04 +01:00
|
|
|
return this->PostResult(this->DoInBackground(params...));
|
2021-07-25 06:37:13 +01:00
|
|
|
} catch(...) {
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2024-04-25 00:49:04 +01:00
|
|
|
this->Cancel();
|
2021-07-27 16:00:09 +01:00
|
|
|
this->m_rethrowException = true;
|
2021-07-25 06:37:13 +01:00
|
|
|
this->m_exceptionPtr = std::current_exception();
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
return {};
|
|
|
|
}, params...);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
return *this;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Waits for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
|
|
|
|
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
2024-04-25 00:49:04 +01:00
|
|
|
Result GetResult(void)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2024-04-25 00:49:04 +01:00
|
|
|
auto status = this->GetStatus();
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Throw an exception if the asynchronous task hasn't been executed. */
|
|
|
|
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* If the task is still running, wait until it finishes. */
|
2024-04-25 00:49:04 +01:00
|
|
|
/* std::future::get() calls std::future::wait() on its own if the result hasn't been retrieved. */
|
|
|
|
/* Finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
|
|
|
|
if (status == AsyncTaskStatus::RUNNING) this->Finish(this->m_future.get());
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Throw an exception if the asynchronous task was cancelled. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->IsCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return result. */
|
|
|
|
return this->m_result;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Waits for at most the given time for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
|
|
|
|
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
|
|
|
template<typename Rep, typename Period>
|
2024-04-25 00:49:04 +01:00
|
|
|
Result GetResult(const std::chrono::duration<Rep, Period>& timeout)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2024-04-25 00:49:04 +01:00
|
|
|
auto status = this->GetStatus();
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Throw an exception if the asynchronous task hasn't been executed. */
|
|
|
|
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Check if the task is still running. */
|
|
|
|
if (status == AsyncTaskStatus::RUNNING)
|
|
|
|
{
|
|
|
|
/* Wait for at most the given time for the asynchronous task to complete. */
|
|
|
|
auto thread_status = this->m_future.wait_for(timeout);
|
|
|
|
switch(thread_status)
|
|
|
|
{
|
|
|
|
case std::future_status::timeout:
|
|
|
|
/* Throw an exception if we timed out while waiting for the task to finish. */
|
|
|
|
throw AsyncTaskException(AsyncTaskException::eEx::TaskWaitTimeout);
|
|
|
|
case std::future_status::ready:
|
|
|
|
/* Retrieve the task result. */
|
2024-04-25 00:49:04 +01:00
|
|
|
/* Finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
|
|
|
|
this->Finish(this->m_future.get());
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Throw an exception if the asynchronous task was cancelled. */
|
2024-04-25 00:49:04 +01:00
|
|
|
if (this->IsCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return result. */
|
|
|
|
return this->m_result;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Returns the current task status. Runs on both threads. */
|
2024-04-25 00:49:04 +01:00
|
|
|
AsyncTaskStatus GetStatus(void) noexcept
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
|
|
|
return this->m_status;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-27 16:00:09 +01:00
|
|
|
/* Returns true if the task was cancelled before it completed normally. May be used on both threads. */
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Can be used by the asynchronous task to return prematurely. */
|
2024-04-25 00:49:04 +01:00
|
|
|
bool IsCancelled(void) noexcept
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2021-07-27 16:00:09 +01:00
|
|
|
return this->m_cancelled;
|
2021-07-25 06:37:13 +01:00
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Used by the calling thread to refresh the task progress, preferrably inside a loop. Returns true if the task finished. */
|
|
|
|
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
2024-04-25 00:49:04 +01:00
|
|
|
bool LoopCallback(void)
|
2021-07-25 06:37:13 +01:00
|
|
|
{
|
2023-02-24 20:32:58 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2024-04-25 00:49:04 +01:00
|
|
|
auto status = this->GetStatus();
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return immediately if the task already finished. */
|
|
|
|
if (status == AsyncTaskStatus::FINISHED) return true;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Return immediately if the task hasn't started, or if its result was already retrieved. */
|
|
|
|
if (status == AsyncTaskStatus::PENDING || !this->m_future.valid()) return false;
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
/* Get task thread status without waiting. */
|
|
|
|
auto thread_status = this->m_future.wait_for(std::chrono::seconds(0));
|
|
|
|
switch(thread_status)
|
|
|
|
{
|
|
|
|
case std::future_status::timeout:
|
|
|
|
/* Update progress. */
|
2024-04-25 00:49:04 +01:00
|
|
|
this->OnProgressUpdate(this->m_progress);
|
2021-07-27 16:00:09 +01:00
|
|
|
break;
|
2021-07-25 06:37:13 +01:00
|
|
|
case std::future_status::ready:
|
|
|
|
/* Finish task. */
|
2024-04-25 00:49:04 +01:00
|
|
|
this->Finish(this->m_future.get());
|
2021-07-25 06:37:13 +01:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2022-07-05 02:04:28 +01:00
|
|
|
|
2021-07-25 06:37:13 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __ASYNC_TASK_HPP__ */
|