TrinityCore
Loading...
Searching...
No Matches
TaskScheduler.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "TaskScheduler.h"
19#include "Errors.h"
20
22
24 : self_reference(this, [](TaskScheduler const*) { }),
25 _now(clock_t::now()),
26 _predicate(EmptyValidator)
27{
28}
29
31
37
39{
40 _now = clock_t::now();
42 return *this;
43}
44
46{
47 _now = clock_t::now();
48 Dispatch(callback);
49 return *this;
50}
51
53{
54 _now += std::chrono::milliseconds(milliseconds);
56 return *this;
57}
58
59TaskScheduler& TaskScheduler::Update(size_t milliseconds, success_t callback)
60{
61 _now += std::chrono::milliseconds(milliseconds);
62 Dispatch(callback);
63 return *this;
64}
65
67{
68 _now += difftime;
70 return *this;
71}
72
74{
75 _now += difftime;
76 Dispatch(callback);
77 return *this;
78}
79
80TaskScheduler& TaskScheduler::Async(std::function<void()> callable)
81{
82 Schedule(duration_t(1), [callable = std::move(callable)](TaskContext const&) { callable(); });
83 return *this;
84}
85
87{
90 return *this;
91}
92
94{
95 _task_holder.RemoveIf([group](TaskContainer const& task) -> bool
96 {
97 return task->IsInGroup(group);
98 });
99 return *this;
100}
101
103{
104 _task_holder.RemoveIf([groups](TaskContainer const& task) -> bool
105 {
106 return std::ranges::any_of(groups, [&](group_t group) { return task->IsInGroup(group); });
107 });
108 return *this;
109}
110
112{
113 _task_holder.ModifyIf([&duration](TaskContainer const& task) -> bool
114 {
115 task->_end += duration;
116 return true;
117 });
118 return *this;
119}
120
122{
123 _task_holder.ModifyIf([&duration, group](TaskContainer const& task) -> bool
124 {
125 if (task->IsInGroup(group))
126 {
127 task->_end += duration;
128 return true;
129 }
130 else
131 return false;
132 });
133 return *this;
134}
135
137{
138 timepoint_t end = _now + duration;
139 _task_holder.ModifyIf([end](TaskContainer const& task) -> bool
140 {
141 task->_end = end;
142 return true;
143 });
144 return *this;
145}
146
148{
149 timepoint_t end = _now + duration;
150 _task_holder.ModifyIf([end, group](TaskContainer const& task) -> bool
151 {
152 if (task->IsInGroup(group))
153 {
154 task->_end = end;
155 return true;
156 }
157 else
158 return false;
159 });
160 return *this;
161}
162
164{
165 _task_holder.Push(std::move(task));
166 return *this;
167}
168
169TaskScheduler& TaskScheduler::InsertTask(TaskQueue::Container::node_type&& node)
170{
171 _task_holder.Push(std::move(node));
172 return *this;
173}
174
176{
177 return InsertTask(std::make_shared<Task>(end + time, time, std::move(task)));
178}
179
181{
182 static constexpr repeated_t DEFAULT_REPEATED = 0;
183 return InsertTask(std::make_shared<Task>(end + time, time, group, DEFAULT_REPEATED, std::move(task)));
184}
185
186void TaskScheduler::Dispatch(success_t const& callback/* = nullptr*/)
187{
188 // If the validation failed abort the dispatching here.
189 if (!_predicate())
190 return;
191
192 while (!_task_holder.IsEmpty())
193 {
194 if (_task_holder.First()->_end > _now)
195 break;
196
197 // Perfect forward the context to the handler
198 // Use weak references to catch destruction before callbacks.
199 TaskContext context(_task_holder.Pop(), std::weak_ptr<TaskScheduler>(self_reference));
200
201 // Invoke the context
202 context.Invoke();
203
204 // If the validation failed abort the dispatching here.
205 if (!_predicate())
206 return;
207 }
208
209 // On finish call the final callback
210 if (callback)
211 callback();
212}
213
215{
216 container.emplace(std::move(task));
217}
218
219void TaskScheduler::TaskQueue::Push(Container::node_type&& node)
220{
221 container.insert(std::move(node));
222}
223
224TaskScheduler::TaskQueue::Container::node_type TaskScheduler::TaskQueue::Pop()
225{
226 return container.extract(container.begin());
227}
228
230{
231 return *container.begin();
232}
233
235{
236 container.clear();
237}
238
239void TaskScheduler::TaskQueue::RemoveIf(std::function<bool(TaskContainer const&)> const& filter)
240{
241 for (auto itr = container.begin(); itr != container.end();)
242 if (filter(*itr))
243 itr = container.erase(itr);
244 else
245 ++itr;
246}
247
248void TaskScheduler::TaskQueue::ModifyIf(std::function<bool(TaskContainer const&)> const& filter)
249{
250 Container cache;
251 for (auto itr = container.begin(); itr != container.end();)
252 if (filter(*itr))
253 cache.insert(container.extract(itr++));
254 else
255 ++itr;
256
257 container.merge(cache);
258}
259
261{
262 return container.empty();
263}
264
265TaskContext::TaskContext(TaskScheduler::TaskQueue::Container::node_type&& task, std::weak_ptr<TaskScheduler>&& owner) noexcept
266 : _task(std::move(task)), _owner(std::move(owner))
267{
268}
269
271 : _task(std::move(right._task)), _owner(std::move(right._owner))
272{
273 //leave moved-from object in usable state (const qualified functions need to remain callable)
274 right._task = GetTaskContainer();
275}
276
278{
279 if (this != &right)
280 {
281 _task = std::move(right._task);
282 _owner = std::move(right._owner);
283
284 //leave moved-from object in usable state (const qualified functions need to remain callable)
285 right._task = GetTaskContainer();
286 }
287 return *this;
288}
289
290TaskContext::~TaskContext() = default;
291
293{
294 return _owner.expired();
295}
296
298{
299 return GetTask()->IsInGroup(group);
300}
301
303{
304 GetTask()->_group = group;
305 return *this;
306}
307
309{
310 GetTask()->_group = std::nullopt;
311 return *this;
312}
313
315{
316 return GetTask()->_repeated;
317}
318
320{
321 // If you encounter this assertion check if you repeat a TaskContext more then 1 time!
322 ASSERT(std::holds_alternative<TaskScheduler::TaskQueue::Container::node_type>(_task), "Bad task logic, task context was consumed already!");
323
324 // Set new duration, in-context timing and increment repeat counter
325 TaskScheduler::TaskQueue::Container::node_type& taskNode = std::get<TaskScheduler::TaskQueue::Container::node_type>(_task);
326 TaskScheduler::TaskContainer task = taskNode.value();
327 task->_duration = duration;
328 task->_end += duration;
329 task->_repeated += 1;
330 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
331 scheduler->InsertTask(std::move(taskNode));
332
333 //leave *this in usable state (const qualified functions need to remain callable)
334 _task = std::move(task);
335
336 return *this;
337}
338
340{
341 return Repeat(GetTask()->_duration);
342}
343
344TaskContext& TaskContext::Async(std::function<void()> callable)
345{
346 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
347 scheduler->Async(std::move(callable));
348
349 return *this;
350}
351
353{
354 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
355 scheduler->ScheduleAt(GetTask()->_end, time, std::move(task));
356
357 return *this;
358}
359
361{
362 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
363 scheduler->ScheduleAt(GetTask()->_end, time, group, std::move(task));
364
365 return *this;
366}
367
369{
370 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
371 scheduler->CancelAll();
372
373 return *this;
374}
375
377{
378 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
379 scheduler->CancelGroup(group);
380
381 return *this;
382}
383
384TaskContext& TaskContext::CancelGroupsOf(std::span<TaskScheduler::group_t> groups)
385{
386 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
387 scheduler->CancelGroupsOf(groups);
388
389 return *this;
390}
391
393{
394 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
395 scheduler->DelayAll(duration);
396
397 return *this;
398}
399
401{
402 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
403 scheduler->DelayGroup(group, duration);
404
405 return *this;
406}
407
409{
410 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
411 scheduler->RescheduleAll(duration);
412
413 return *this;
414}
415
417{
418 if (std::shared_ptr<TaskScheduler> scheduler = _owner.lock())
419 scheduler->RescheduleGroup(group, duration);
420
421 return *this;
422}
423
425{
426 GetTask()->_task(*this);
427}
428
430{
431 static_assert(std::variant_size_v<decltype(_task)> == 2);
432 switch (_task.index())
433 {
434 case 0: return std::get<0>(_task).value();
435 case 1: return std::get<1>(_task);
436 default:
437 ASSERT(false);
438 }
439}
440
442{
443 static_assert(std::variant_size_v<decltype(_task)> == 2);
444 switch (_task.index())
445 {
446 case 0: return std::get<0>(_task).value().get();
447 case 1: return std::get<1>(_task).get();
448 default:
449 ASSERT(false);
450 }
451}
#define ASSERT
Definition Errors.h:80
static bool EmptyValidator()
TaskContext & Async(std::function< void()> callable)
TaskContext & CancelGroupsOf(std::span< TaskScheduler::group_t > groups)
TaskContext & DelayGroup(TaskScheduler::group_t group, TaskScheduler::duration_t duration)
Delays all tasks of a group with the given duration from within the context.
void Invoke()
Invokes the associated hook of the task.
TaskContext & CancelAll()
Cancels all tasks from within the context.
std::variant< TaskScheduler::TaskQueue::Container::node_type, TaskScheduler::TaskContainer > _task
Associated task.
TaskContext & RescheduleAll(TaskScheduler::duration_t duration)
Reschedule all tasks with the given duration.
TaskContext & Schedule(TaskScheduler::duration_t time, TaskScheduler::task_handler_t task)
TaskContext & SetGroup(TaskScheduler::group_t group)
Sets the event in the given group.
TaskContext & operator=(TaskContext const &right)=delete
TaskScheduler::repeated_t GetRepeatCounter() const
Returns the repeat counter which increases every time the task is repeated.
TaskContext & DelayAll(TaskScheduler::duration_t duration)
Delays all tasks with the given duration from within the context.
TaskContext & ClearGroup()
Removes the group from the event.
TaskScheduler::Task * GetTask() const noexcept
TaskContext() noexcept
TaskContext & Repeat()
TaskScheduler::TaskContainer & GetTaskContainer() noexcept
bool IsInGroup(TaskScheduler::group_t group) const
Returns true if the event is in the given group.
TaskContext & CancelGroup(TaskScheduler::group_t group)
Cancel all tasks of a single group from within the context.
bool IsExpired() const
Returns true if the owner was deallocated and this context has expired.
TaskContext & RescheduleGroup(TaskScheduler::group_t group, TaskScheduler::duration_t duration)
Reschedule all tasks of a group with the given duration.
void ModifyIf(std::function< bool(TaskContainer const &)> const &filter)
TaskContainer const & First() const
void RemoveIf(std::function< bool(TaskContainer const &)> const &filter)
Container::node_type Pop()
Pops the task out of the container.
std::multiset< TaskContainer, Compare > Container
void Push(TaskContainer &&task)
TaskScheduler & CancelGroup(group_t group)
TaskScheduler & DelayGroup(group_t group, duration_t duration)
Delays all tasks of a group with the given duration.
TaskScheduler & CancelAll()
TaskScheduler & RescheduleAll(duration_t duration)
Reschedule all tasks with a given duration.
std::shared_ptr< Task > TaskContainer
clock_t::time_point timepoint_t
void Dispatch(success_t const &callback)
Dispatch remaining tasks.
std::function< void()> success_t
std::shared_ptr< TaskScheduler > self_reference
Contains a self reference to track if this object was deleted or not.
TaskScheduler & InsertTask(TaskContainer &&task)
Insert a new task to the enqueued tasks.
predicate_t _predicate
TaskScheduler & RescheduleGroup(group_t group, duration_t duration)
Reschedule all tasks of a group with the given duration.
clock_t::duration duration_t
TaskScheduler & Schedule(duration_t time, task_handler_t task)
static success_t const EmptySuccessCallback
TaskScheduler & ClearValidator()
Clears the validator which is asked if tasks are allowed to be executed.
timepoint_t _now
The current time point (now)
TaskScheduler & DelayAll(duration_t duration)
Delays all tasks with the given duration.
TaskScheduler & Async(std::function< void()> callable)
TaskScheduler & CancelGroupsOf(std::span< group_t > groups)
static bool EmptyValidator()
TaskScheduler & ScheduleAt(timepoint_t end, duration_t time, task_handler_t task)
TaskQueue _task_holder
The Task Queue which contains all task objects.
TaskScheduler & Update()
Update the scheduler to the current time.