#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2023 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library 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
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_PLATFORM_TASKS_THREADEDTASK_H
#define NUCLEX_PLATFORM_TASKS_THREADEDTASK_H
#include "Nuclex/Platform/Config.h"
#include "Nuclex/Platform/Tasks/Task.h" // for Task
namespace Nuclex { namespace Support { namespace Threading {
// ------------------------------------------------------------------------------------------- //
class ThreadPool;
// ------------------------------------------------------------------------------------------- //
}}} // namespace Nuclex::Support::Threading
namespace Nuclex { namespace Platform { namespace Tasks {
// ------------------------------------------------------------------------------------------- //
/// Task that uses multiple threads via the thread pool
///
///
/// Use this class if you create tasks that perform work on multiple threads by
/// themselves (if, instead, you have a third party library that does its own
/// threading, use normal task and just set the resource manifest to the number
/// of CPU cores that the third-party library will use).
///
///
/// Your RunThreaded() method will be called on the number of threads you specify.
///
///
class NUCLEX_PLATFORM_TYPE ThreadedTask : public Task {
/// Initializes a threaded task using the specified number of threads
/// Thread pool that will run the task's workload
///
/// Maximum number of threads to use. This is the number of calls that will be made
/// to the method. If the thread pool has fewer threads than
/// this number, then some of the calls will happen
/// sequentially on reused threadpool threads, which may lead to poor CPU utilization
/// (i.e. threaded task with 6 intended threads on 4 core CPU will keep 4 cores busy
/// for a bit, then run the remaining 2 task threads and keep only 2 cores busy).
///
public: NUCLEX_PLATFORM_API ThreadedTask(
Nuclex::Support::Threading::ThreadPool &threadPool,
std::size_t maximumThreadCount = std::size_t(-1)
) :
threadPool(threadPool),
maximumThreadCount(maximumThreadCount) {}
/// Frees all resources owned by the task
///
/// The task must be either finished or cancelled before it may be destroyed.
///
public: NUCLEX_PLATFORM_API virtual ~ThreadedTask() = default;
/// Executes the task, using the specified resource units
///
/// Indices of the resource units the task coordinator has assigned this task
///
///
/// Lets the task detect when it is requested to cancel its processing
///
public: NUCLEX_PLATFORM_API void Run(
const std::array &resourceUnitIndices,
const CancellationWatcher &cancellationWatcher
) noexcept override;
///
/// Called in parallel on the specified number of threads to perform the task's work
///
///
/// when you set up the task coordinator, you specify one or more "units"
/// that provide each resources (likely you'll only add one "SystemMemory"
/// unit but there may be one "VideoMemory" unit per GPU in the system).
/// This array tells your task which resource units the task coordinator wants it
/// to use. Feel free to ignore this if you only ever have one unit of each resource.
///
///
/// Can be used to figure out whether the task has been cancelled. This will happen
/// if the task coordinator is shut down or cleared while tasks are executing.
/// Any task that takes longer than a couple of milliseconds should check for
/// cancellation at regular intervals to ensure the task coordinator isn't clogged.
///
protected: NUCLEX_PLATFORM_API virtual void ThreadedRun(
const std::array &resourceUnitIndices,
const CancellationWatcher &cancellationWatcher
) noexcept = 0;
///
/// Used internally to calls the ThreadedRun() method the a thread pool thread
///
/// The 'this' pointer of the threaded task instance
///
/// Indices of the resource units the task coordinator has assigned this task
///
///
/// Lets the task detect when it is requested to cancel its processing
///
///
/// This method is used rather than std::bind() in order to not pollute the call stack
/// and avoid the use of needless lambda functors in the thread pool callbacks.
///
private: static void invokeThreadedRun(
ThreadedTask *self,
const std::array *resourceUnitIndices,
const CancellationWatcher *cancellationWatcher
);
/// Thread pool that will be used to run work in multiple threads
private: Nuclex::Support::Threading::ThreadPool &threadPool;
/// Maximum number of threads that the task will use
private: std::size_t maximumThreadCount;
};
// ------------------------------------------------------------------------------------------- //
}}} // namespace Nuclex::Platform::Tasks
#endif // NUCLEX_PLATFORM_TASKS_THREADEDTASK_H