| Index: src/Scheduler.h |
| =================================================================== |
| --- a/src/Scheduler.h |
| +++ b/src/Scheduler.h |
| @@ -26,46 +26,6 @@ |
| #include <queue> |
| #include <thread> |
| -namespace AdblockPlus |
| -{ |
| - /* |
| - * Adapter class for heap allocated function objects |
| - * |
| - * The legacy behavior of the original threading regime combined task life |
| - * cycle and execution. Previously, tasks were allocated by the user and |
| - * deleted at the end of execution internally. This adapter class allows |
| - * separation of these concerns. It allows the legacy allocation behavior |
| - * without burdening the new scheduler with any concern over task life cycle. |
| - */ |
| - template<class T> |
| - class HeapFunction |
| - { |
| - std::shared_ptr<T> body; |
| - |
| - public: |
| - HeapFunction(std::shared_ptr<T> body) |
| - :body(body) |
| - {} |
| - |
| - void operator()() |
| - { |
| - if (body) |
| - { |
| - body->operator()(); |
| - } |
| - } |
| - }; |
| - |
| - /* |
| - * Utility function uses type inference to simplify expressions at point of use. |
| - */ |
| - template<class T> |
| - HeapFunction<T> MakeHeapFunction(std::shared_ptr<T> body) |
| - { |
| - return HeapFunction<T>(body); |
| - } |
| -} |
| - |
| /** |
| * Interface class for scheduled tasks. |
| */ |
| @@ -84,14 +44,6 @@ |
| }; |
| /** |
| - * Execute a task immediately in detached thread that's used only for this task. |
| - * |
| - * The present version is nothing more than a rewrite of the legacy behavior, |
| - * which was to create a new thread for each task. |
| - */ |
| -void StartImmediatelyInSingleUseDetachedThread(std::function<void()> task); |
| - |
| -/** |
| * Active object to run arbitrary function objects. |
| * |
| * Operations execute in a thread owned by this instance. |
| @@ -196,19 +148,53 @@ |
| * but there's no interface for this. |
| * - No support for delayed execution to support JavaScript `SetTimeout`. |
| */ |
| -template<class Worker> |
| +template<class Worker, class UserData> |
| class SchedulerT |
| { |
| /** |
| + * A task as scheduled for execution. |
| + * |
| + * This class is a combination of two kinds of data: |
| + * 1. An external object containing the body of the task. |
| + * 2. All the internal elements required to run the task. |
| + */ |
| + template<class UserData> |
| + struct ScheduledTask |
| + { |
| + /** |
| + * The external task. |
| + * This member comes to us from our Run() method. |
| + */ |
| + std::shared_ptr<TaskFunctionInterface> body; |
| + /** |
| + * The only internal data at present is the worker thread. |
| + */ |
| + Worker worker; |
| + /** |
| + * Data associated with this task by the user of the scheduler |
| + */ |
| + std::shared_ptr<UserData> userData; |
| + /** |
| + * Member worker is default-constructed |
| + * because VS2012 insists on copy constructors when it doesn't need them. |
| + */ |
| + ScheduledTask(std::shared_ptr<TaskFunctionInterface>&& body, std::shared_ptr<UserData>&& ud) |
| + : body(std::forward<std::shared_ptr<TaskFunctionInterface>>(body)), |
| + userData(std::forward<std::shared_ptr<UserData>>(ud)) |
| + {} |
| + }; |
| + |
| + /** |
| * Shared mutex for all synchronization |
| */ |
| std::mutex m; |
| typedef std::unique_lock<std::mutex> UniqueLockType; |
| + typedef std::list<ScheduledTask<UserData>> TaskListType; |
| /** |
| * A list containing an entry for each running task. |
| */ |
| - std::list<Worker> taskList; |
| + TaskListType taskList; |
| /** |
| * Condition variable notified when a running task is removed from its list |
| @@ -221,13 +207,20 @@ |
| OperationRunner monitor; |
| /** |
| + * State flag. |
| + * |
| + * We only have two states: normal and terminating. |
| + */ |
| + bool isNormal; |
| + |
| + /** |
| * Execution wrapper for a task. |
| * @param task |
| * An iterator to the task within the task list. |
| */ |
| - void WorkerMain(std::function<void()> body, typename std::list<Worker>::iterator task) |
| + void WorkerMain(typename TaskListType::iterator task) |
| { |
| - body(); |
| + (*task->body)(); |
| monitor.Run([this, task]() { JoinAndRemove(task); }); |
| } |
| @@ -238,9 +231,9 @@ |
| * |
| * This is the only function that may remove tasks from the running task list. |
| */ |
| - void JoinAndRemove(typename std::list<Worker>::iterator task) |
| + void JoinAndRemove(typename TaskListType::iterator task) |
| { |
| - task->Join(); |
| + task->worker.Join(); |
| { |
| UniqueLockType ul(m); |
| taskList.erase(task); |
| @@ -250,6 +243,7 @@ |
| public: |
| SchedulerT() |
| + : isNormal(true) |
| {} |
| ~SchedulerT() |
| @@ -259,21 +253,46 @@ |
| // We call it in the destructor to ensure all tasks have completed. |
| // If we do not, we risk destruction of a joinable thread, |
| // which would generate a call to `std::terminate()`. |
| + ShutDown(); |
| JoinAll(); |
| } |
| /** |
| + * Interrupt all pending tasks so that they'll terminate soon. |
| + * Execution may proceed after a call to Interrupt(), |
| + * but it does indicate that a task should terminate as soon as it can. |
| + */ |
| + void ShutDown() |
| + { |
| + UniqueLockType ul(m); |
| + // Ensure the task list does not grow during shutdown. |
| + isNormal = false; |
| + |
| + for (auto& it : taskList) |
| + { |
| + it.body->Interrupt(); |
| + } |
| + } |
| + |
| + /** |
| * Construct a `Worker` and run a task in it immediately. |
| * |
| * This is the only function that may add tasks to the running task list. |
| */ |
| - void Run(std::function<void()> body) |
| + void Run(std::shared_ptr<TaskFunctionInterface>&& body, std::shared_ptr<UserData>&& ud) |
| { |
| UniqueLockType ul(m); |
| + if (!isNormal) |
| + { |
| + // We don't run new tasks if we're in a terminating state |
| + return; |
| + } |
| // Note that the interface to `Worker` uses a constructor and not a factory |
| // Here we construct in place, avoiding both move and copy. |
| - auto tt = taskList.emplace(taskList.begin()); |
| - tt->Run([this, body, tt]() { WorkerMain(body, tt); }); |
| + auto tt = taskList.emplace(taskList.begin(), |
| + std::forward<std::shared_ptr<TaskFunctionInterface>>(body), |
| + std::forward<std::shared_ptr<UserData>>(ud)); |
| + tt->worker.Run([this, tt]() { WorkerMain(tt); }); |
| } |
| /** |