diff options
Diffstat (limited to 'src/server/shared/Utilities/TaskScheduler.h')
-rw-r--r-- | src/server/shared/Utilities/TaskScheduler.h | 650 |
1 files changed, 0 insertions, 650 deletions
diff --git a/src/server/shared/Utilities/TaskScheduler.h b/src/server/shared/Utilities/TaskScheduler.h deleted file mode 100644 index f1fe7ea0a21..00000000000 --- a/src/server/shared/Utilities/TaskScheduler.h +++ /dev/null @@ -1,650 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * - * This program 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 2 of the License, or (at your - * option) any later version. - * - * This program 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef _TASK_SCHEDULER_H_ -#define _TASK_SCHEDULER_H_ - -#include <algorithm> -#include <chrono> -#include <vector> -#include <queue> -#include <memory> -#include <utility> -#include <set> - -#include <boost/optional.hpp> - -#include "Util.h" -#include "Duration.h" - -class TaskContext; - -/// The TaskScheduler class provides the ability to schedule std::function's in the near future. -/// Use TaskScheduler::Update to update the scheduler. -/// Popular methods are: -/// * Schedule (Schedules a std::function which will be executed in the near future). -/// * Schedules an asynchronous function which will be executed at the next update tick. -/// * Cancel, Delay & Reschedule (Methods to manipulate already scheduled tasks). -/// Tasks are organized in groups (uint), multiple tasks can have the same group id, -/// you can provide a group or not, but keep in mind that you can only manipulate specific tasks through its group id! -/// Tasks callbacks use the function signature void(TaskContext) where TaskContext provides -/// access to the function schedule plan which makes it possible to repeat the task -/// with the same duration or a new one. -/// It also provides access to the repeat counter which is useful for task that repeat itself often -/// but behave different every time (spoken event dialogs for example). -class TaskScheduler -{ - friend class TaskContext; - - // Time definitions (use steady clock) - typedef std::chrono::steady_clock clock_t; - typedef clock_t::time_point timepoint_t; - typedef clock_t::duration duration_t; - - // Task group type - typedef uint32 group_t; - // Task repeated type - typedef uint32 repeated_t; - // Task handle type - typedef std::function<void(TaskContext)> task_handler_t; - // Predicate type - typedef std::function<bool()> predicate_t; - // Success handle type - typedef std::function<void()> success_t; - - class Task - { - friend class TaskContext; - friend class TaskScheduler; - - timepoint_t _end; - duration_t _duration; - boost::optional<group_t> _group; - repeated_t _repeated; - task_handler_t _task; - - public: - // All Argument construct - Task(timepoint_t const& end, duration_t const& duration, boost::optional<group_t> const& group, - repeated_t const repeated, task_handler_t const& task) - : _end(end), _duration(duration), _group(group), _repeated(repeated), _task(task) { } - - // Minimal Argument construct - Task(timepoint_t const& end, duration_t const& duration, task_handler_t const& task) - : _end(end), _duration(duration), _group(boost::none), _repeated(0), _task(task) { } - - // Copy construct - Task(Task const&) = delete; - // Move construct - Task(Task&&) = delete; - // Copy Assign - Task& operator= (Task const&) = default; - // Move Assign - Task& operator= (Task&& right) = delete; - - // Order tasks by its end - inline bool operator< (Task const& other) const - { - return _end < other._end; - } - - inline bool operator> (Task const& other) const - { - return _end > other._end; - } - - // Compare tasks with its end - inline bool operator== (Task const& other) - { - return _end == other._end; - } - - // Returns true if the task is in the given group - inline bool IsInGroup(group_t const group) const - { - return _group == group; - } - }; - - typedef std::shared_ptr<Task> TaskContainer; - - /// Container which provides Task order, insert and reschedule operations. - struct Compare - { - bool operator() (TaskContainer const& left, TaskContainer const& right) - { - return (*left.get()) < (*right.get()); - }; - }; - - class TaskQueue - { - std::multiset<TaskContainer, Compare> container; - - public: - // Pushes the task in the container - void Push(TaskContainer&& task); - - /// Pops the task out of the container - TaskContainer Pop(); - - TaskContainer const& First() const; - - void Clear(); - - void RemoveIf(std::function<bool(TaskContainer const&)> const& filter); - - void ModifyIf(std::function<bool(TaskContainer const&)> const& filter); - - bool IsEmpty() const; - }; - - /// Contains a self reference to track if this object was deleted or not. - std::shared_ptr<TaskScheduler> self_reference; - - /// The current time point (now) - timepoint_t _now; - - /// The Task Queue which contains all task objects. - TaskQueue _task_holder; - - typedef std::queue<std::function<void()>> AsyncHolder; - - /// Contains all asynchronous tasks which will be invoked at - /// the next update tick. - AsyncHolder _asyncHolder; - - predicate_t _predicate; - - static bool EmptyValidator() - { - return true; - } - - static void EmptyCallback() - { - } - -public: - TaskScheduler() - : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(EmptyValidator) { } - - template<typename P> - TaskScheduler(P&& predicate) - : self_reference(this, [](TaskScheduler const*) { }), _now(clock_t::now()), _predicate(std::forward<P>(predicate)) { } - - TaskScheduler(TaskScheduler const&) = delete; - TaskScheduler(TaskScheduler&&) = delete; - TaskScheduler& operator= (TaskScheduler const&) = delete; - TaskScheduler& operator= (TaskScheduler&&) = delete; - - /// Sets a validator which is asked if tasks are allowed to be executed. - template<typename P> - TaskScheduler& SetValidator(P&& predicate) - { - _predicate = std::forward<P>(predicate); - return *this; - } - - /// Clears the validator which is asked if tasks are allowed to be executed. - TaskScheduler& ClearValidator(); - - /// Update the scheduler to the current time. - /// Calls the optional callback on successfully finish. - TaskScheduler& Update(success_t const& callback = EmptyCallback); - - /// Update the scheduler with a difftime in ms. - /// Calls the optional callback on successfully finish. - TaskScheduler& Update(size_t const milliseconds, success_t const& callback = EmptyCallback); - - /// Update the scheduler with a difftime. - /// Calls the optional callback on successfully finish. - template<class _Rep, class _Period> - TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime, - success_t const& callback = EmptyCallback) - { - _now += difftime; - Dispatch(callback); - return *this; - } - - /// Schedule an callable function that is executed at the next update tick. - /// Its safe to modify the TaskScheduler from within the callable. - TaskScheduler& Async(std::function<void()> const& callable); - - /// Schedule an event with a fixed rate. - /// Never call this from within a task context! Use TaskContext::Schedule instead! - template<class _Rep, class _Period> - TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, - task_handler_t const& task) - { - return ScheduleAt(_now, time, task); - } - - /// Schedule an event with a fixed rate. - /// Never call this from within a task context! Use TaskContext::Schedule instead! - template<class _Rep, class _Period> - TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, - group_t const group, task_handler_t const& task) - { - return ScheduleAt(_now, time, group, task); - } - - /// Schedule an event with a randomized rate between min and max rate. - /// Never call this from within a task context! Use TaskContext::Schedule instead! - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max, task_handler_t const& task) - { - return Schedule(RandomDurationBetween(min, max), task); - } - - /// Schedule an event with a fixed rate. - /// Never call this from within a task context! Use TaskContext::Schedule instead! - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max, group_t const group, - task_handler_t const& task) - { - return Schedule(RandomDurationBetween(min, max), group, task); - } - - /// Cancels all tasks. - /// Never call this from within a task context! Use TaskContext::CancelAll instead! - TaskScheduler& CancelAll(); - - /// Cancel all tasks of a single group. - /// Never call this from within a task context! Use TaskContext::CancelGroup instead! - TaskScheduler& CancelGroup(group_t const group); - - /// Cancels all groups in the given std::vector. - /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" - TaskScheduler& CancelGroupsOf(std::vector<group_t> const& groups); - - /// Delays all tasks with the given duration. - template<class _Rep, class _Period> - TaskScheduler& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) - { - _task_holder.ModifyIf([&duration](TaskContainer const& task) -> bool - { - task->_end += duration; - return true; - }); - return *this; - } - - /// Delays all tasks with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return DelayAll(RandomDurationBetween(min, max)); - } - - /// Delays all tasks of a group with the given duration. - template<class _Rep, class _Period> - TaskScheduler& DelayGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) - { - _task_holder.ModifyIf([&duration, group](TaskContainer const& task) -> bool - { - if (task->IsInGroup(group)) - { - task->_end += duration; - return true; - } - else - return false; - }); - return *this; - } - - /// Delays all tasks of a group with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& DelayGroup(group_t const group, - std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return DelayGroup(group, RandomDurationBetween(min, max)); - } - - /// Reschedule all tasks with a given duration. - template<class _Rep, class _Period> - TaskScheduler& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) - { - auto const end = _now + duration; - _task_holder.ModifyIf([end](TaskContainer const& task) -> bool - { - task->_end = end; - return true; - }); - return *this; - } - - /// Reschedule all tasks with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return RescheduleAll(RandomDurationBetween(min, max)); - } - - /// Reschedule all tasks of a group with the given duration. - template<class _Rep, class _Period> - TaskScheduler& RescheduleGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) - { - auto const end = _now + duration; - _task_holder.ModifyIf([end, group](TaskContainer const& task) -> bool - { - if (task->IsInGroup(group)) - { - task->_end = end; - return true; - } - else - return false; - }); - return *this; - } - - /// Reschedule all tasks of a group with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskScheduler& RescheduleGroup(group_t const group, - std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return RescheduleGroup(group, RandomDurationBetween(min, max)); - } - -private: - /// Insert a new task to the enqueued tasks. - TaskScheduler& InsertTask(TaskContainer task); - - template<class _Rep, class _Period> - TaskScheduler& ScheduleAt(timepoint_t const& end, - std::chrono::duration<_Rep, _Period> const& time, task_handler_t const& task) - { - return InsertTask(TaskContainer(new Task(end + time, time, task))); - } - - /// Schedule an event with a fixed rate. - /// Never call this from within a task context! Use TaskContext::schedule instead! - template<class _Rep, class _Period> - TaskScheduler& ScheduleAt(timepoint_t const& end, - std::chrono::duration<_Rep, _Period> const& time, - group_t const group, task_handler_t const& task) - { - static repeated_t const DEFAULT_REPEATED = 0; - return InsertTask(TaskContainer(new Task(end + time, time, group, DEFAULT_REPEATED, task))); - } - - // Returns a random duration between min and max - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - static std::chrono::milliseconds - RandomDurationBetween(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - auto const milli_min = std::chrono::duration_cast<std::chrono::milliseconds>(min); - auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max); - - // TC specific: use SFMT URandom - return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count())); - } - - /// Dispatch remaining tasks - void Dispatch(success_t const& callback); -}; - -class TaskContext -{ - friend class TaskScheduler; - - /// Associated task - TaskScheduler::TaskContainer _task; - - /// Owner - std::weak_ptr<TaskScheduler> _owner; - - /// Marks the task as consumed - std::shared_ptr<bool> _consumed; - - /// Dispatches an action safe on the TaskScheduler - TaskContext& Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply); - -public: - // Empty constructor - TaskContext() - : _task(), _owner(), _consumed(std::make_shared<bool>(true)) { } - - // Construct from task and owner - explicit TaskContext(TaskScheduler::TaskContainer&& task, std::weak_ptr<TaskScheduler>&& owner) - : _task(task), _owner(owner), _consumed(std::make_shared<bool>(false)) { } - - // Copy construct - TaskContext(TaskContext const& right) - : _task(right._task), _owner(right._owner), _consumed(right._consumed) { } - - // Move construct - TaskContext(TaskContext&& right) - : _task(std::move(right._task)), _owner(std::move(right._owner)), _consumed(std::move(right._consumed)) { } - - // Copy assign - TaskContext& operator= (TaskContext const& right) - { - _task = right._task; - _owner = right._owner; - _consumed = right._consumed; - return *this; - } - - // Move assign - TaskContext& operator= (TaskContext&& right) - { - _task = std::move(right._task); - _owner = std::move(right._owner); - _consumed = std::move(right._consumed); - return *this; - } - - /// Returns true if the owner was deallocated and this context has expired. - bool IsExpired() const; - - /// Returns true if the event is in the given group - bool IsInGroup(TaskScheduler::group_t const group) const; - - /// Sets the event in the given group - TaskContext& SetGroup(TaskScheduler::group_t const group); - - /// Removes the group from the event - TaskContext& ClearGroup(); - - /// Returns the repeat counter which increases every time the task is repeated. - TaskScheduler::repeated_t GetRepeatCounter() const; - - /// Repeats the event and sets a new duration. - /// std::chrono::seconds(5) for example. - /// This will consume the task context, its not possible to repeat the task again - /// from the same task context! - template<class _Rep, class _Period> - TaskContext& Repeat(std::chrono::duration<_Rep, _Period> const& duration) - { - AssertOnConsumed(); - - // Set new duration, in-context timing and increment repeat counter - _task->_duration = duration; - _task->_end += duration; - _task->_repeated += 1; - (*_consumed) = true; - return Dispatch(std::bind(&TaskScheduler::InsertTask, std::placeholders::_1, _task)); - } - - /// Repeats the event with the same duration. - /// This will consume the task context, its not possible to repeat the task again - /// from the same task context! - TaskContext& Repeat() - { - return Repeat(_task->_duration); - } - - /// Repeats the event and set a new duration that is randomized between min and max. - /// std::chrono::seconds(5) for example. - /// This will consume the task context, its not possible to repeat the task again - /// from the same task context! - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& Repeat(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return Repeat(TaskScheduler::RandomDurationBetween(min, max)); - } - - /// Schedule a callable function that is executed at the next update tick from within the context. - /// Its safe to modify the TaskScheduler from within the callable. - TaskContext& Async(std::function<void()> const& callable); - - /// Schedule an event with a fixed rate from within the context. - /// Its possible that the new event is executed immediately! - /// Use TaskScheduler::Async to create a task - /// which will be called at the next update tick. - template<class _Rep, class _Period> - TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, - TaskScheduler::task_handler_t const& task) - { - auto const end = _task->_end; - return Dispatch([end, time, task](TaskScheduler& scheduler) -> TaskScheduler& - { - return scheduler.ScheduleAt<_Rep, _Period>(end, time, task); - }); - } - - /// Schedule an event with a fixed rate from within the context. - /// Its possible that the new event is executed immediately! - /// Use TaskScheduler::Async to create a task - /// which will be called at the next update tick. - template<class _Rep, class _Period> - TaskContext& Schedule(std::chrono::duration<_Rep, _Period> const& time, - TaskScheduler::group_t const group, TaskScheduler::task_handler_t const& task) - { - auto const end = _task->_end; - return Dispatch([end, time, group, task](TaskScheduler& scheduler) -> TaskScheduler& - { - return scheduler.ScheduleAt<_Rep, _Period>(end, time, group, task); - }); - } - - /// Schedule an event with a randomized rate between min and max rate from within the context. - /// Its possible that the new event is executed immediately! - /// Use TaskScheduler::Async to create a task - /// which will be called at the next update tick. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::task_handler_t const& task) - { - return Schedule(TaskScheduler::RandomDurationBetween(min, max), task); - } - - /// Schedule an event with a randomized rate between min and max rate from within the context. - /// Its possible that the new event is executed immediately! - /// Use TaskScheduler::Async to create a task - /// which will be called at the next update tick. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::group_t const group, - TaskScheduler::task_handler_t const& task) - { - return Schedule(TaskScheduler::RandomDurationBetween(min, max), group, task); - } - - /// Cancels all tasks from within the context. - TaskContext& CancelAll(); - - /// Cancel all tasks of a single group from within the context. - TaskContext& CancelGroup(TaskScheduler::group_t const group); - - /// Cancels all groups in the given std::vector from within the context. - /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" - TaskContext& CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups); - - /// Delays all tasks with the given duration from within the context. - template<class _Rep, class _Period> - TaskContext& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) - { - return Dispatch(std::bind(&TaskScheduler::DelayAll<_Rep, _Period>, std::placeholders::_1, duration)); - } - - /// Delays all tasks with a random duration between min and max from within the context. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return DelayAll(TaskScheduler::RandomDurationBetween(min, max)); - } - - /// Delays all tasks of a group with the given duration from within the context. - template<class _Rep, class _Period> - TaskContext& DelayGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) - { - return Dispatch(std::bind(&TaskScheduler::DelayGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); - } - - /// Delays all tasks of a group with a random duration between min and max from within the context. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& DelayGroup(TaskScheduler::group_t const group, - std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return DelayGroup(group, TaskScheduler::RandomDurationBetween(min, max)); - } - - /// Reschedule all tasks with the given duration. - template<class _Rep, class _Period> - TaskContext& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) - { - return Dispatch(std::bind(&TaskScheduler::RescheduleAll, std::placeholders::_1, duration)); - } - - /// Reschedule all tasks with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return RescheduleAll(TaskScheduler::RandomDurationBetween(min, max)); - } - - /// Reschedule all tasks of a group with the given duration. - template<class _Rep, class _Period> - TaskContext& RescheduleGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) - { - return Dispatch(std::bind(&TaskScheduler::RescheduleGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); - } - - /// Reschedule all tasks of a group with a random duration between min and max. - template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> - TaskContext& RescheduleGroup(TaskScheduler::group_t const group, - std::chrono::duration<_RepLeft, _PeriodLeft> const& min, - std::chrono::duration<_RepRight, _PeriodRight> const& max) - { - return RescheduleGroup(group, TaskScheduler::RandomDurationBetween(min, max)); - } - -private: - /// Asserts if the task was consumed already. - void AssertOnConsumed() const; - - /// Invokes the associated hook of the task. - void Invoke(); -}; - -#endif /// _TASK_SCHEDULER_H_ |