Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <assert.h>
6 : #include <fstream>
7 : #include <map>
8 : #include <iostream>
9 : #include <atomic>
10 : #include <boost/intrusive/set.hpp>
11 : #include <boost/optional.hpp>
12 : #include <oneapi/tbb/enumerable_thread_specific.h>
13 : #include <oneapi/tbb/global_control.h>
14 :
15 : #include "base/logging.h"
16 : #include "base/task.h"
17 : #include "base/task_annotations.h"
18 : #include "base/task_tbbkeepawake.h"
19 : #include "base/task_monitor.h"
20 :
21 : #include <base/sandesh/task_types.h>
22 :
23 : using namespace std;
24 :
25 : int TaskScheduler::ThreadAmpFactor_ = 1;
26 : class TaskEntry;
27 : struct TaskDeferEntryCmp;
28 :
29 : typedef oneapi::tbb::enumerable_thread_specific<Task *> TaskInfo;
30 :
31 : static TaskInfo task_running;
32 :
33 : // Vector of Task entries
34 : typedef std::vector<TaskEntry *> TaskEntryList;
35 :
36 : boost::scoped_ptr<TaskScheduler> TaskScheduler::singleton_;
37 :
38 : #define TASK_TRACE(scheduler, task, msg, delay)\
39 : do {\
40 : scheduler->Log(__FILE__, __LINE__, task, msg, delay);\
41 : } while (false)
42 :
43 : /// @brief A private class used to implement tbb::task
44 : /// An object is created when task is ready for execution and
45 : /// registered with tbb::task
46 : class TaskFunctor {
47 : public:
48 :
49 : /// @brief Creates a new instance of TaskFunctor using the provided
50 : /// implementaton (a parent).
51 7234883 : TaskFunctor(Task *t) : parent_(t) {};
52 :
53 : /// @brief Creates a copy of the object.
54 : TaskFunctor(const TaskFunctor& tf)
55 : : parent_(tf.parent_) {}
56 :
57 7234883 : TaskFunctor(TaskFunctor &&tf)
58 7234883 : : parent_(tf.parent_) {}
59 :
60 : /// @brief Destructor is called when a task execution is compeleted.
61 : /// Invoked implicitly by tbb::task.
62 : /// Invokes OnTaskExit to schedule tasks pending tasks.
63 : virtual ~TaskFunctor();
64 :
65 : /// @brief Method called from TBB to execute the task.
66 : /// Invokes Run() method of the parent (an implementation).
67 : /// Supports task continuation when Run() returns false.
68 : void operator ()() const;
69 :
70 : private:
71 :
72 : /// @brief
73 : mutable Task *parent_;
74 :
75 : /// @brief
76 : const TaskFunctor & operator = (const TaskFunctor) = delete;
77 : };
78 :
79 : /// @brief A class maintaning information for every <task, instance>
80 : ///
81 : /// policyq_ : contains,
82 : /// - Policies configured for a task
83 : /// - Complementary policies for a task.
84 : /// Example, if a policy is of form <tid0> => <tid1, inst1> <tid2, -1>
85 : /// <tid1, inst1> cannot run if <tid0, inst1> is running
86 : /// <tid2, *> cannot run when <tid0, *> are running. These become
87 : /// complementary rule
88 : ///
89 : /// waitq_ : Tasks of this instance created and waiting to be executed. Tasks
90 : /// are stored and executed in order of their creation
91 : /// Task can be added here on Enqueue if policy conditions are not
92 : /// met. Its taken out from waitq_ only when its about to Run
93 : ///
94 : /// deferq_ : Tree of TaskEntry waiting on this task instance. The TaskEntry.
95 : /// This tree is populated if all conditions are met
96 : /// - This TaskEntry has tasks created
97 : /// - The TaskEntry in deferq_ has tasks created
98 : /// - The Tree is sorted on task seqno_
99 : ///
100 : /// run_task_ : Task running in context of this TaskEntry. Only entries in
101 : /// task_entry_db_ have this set. Entries in task_db_ will always
102 : /// have this as NULL
103 : /// Running task is not in waitq_ or deferq_
104 : ///
105 : /// run_count_: Number of running tasks for this TaskEntry
106 : class TaskEntry {
107 : public:
108 : TaskEntry(int task_id);
109 : TaskEntry(int task_id, int task_instance);
110 : ~TaskEntry();
111 :
112 : void AddPolicy(TaskEntry *entry);
113 62502448 : size_t WaitQSize() const { return waitq_.size(); };
114 : void AddToWaitQ(Task *t);
115 : bool DeleteFromWaitQ(Task *t);
116 :
117 : /// @brief Adds a task to deferq_.
118 : /// Only one task of a given instance goes into deferq_ for its policies.
119 : void AddToDeferQ(TaskEntry *entry);
120 :
121 : /// @brief Deletes a task from deferq_.
122 : void DeleteFromDeferQ(TaskEntry &entry);
123 :
124 : TaskEntry *ActiveEntryInPolicy();
125 : bool DeferOnPolicyFail(Task *t);
126 :
127 : /// @brief Starts a task.
128 : /// If there are more entries in waitq_ add them to deferq_.
129 : void RunTask(Task *t);
130 :
131 : /// @brief Starts executing tasks from deferq_ of a TaskEntry.
132 : void RunDeferQ();
133 :
134 : /// @brief Starts executing tasks from deferq_ of TaskEntry and
135 : /// TaskGroup in the temporal order
136 : void RunCombinedDeferQ();
137 : void RunWaitQ();
138 : void RunDeferEntry();
139 :
140 : /// @brief Starts executing tasks from deferq_ of TaskEntries which are
141 : /// enabled.
142 : void RunDeferQForGroupEnable();
143 :
144 : void TaskExited(Task *t, TaskGroup *group);
145 : TaskStats *GetTaskStats();
146 : void ClearTaskStats();
147 : void ClearQueues();
148 :
149 : /// @brief Addition/deletion of TaskEntry in the deferq_ is based on the
150 : /// seqno.
151 : /// seqno of the first Task in the waitq_ is used as the key. This function
152 : /// would be invoked by the comparison function during addition/deletion
153 : /// of TaskEntry in the deferq_.
154 : boost::optional<uint64_t> GetTaskDeferEntrySeqno() const;
155 :
156 : /// @brief Returns the code ID of this task entry
157 : int task_code_id() const { return task_code_id_; }
158 :
159 : /// @brief Returns the data ID of this task entry
160 : int task_data_id() const { return task_data_id_; }
161 :
162 : /// @brief Returns the count of runs for this task entry
163 : int GetRunCount() const { return run_count_; }
164 :
165 : /// @brief Disables this task entry
166 0 : void SetDisable(bool disable) { disable_ = disable; }
167 35405347 : bool IsDisabled() { return disable_; }
168 : void GetSandeshData(SandeshTaskEntry *resp) const;
169 :
170 : private:
171 : friend class TaskGroup;
172 : friend class TaskScheduler;
173 :
174 : /// @brief List of Task's in waitq_
175 : typedef boost::intrusive::member_hook<Task,
176 : boost::intrusive::list_member_hook<>, &Task::waitq_hook_> WaitQHook;
177 : typedef boost::intrusive::list<Task, WaitQHook> TaskWaitQ;
178 :
179 : boost::intrusive::set_member_hook<> task_defer_node;
180 : typedef boost::intrusive::member_hook<TaskEntry,
181 : boost::intrusive::set_member_hook<>,
182 : &TaskEntry::task_defer_node> TaskDeferListOption;
183 :
184 : /// @brief It is a tree of TaskEntries deferred and waiting on the
185 : /// containing task to exit. The tree is sorted by seqno_ of first
186 : /// task in the TaskEntry
187 : typedef boost::intrusive::set<TaskEntry, TaskDeferListOption,
188 : boost::intrusive::compare<TaskDeferEntryCmp> > TaskDeferList;
189 :
190 : int task_code_id_;
191 : int task_data_id_;
192 :
193 : /// @brief No. of tasks running
194 : int run_count_;
195 :
196 : /// @brief Task currently running
197 : Task *run_task_;
198 :
199 : /// @brief Tasks waiting to run on some condition
200 : TaskWaitQ waitq_;
201 :
202 : /// @brief Policy rules for a task
203 : TaskEntryList policyq_;
204 :
205 : /// @brief Tasks deferred for this to exit
206 : TaskDeferList *deferq_;
207 : TaskEntry *deferq_task_entry_;
208 : TaskGroup *deferq_task_group_;
209 : bool disable_;
210 :
211 : /// @brief Cummulative Maintenance stats
212 : TaskStats stats_;
213 :
214 : DISALLOW_COPY_AND_ASSIGN(TaskEntry);
215 : };
216 :
217 : /// @brief Comparison routine for the TaskDeferList
218 : struct TaskDeferEntryCmp {
219 14639143 : bool operator() (const TaskEntry &lhs, const TaskEntry &rhs) const {
220 14639143 : return (lhs.GetTaskDeferEntrySeqno() <
221 29278286 : rhs.GetTaskDeferEntrySeqno());
222 : }
223 : };
224 :
225 : /// @brief TaskGroup maintains per <task-id> information including,
226 : ///
227 : /// polic_set_ : Boolean used to ensure policy is set only once per task
228 : /// Task policy change is not yet supported
229 : ///
230 : /// policy_ : List of policy rules for the task
231 : ///
232 : /// run_count_ : Number of tasks running in context of this task-group
233 : ///
234 : /// deferq_ : Tasks deferred till run_count_ on this task becomes 0
235 : ///
236 : /// task_entry_ : Default TaskEntry used for task without an instance
237 : ///
238 : /// disable_entry_ : TaskEntry which maintains a deferQ for tasks enqueued
239 : /// while TaskGroup is disabled
240 : class TaskGroup {
241 : public:
242 : TaskGroup(int task_id);
243 : ~TaskGroup();
244 :
245 : TaskEntry *QueryTaskEntry(int task_instance) const;
246 : TaskEntry *GetTaskEntry(int task_instance);
247 : void AddPolicy(TaskGroup *group);
248 :
249 : /// @brief Add task to deferq_
250 : /// Only one task of a given instance goes into deferq_ for its policies.
251 : void AddToDeferQ(TaskEntry *entry);
252 :
253 : /// @brief Enqueue TaskEntry in disable_entry's deferQ
254 : void AddToDisableQ(TaskEntry *entry);
255 :
256 : /// @brief Add TaskEntries to disable_entry_ which have tasks enqueued and
257 : /// are already disabled.
258 : void AddEntriesToDisableQ();
259 :
260 524 : TaskEntry *GetDisableEntry() { return disable_entry_; }
261 :
262 : /// @brief Delete task from deferq_
263 : void DeleteFromDeferQ(TaskEntry &entry);
264 : TaskGroup *ActiveGroupInPolicy();
265 : bool DeferOnPolicyFail(TaskEntry *entry, Task *t);
266 :
267 : /// @brief Returns true, if the waiq_ of all the tasks in the group are
268 : /// empty.
269 : ///
270 : /// Note: This function is invoked from TaskScheduler::IsEmpty() for each
271 : /// task group and is intended to be invoked only in the test code. If this
272 : /// function needs to be used outside test code, then we may want to
273 : /// consider storing the waitq_ count for performance reason.
274 : bool IsWaitQEmpty();
275 :
276 20715073 : int TaskRunCount() const {return run_count_;};
277 :
278 : /// @brief Starts executing tasks from deferq_ of a TaskGroup
279 : void RunDeferQ();
280 :
281 : /// @brief Run tasks that maybe suspended. Schedule tasks only for
282 : /// TaskEntries which are enabled.
283 : void RunDisableEntries();
284 : void TaskExited(Task *t);
285 : void PolicySet();
286 7234883 : void TaskStarted() {run_count_++;};
287 0 : void IncrementTotalRunTime(int64_t rtime) { total_run_time_ += rtime; }
288 : TaskStats *GetTaskGroupStats();
289 : TaskStats *GetTaskStats();
290 : TaskStats *GetTaskStats(int task_instance);
291 : void ClearTaskGroupStats();
292 : void ClearTaskStats();
293 : void ClearTaskStats(int instance_id);
294 8 : void SetDisable(bool disable) { disable_ = disable; }
295 22005162 : bool IsDisabled() { return disable_; }
296 : void GetSandeshData(SandeshTaskGroup *resp, bool summary) const;
297 :
298 0 : int task_id() const { return task_code_id_; }
299 0 : size_t deferq_size() const { return deferq_.size(); }
300 0 : size_t num_tasks() const {
301 0 : size_t count = 0;
302 0 : for (TaskEntryList::const_iterator it = task_entry_db_.begin();
303 0 : it != task_entry_db_.end(); ++it) {
304 0 : if (*it != NULL) {
305 0 : count++;
306 : }
307 : }
308 0 : return count;
309 : }
310 :
311 7234883 : oneapi::tbb::task_group &tbb_group() {
312 7234883 : return tbb_group_;
313 : }
314 :
315 : private:
316 : friend class TaskEntry;
317 : friend class TaskScheduler;
318 :
319 : /// @brief Vector of Task Group policies
320 : typedef std::vector<TaskGroup *> TaskGroupPolicyList;
321 : typedef boost::intrusive::member_hook<TaskEntry,
322 : boost::intrusive::set_member_hook<>,
323 : &TaskEntry::task_defer_node> TaskDeferListOption;
324 :
325 : /// @brief It is a tree of TaskEntries deferred and waiting on the
326 : /// containing task to exit. The tree is sorted by seqno_ of first
327 : /// task in the TaskEntry
328 : typedef boost::intrusive::set<TaskEntry, TaskDeferListOption,
329 : boost::intrusive::compare<TaskDeferEntryCmp> > TaskDeferList;
330 :
331 : static const int kVectorGrowSize = 16;
332 : int task_code_id_;
333 :
334 : /// @brief A TBB object to store executing tasks.
335 : oneapi::tbb::task_group tbb_group_;
336 :
337 : /// @brief Specifies if policy is already set
338 : bool policy_set_;
339 :
340 : /// @brief No. of tasks running in the group
341 : int run_count_;
342 : std::atomic<uint64_t> total_run_time_;
343 :
344 : /// @brief Policy rules for the group
345 : TaskGroupPolicyList policy_;
346 :
347 : /// @brief Tasks deferred till run_count_ is 0
348 : TaskDeferList deferq_;
349 :
350 : /// @brief Tasks deferred till run_count_ is 0
351 : TaskEntry *task_entry_;
352 :
353 : /// @brief Task entry for disabled group
354 : TaskEntry *disable_entry_;
355 :
356 : /// @brief task-entries in this group
357 : TaskEntryList task_entry_db_;
358 : uint32_t execute_delay_;
359 : uint32_t schedule_delay_;
360 : bool disable_;
361 :
362 : TaskStats stats_;
363 : DISALLOW_COPY_AND_ASSIGN(TaskGroup);
364 : };
365 :
366 : ////////////////////////////////////////////////////////////////////////////
367 : // Implementation for class TaskImpl
368 : ////////////////////////////////////////////////////////////////////////////
369 :
370 7234076 : void TaskFunctor::operator ()() const {
371 7234076 : TaskInfo::reference running = task_running.local();
372 7231212 : running = parent_;
373 7231212 : parent_->tbb_state(Task::TBB_EXEC);
374 : try {
375 7230991 : uint64_t t = 0;
376 7230991 : if (parent_->enqueue_time() != 0) {
377 0 : t = ClockMonotonicUsec();
378 0 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
379 0 : if ((t - parent_->enqueue_time()) >
380 0 : scheduler->schedule_delay(parent_)) {
381 0 : TASK_TRACE(scheduler, parent_, "TBB schedule time(in usec) ",
382 : (t - parent_->enqueue_time()));
383 : }
384 7230823 : } else if (TaskScheduler::GetInstance()->track_run_time()) {
385 0 : t = ClockMonotonicUsec();
386 : }
387 7230683 : bool is_complete = parent_->Run();
388 7231861 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
389 7231995 : if (t != 0) {
390 0 : int64_t delay = ClockMonotonicUsec() - t;
391 0 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
392 0 : uint32_t execute_delay = scheduler->execute_delay(parent_);
393 0 : if (execute_delay && delay > execute_delay) {
394 0 : TASK_TRACE(scheduler, parent_, "Run time(in usec) ", delay);
395 : }
396 0 : if (scheduler->track_run_time()) {
397 : TaskGroup *group =
398 0 : scheduler->QueryTaskGroup(parent_->task_code_id());
399 0 : group->IncrementTotalRunTime(delay);
400 : }
401 : }
402 7231827 : running = NULL;
403 7231827 : if (is_complete == true) {
404 5817992 : parent_->set_task_complete();
405 : } else {
406 1413835 : parent_->set_task_recycle();
407 : }
408 7231224 : scheduler->OnTaskExit(parent_);
409 0 : } catch (std::exception &e) {
410 :
411 : // Store exception information statically, to easily read exception
412 : // information from the core.
413 0 : static std::string what = e.what();
414 :
415 0 : LOG(ERROR, "!!!! ERROR !!!! Task caught fatal exception: " << what
416 : << " TaskImpl: " << this);
417 0 : assert(0);
418 0 : } catch (...) {
419 0 : LOG(ERROR, "!!!! ERROR !!!! Task caught fatal unknown exception"
420 : << " TaskImpl: " << this);
421 0 : assert(0);
422 0 : }
423 7234713 : }
424 :
425 14469334 : TaskFunctor::~TaskFunctor() {
426 14469334 : }
427 :
428 15932 : int TaskScheduler::GetThreadCount(int thread_count) {
429 : static bool init_;
430 : static int num_cores_;
431 :
432 15932 : if (init_) {
433 15682 : return num_cores_ * ThreadAmpFactor_;
434 : }
435 :
436 250 : char *num_cores_str = getenv("TBB_THREAD_COUNT");
437 250 : if (!num_cores_str) {
438 250 : if (thread_count == 0) {
439 250 : num_cores_ = oneapi::tbb::info::default_concurrency();
440 : } else {
441 0 : num_cores_ = thread_count;
442 : }
443 : } else {
444 0 : num_cores_ = strtol(num_cores_str, NULL, 0);
445 : }
446 :
447 250 : init_ = true;
448 250 : return num_cores_ * ThreadAmpFactor_;
449 : }
450 :
451 0 : int TaskScheduler::GetDefaultThreadCount() {
452 0 : return oneapi::tbb::this_task_arena::max_concurrency();
453 : }
454 :
455 250 : bool TaskScheduler::ShouldUseSpawn() {
456 250 : if (getenv("TBB_USE_SPAWN"))
457 0 : return true;
458 :
459 250 : return false;
460 : }
461 :
462 : ////////////////////////////////////////////////////////////////////////////
463 : // Implementation for class TaskScheduler
464 : ////////////////////////////////////////////////////////////////////////////
465 :
466 250 : TaskScheduler::TaskScheduler(int task_count) :
467 250 : use_spawn_(ShouldUseSpawn()),
468 500 : tbb_global_control_(
469 : oneapi::tbb::global_control::max_allowed_parallelism,
470 250 : GetThreadCount(task_count) + 1),
471 250 : task_scheduler_(GetThreadCount(task_count) + 1),
472 250 : running_(true), seqno_(0), id_max_(0), log_fn_(), track_run_time_(false),
473 250 : measure_delay_(false), schedule_delay_(0), execute_delay_(0),
474 250 : enqueue_count_(0), done_count_(0), cancel_count_(0), evm_(NULL),
475 250 : tbb_awake_task_(NULL), task_monitor_(NULL) {
476 250 : hw_thread_count_ = GetThreadCount(task_count);
477 250 : task_group_db_.resize(TaskScheduler::kVectorGrowSize);
478 250 : stop_entry_ = new TaskEntry(-1);
479 250 : }
480 :
481 250 : TaskScheduler::~TaskScheduler() {
482 : TaskGroup *group;
483 :
484 250 : for (TaskGroupDb::iterator iter = task_group_db_.begin();
485 8129 : iter != task_group_db_.end(); ++iter) {
486 7879 : if ((group = *iter) == NULL) {
487 3647 : continue;
488 : }
489 4232 : *iter = NULL;
490 4232 : delete group;
491 : }
492 :
493 4595 : for (TaskIdMap::iterator loc = id_map_.begin(); loc != id_map_.end();
494 4345 : id_map_.erase(loc++)) {
495 : }
496 :
497 250 : delete stop_entry_;
498 250 : stop_entry_ = NULL;
499 250 : task_group_db_.clear();
500 :
501 250 : return;
502 250 : }
503 :
504 0 : void TaskScheduler::Initialize(uint32_t thread_count, EventManager *evm) {
505 0 : assert(singleton_.get() == NULL);
506 0 : singleton_.reset(new TaskScheduler((int)thread_count));
507 :
508 0 : if (evm) {
509 0 : singleton_.get()->evm_ = evm;
510 0 : singleton_.get()->tbb_awake_task_ = new TaskTbbKeepAwake();
511 0 : assert(singleton_.get()->tbb_awake_task_);
512 :
513 0 : singleton_.get()->tbb_awake_task_->StartTbbKeepAwakeTask(
514 : singleton_.get(), evm,
515 : "TaskScheduler::TbbKeepAwake");
516 : }
517 0 : }
518 :
519 0 : void TaskScheduler::set_event_manager(EventManager *evm) {
520 0 : assert(evm);
521 0 : evm_ = evm;
522 0 : if (tbb_awake_task_ == NULL) {
523 0 : tbb_awake_task_ = new TaskTbbKeepAwake();
524 0 : assert(tbb_awake_task_);
525 :
526 0 : tbb_awake_task_->StartTbbKeepAwakeTask(this, evm,
527 : "TaskScheduler::TbbKeepAwake");
528 : }
529 0 : }
530 :
531 4 : void TaskScheduler::ModifyTbbKeepAwakeTimeout(uint32_t timeout) {
532 4 : if (tbb_awake_task_) {
533 0 : tbb_awake_task_->ModifyTbbKeepAwakeTimeout(timeout);
534 : }
535 4 : }
536 :
537 4 : void TaskScheduler::EnableMonitor(EventManager *evm,
538 : uint64_t tbb_keepawake_time_msec,
539 : uint64_t inactivity_time_msec,
540 : uint64_t poll_interval_msec) {
541 4 : if (task_monitor_ != NULL)
542 0 : return;
543 :
544 4 : task_monitor_ = new TaskMonitor(this, tbb_keepawake_time_msec,
545 4 : inactivity_time_msec, poll_interval_msec);
546 4 : task_monitor_->Start(evm);
547 : }
548 :
549 0 : void TaskScheduler::Log(const char *file_name, uint32_t line_no,
550 : const Task *task, const char *description,
551 : uint64_t delay) {
552 0 : if (log_fn_.empty() == false) {
553 0 : log_fn_(file_name, line_no, task, description, delay);
554 : }
555 0 : }
556 :
557 4 : void TaskScheduler::RegisterLog(LogFn fn) {
558 4 : log_fn_ = fn;
559 4 : }
560 :
561 0 : uint32_t TaskScheduler::schedule_delay(Task *task) const {
562 0 : if (task->schedule_delay() > schedule_delay_)
563 0 : return task->schedule_delay();
564 0 : return schedule_delay_;
565 : }
566 :
567 0 : uint32_t TaskScheduler::execute_delay(Task *task) const {
568 0 : if (task->execute_delay() > execute_delay_)
569 0 : return task->execute_delay();
570 0 : return execute_delay_;
571 : }
572 :
573 101715380 : TaskScheduler *TaskScheduler::GetInstance() {
574 101715380 : if (singleton_.get() == NULL) {
575 250 : singleton_.reset(new TaskScheduler());
576 : }
577 101695019 : return singleton_.get();
578 : }
579 :
580 38510405 : TaskGroup *TaskScheduler::GetTaskGroup(int task_id) {
581 38510405 : assert(task_id >= 0);
582 38510405 : int size = task_group_db_.size();
583 38510405 : if (size <= task_id) {
584 187 : task_group_db_.resize(task_id + TaskScheduler::kVectorGrowSize);
585 : }
586 :
587 38510405 : TaskGroup *group = task_group_db_[task_id];
588 38510405 : if (group == NULL) {
589 4232 : group = new TaskGroup(task_id);
590 4232 : task_group_db_[task_id] = group;
591 : }
592 :
593 38510405 : return group;
594 : }
595 :
596 20306097 : TaskGroup *TaskScheduler::QueryTaskGroup(int task_id) {
597 20306097 : return task_group_db_[task_id];
598 : }
599 :
600 5132171 : bool TaskScheduler::IsTaskGroupEmpty(int task_id) const {
601 5132171 : CHECK_CONCURRENCY("bgp::Config");
602 5132171 : std::scoped_lock lock(mutex_);
603 5132171 : TaskGroup *group = task_group_db_[task_id];
604 5132171 : assert(group);
605 5132171 : assert(group->TaskRunCount() == 0);
606 10264342 : return group->IsWaitQEmpty();
607 5132171 : }
608 :
609 7240127 : TaskEntry *TaskScheduler::GetTaskEntry(int task_id, int task_instance) {
610 7240127 : TaskGroup *group = GetTaskGroup(task_id);
611 7240127 : return group->GetTaskEntry(task_instance);
612 : }
613 :
614 7235407 : TaskEntry *TaskScheduler::QueryTaskEntry(int task_id, int task_instance) {
615 7235407 : TaskGroup *group = QueryTaskGroup(task_id);
616 7235407 : if (group == NULL)
617 0 : return NULL;
618 7235407 : return group->QueryTaskEntry(task_instance);
619 : }
620 :
621 8 : void TaskScheduler::EnableLatencyThresholds(uint32_t execute,
622 : uint32_t schedule) {
623 8 : execute_delay_ = execute;
624 8 : schedule_delay_ = schedule;
625 8 : measure_delay_ = (execute_delay_ != 0 || schedule_delay_ != 0);
626 8 : }
627 :
628 24 : void TaskScheduler::SetLatencyThreshold(const std::string &name,
629 : uint32_t execute, uint32_t schedule) {
630 24 : int task_id = GetTaskId(name);
631 24 : TaskGroup *group = GetTaskGroup(task_id);
632 24 : group->execute_delay_ = execute;
633 24 : group->schedule_delay_ = schedule;
634 24 : }
635 :
636 3066 : void TaskScheduler::SetPolicy(int task_id, TaskPolicy &policy) {
637 3066 : std::scoped_lock lock(mutex_);
638 :
639 3066 : TaskGroup *group = GetTaskGroup(task_id);
640 3066 : TaskEntry *group_entry = group->GetTaskEntry(-1);
641 3066 : group->PolicySet();
642 :
643 30402 : for (const auto& pol_item: policy) {
644 27336 : if (pol_item.match_data_id == -1) {
645 24976 : TaskGroup *policy_group = GetTaskGroup(pol_item.match_code_id);
646 24976 : group->AddPolicy(policy_group);
647 24976 : policy_group->AddPolicy(group);
648 : } else {
649 2360 : TaskEntry *entry = GetTaskEntry(task_id, pol_item.match_data_id);
650 4720 : TaskEntry *policy_entry = GetTaskEntry(pol_item.match_code_id,
651 2360 : pol_item.match_data_id);
652 2360 : entry->AddPolicy(policy_entry);
653 2360 : policy_entry->AddPolicy(entry);
654 :
655 2360 : group_entry->AddPolicy(policy_entry);
656 2360 : policy_entry->AddPolicy(group_entry);
657 : }
658 : }
659 3066 : }
660 :
661 5818253 : void TaskScheduler::Enqueue(Task *t) {
662 5818253 : std::scoped_lock lock(mutex_);
663 5820546 : EnqueueUnLocked(t);
664 5820546 : }
665 :
666 7235407 : void TaskScheduler::EnqueueUnLocked(Task *t) {
667 7235407 : if (measure_delay_) {
668 0 : t->enqueue_time_ = ClockMonotonicUsec();
669 : }
670 : // Ensure that task is enqueued only once.
671 7235407 : assert(t->seqno() == 0);
672 7235407 : enqueue_count_++;
673 7235407 : t->seqno(++seqno_);
674 7235407 : TaskGroup *group = GetTaskGroup(t->task_code_id());
675 7235407 : t->schedule_delay_ = group->schedule_delay_;
676 7235407 : t->execute_delay_ = group->execute_delay_;
677 7235407 : group->stats_.enqueue_count_++;
678 :
679 7235407 : TaskEntry *entry = GetTaskEntry(t->task_code_id(), t->task_data_id());
680 7235407 : entry->stats_.enqueue_count_++;
681 : // If either TaskGroup or TaskEntry is disabled for Unit-Test purposes,
682 : // enqueue new task in waitq and update TaskGroup if needed.
683 :
684 7235407 : if (group->IsDisabled() || entry->IsDisabled()) {
685 16 : entry->AddToWaitQ(t);
686 16 : if (group->IsDisabled()) {
687 16 : group->AddToDisableQ(entry);
688 : }
689 16 : return;
690 : }
691 :
692 : // Add task to waitq_ if its already populated
693 7235391 : if (entry->WaitQSize() != 0) {
694 2849506 : entry->AddToWaitQ(t);
695 2849506 : return;
696 : }
697 :
698 : // Is scheduler stopped? Dont add task to deferq_ if scheduler is stopped.
699 : // TaskScheduler::Start() will run tasks from waitq_
700 4385885 : if (!running_) {
701 5309 : entry->AddToWaitQ(t);
702 5309 : stop_entry_->AddToDeferQ(entry);
703 5309 : return;
704 : }
705 :
706 : // Check Task Group policy. On policy violation, DeferOnPolicyFail()
707 : // adds the Task to the TaskEntry's waitq_ and the TaskEntry will be
708 : // added to deferq_ of the matching TaskGroup.
709 4380576 : if (group->DeferOnPolicyFail(entry, t)) {
710 1622396 : return;
711 : }
712 :
713 : // Check Task Entry policy. On policy violation, DeferOnPolicyFail()
714 : // adds the Task to the TaskEntry's waitq_ and the TaskEntry will be
715 : // added to deferq_ of the matching TaskEntry.
716 2758180 : if (entry->DeferOnPolicyFail(t)) {
717 1468486 : return;
718 : }
719 :
720 :
721 1289694 : entry->RunTask(t);
722 :
723 1289694 : return;
724 : }
725 :
726 527 : TaskScheduler::CancelReturnCode TaskScheduler::Cancel(Task *t) {
727 527 : std::scoped_lock lock(mutex_);
728 :
729 : // If the task is in RUN state, mark the task for cancellation and return.
730 527 : if (t->state_ == Task::RUN) {
731 3 : t->task_cancel_ = true;
732 524 : } else if (t->state_ == Task::WAIT) {
733 524 : TaskEntry *entry = QueryTaskEntry(t->task_code_id(), t->task_data_id());
734 524 : TaskGroup *group = QueryTaskGroup(t->task_code_id());
735 524 : assert(entry->WaitQSize());
736 : // Get the first entry in the waitq_
737 524 : Task *first_wait_task = &(*entry->waitq_.begin());
738 524 : TaskEntry *disable_entry = group->GetDisableEntry();
739 524 : assert(entry->DeleteFromWaitQ(t) == true);
740 : // If the waitq_ is empty, then remove the TaskEntry from the deferq.
741 524 : if (!entry->WaitQSize()) {
742 412 : if (entry->deferq_task_group_) {
743 361 : assert(entry->deferq_task_entry_ == NULL);
744 361 : entry->deferq_task_group_->DeleteFromDeferQ(*entry);
745 51 : } else if (entry->deferq_task_entry_) {
746 51 : entry->deferq_task_entry_->DeleteFromDeferQ(*entry);
747 0 : } else if (group->IsDisabled()) {
748 : // Remove TaskEntry from deferq of disable_entry
749 0 : disable_entry->DeleteFromDeferQ(*entry);
750 : } else {
751 0 : if (!entry->IsDisabled()) {
752 0 : assert(0);
753 : }
754 : }
755 112 : } else if (t == first_wait_task) {
756 : // TaskEntry is inserted in the deferq_ based on the Task seqno.
757 : // deferq_ comparison function uses the seqno of the first entry in
758 : // the waitq_. Therefore, if the task to be cancelled is the first
759 : // entry in the waitq_, then delete the entry from the deferq_ and
760 : // add it again.
761 31 : TaskGroup *deferq_tgroup = entry->deferq_task_group_;
762 31 : TaskEntry *deferq_tentry = entry->deferq_task_entry_;
763 31 : if (deferq_tgroup) {
764 16 : assert(deferq_tentry == NULL);
765 16 : deferq_tgroup->DeleteFromDeferQ(*entry);
766 16 : deferq_tgroup->AddToDeferQ(entry);
767 15 : } else if (deferq_tentry) {
768 15 : deferq_tentry->DeleteFromDeferQ(*entry);
769 15 : deferq_tentry->AddToDeferQ(entry);
770 0 : } else if (group->IsDisabled()) {
771 : // Remove TaskEntry from deferq of disable_entry and add back
772 0 : disable_entry->DeleteFromDeferQ(*entry);
773 0 : disable_entry->AddToDeferQ(entry);
774 : } else {
775 0 : if (!entry->IsDisabled()) {
776 0 : assert(0);
777 : }
778 : }
779 : }
780 524 : delete t;
781 524 : cancel_count_++;
782 524 : return CANCELLED;
783 : } else {
784 0 : return FAILED;
785 : }
786 3 : return QUEUED;
787 527 : }
788 :
789 7231265 : void TaskScheduler::OnTaskExit(Task *t) {
790 7231265 : std::scoped_lock lock(mutex_);
791 7234883 : done_count_++;
792 :
793 7234883 : t->tbb_state(Task::TBB_DONE);
794 7234883 : TaskEntry *entry = QueryTaskEntry(t->task_code_id(), t->task_data_id());
795 7234883 : entry->TaskExited(t, GetTaskGroup(t->task_code_id()));
796 :
797 : //
798 : // Delete the task it is not marked for recycling or already cancelled.
799 : //
800 7234883 : if ((t->task_recycle_ == false) || (t->task_cancel_ == true)) {
801 : // Delete the container Task object, if the
802 : // task is not marked to be recycled (or)
803 : // if the task is marked for cancellation
804 5820022 : if (t->task_cancel_ == true) {
805 3 : t->OnTaskCancel();
806 : }
807 5820022 : delete t;
808 5820022 : return;
809 : }
810 :
811 : // Task is being recycled, reset the state, seq_no and TBB task handle
812 1414861 : t->task_impl_ = oneapi::tbb::task_handle{};
813 1414861 : t->seqno(0);
814 1414861 : t->state(Task::INIT);
815 1414861 : t->tbb_state(Task::TBB_INIT);
816 1414861 : EnqueueUnLocked(t);
817 7234883 : }
818 :
819 37729 : void TaskScheduler::Stop() {
820 37729 : std::scoped_lock lock(mutex_);
821 :
822 37729 : running_ = false;
823 37729 : }
824 :
825 37729 : void TaskScheduler::Start() {
826 37729 : std::scoped_lock lock(mutex_);
827 :
828 37729 : running_ = true;
829 :
830 : // Run all tasks that may be suspended
831 37729 : stop_entry_->RunDeferQ();
832 75458 : return;
833 37729 : }
834 :
835 0 : void TaskScheduler::Print() {
836 0 : for (TaskGroupDb::iterator iter = task_group_db_.begin();
837 0 : iter != task_group_db_.end(); ++iter) {
838 0 : TaskGroup *group = *iter;
839 0 : if (group == NULL) {
840 0 : continue;
841 : }
842 :
843 0 : cout << "id: " << group->task_id() <<
844 0 : " run: " << group->TaskRunCount() << endl;
845 0 : cout << "deferq: " << group->deferq_size() <<
846 0 : " task count: " << group->num_tasks() << endl;
847 : }
848 0 : }
849 :
850 1581854 : bool TaskScheduler::IsEmpty(bool running_only) {
851 : TaskGroup *group;
852 :
853 1581854 : std::scoped_lock lock(mutex_);
854 :
855 1581854 : for (TaskGroupDb::iterator it = task_group_db_.begin();
856 22631242 : it != task_group_db_.end(); ++it) {
857 22281154 : if ((group = *it) == NULL) {
858 6698252 : continue;
859 : }
860 15582902 : if (group->TaskRunCount()) {
861 1231766 : return false;
862 : }
863 14769735 : if (group->IsDisabled()) {
864 0 : continue;
865 : }
866 14769735 : if ((false == running_only) && (false == group->IsWaitQEmpty())) {
867 418599 : return false;
868 : }
869 : }
870 :
871 350088 : return true;
872 1581854 : }
873 13 : std::string TaskScheduler::GetTaskName(int task_id) const {
874 405 : for (TaskIdMap::const_iterator it = id_map_.begin(); it != id_map_.end();
875 392 : it++) {
876 405 : if (task_id == it->second)
877 13 : return it->first;
878 : }
879 :
880 0 : return "ERROR";
881 : }
882 :
883 63604833 : int TaskScheduler::GetTaskId(const string &name) {
884 : {
885 : // Grab read-only lock first. Most of the time, task-id already exists
886 : // in the id_map_. Hence there should not be any contention for lock
887 : // aquisition.
888 63604833 : std::shared_lock<std::shared_mutex> lock(id_map_mutex_);
889 63738134 : TaskIdMap::iterator loc = id_map_.find(name);
890 63574579 : if (loc != id_map_.end()) {
891 63564753 : return loc->second;
892 : }
893 63560938 : }
894 :
895 : // Grab read-write lock to allocate a new task id and insert into the map.
896 4345 : std::unique_lock<std::shared_mutex> lock(id_map_mutex_);
897 4345 : int tid = ++id_max_;
898 4345 : id_map_.insert(make_pair(name, tid));
899 4345 : return tid;
900 4345 : }
901 :
902 0 : void TaskScheduler::ClearTaskGroupStats(int task_id) {
903 0 : TaskGroup *group = GetTaskGroup(task_id);
904 0 : if (group == NULL)
905 0 : return;
906 :
907 0 : group->ClearTaskGroupStats();
908 : }
909 :
910 27 : void TaskScheduler::ClearTaskStats(int task_id) {
911 27 : TaskGroup *group = GetTaskGroup(task_id);
912 27 : if (group == NULL)
913 0 : return;
914 :
915 27 : group->ClearTaskStats();
916 : }
917 :
918 0 : void TaskScheduler::ClearTaskStats(int task_id, int instance_id) {
919 0 : TaskGroup *group = GetTaskGroup(task_id);
920 0 : if (group == NULL)
921 0 : return;
922 :
923 0 : group->ClearTaskStats(instance_id);
924 : }
925 :
926 0 : TaskStats *TaskScheduler::GetTaskGroupStats(int task_id) {
927 0 : TaskGroup *group = GetTaskGroup(task_id);
928 0 : if (group == NULL)
929 0 : return NULL;
930 :
931 0 : return group->GetTaskGroupStats();
932 : }
933 :
934 4 : TaskStats *TaskScheduler::GetTaskStats(int task_id) {
935 4 : TaskGroup *group = GetTaskGroup(task_id);
936 4 : if (group == NULL)
937 0 : return NULL;
938 :
939 4 : return group->GetTaskStats();
940 : }
941 :
942 0 : TaskStats *TaskScheduler::GetTaskStats(int task_id, int instance_id) {
943 0 : TaskGroup *group = GetTaskGroup(task_id);
944 0 : if (group == NULL)
945 0 : return NULL;
946 :
947 0 : return group->GetTaskStats(instance_id);
948 : }
949 :
950 190 : void TaskScheduler::Terminate() {
951 190 : if (task_monitor_) {
952 4 : task_monitor_->Terminate();
953 4 : delete task_monitor_;
954 4 : task_monitor_ = NULL;
955 : }
956 :
957 190 : for (int i = 0; i < 10000; i++) {
958 190 : if (IsEmpty()) break;
959 0 : usleep(1000);
960 : }
961 190 : assert(IsEmpty());
962 190 : if (tbb_awake_task_) {
963 0 : tbb_awake_task_->ShutTbbKeepAwakeTask();
964 0 : delete tbb_awake_task_;
965 0 : tbb_awake_task_ = NULL;
966 : }
967 190 : evm_ = NULL;
968 190 : singleton_->task_scheduler_.terminate();
969 : oneapi::tbb::task_scheduler_handle handle =
970 190 : oneapi::tbb::task_scheduler_handle{oneapi::tbb::attach{}};
971 190 : oneapi::tbb::finalize(handle);
972 190 : singleton_.reset(NULL);
973 190 : }
974 :
975 57150 : void TaskScheduler::SetRunningTask(Task *unit_test) {
976 57150 : TaskInfo::reference running = task_running.local();
977 57150 : running = unit_test;
978 57150 : }
979 :
980 57150 : void TaskScheduler::ClearRunningTask() {
981 57150 : TaskInfo::reference running = task_running.local();
982 57150 : running = NULL;
983 57150 : }
984 :
985 44 : void TaskScheduler::SetThreadAmpFactor(int n) {
986 44 : ThreadAmpFactor_ = n;
987 44 : }
988 :
989 4 : void TaskScheduler::DisableTaskGroup(int task_id) {
990 4 : TaskGroup *group = GetTaskGroup(task_id);
991 4 : if (!group->IsDisabled()) {
992 : // Add TaskEntries(that contain enqueued tasks) which are already
993 : // disabled to disable_ entry maintained at TaskGroup.
994 4 : group->SetDisable(true);
995 4 : group->AddEntriesToDisableQ();
996 : }
997 4 : }
998 :
999 4 : void TaskScheduler::EnableTaskGroup(int task_id) {
1000 4 : TaskGroup *group = GetTaskGroup(task_id);
1001 4 : group->SetDisable(false);
1002 : // Run tasks that maybe suspended
1003 4 : group->RunDisableEntries();
1004 4 : }
1005 :
1006 0 : void TaskScheduler::DisableTaskEntry(int task_id, int instance_id) {
1007 0 : TaskEntry *entry = GetTaskEntry(task_id, instance_id);
1008 0 : entry->SetDisable(true);
1009 0 : }
1010 :
1011 0 : void TaskScheduler::EnableTaskEntry(int task_id, int instance_id) {
1012 0 : TaskEntry *entry = GetTaskEntry(task_id, instance_id);
1013 0 : entry->SetDisable(false);
1014 0 : TaskGroup *group = GetTaskGroup(task_id);
1015 : // If group is still disabled, do not schedule the task. Task will be
1016 : // scheduled for run when TaskGroup is enabled.
1017 0 : if (group->IsDisabled()) {
1018 0 : return;
1019 : }
1020 : // Run task instances that maybe suspended
1021 0 : if (entry->WaitQSize() != 0) {
1022 0 : entry->RunDeferEntry();
1023 : }
1024 : }
1025 :
1026 : ////////////////////////////////////////////////////////////////////////////
1027 : // Implementation for class TaskGroup
1028 : ////////////////////////////////////////////////////////////////////////////
1029 :
1030 4232 : TaskGroup::TaskGroup(int task_id) : task_code_id_(task_id), tbb_group_(),
1031 8464 : policy_set_(false), run_count_(0), execute_delay_(0), schedule_delay_(0),
1032 8464 : disable_(false) {
1033 4232 : total_run_time_ = 0;
1034 4232 : task_entry_db_.resize(TaskGroup::kVectorGrowSize);
1035 4232 : task_entry_ = new TaskEntry(task_id);
1036 4232 : memset(&stats_, 0, sizeof(stats_));
1037 4232 : disable_entry_ = new TaskEntry(task_id);
1038 4232 : }
1039 :
1040 4232 : TaskGroup::~TaskGroup() {
1041 4232 : policy_.clear();
1042 4232 : deferq_.clear();
1043 :
1044 4232 : delete task_entry_;
1045 4232 : task_entry_ = NULL;
1046 :
1047 71944 : for (size_t i = 0; i < task_entry_db_.size(); i++) {
1048 67712 : if (task_entry_db_[i] != NULL) {
1049 4901 : delete task_entry_db_[i];
1050 4901 : task_entry_db_[i] = NULL;
1051 : }
1052 : }
1053 :
1054 4232 : delete disable_entry_;
1055 4232 : disable_entry_ = NULL;
1056 4232 : task_entry_db_.clear();
1057 4232 : }
1058 :
1059 7243193 : TaskEntry *TaskGroup::GetTaskEntry(int task_instance) {
1060 7243193 : if (task_instance == -1)
1061 240626 : return task_entry_;
1062 :
1063 7002567 : int size = task_entry_db_.size();
1064 7002567 : if (size <= task_instance) {
1065 0 : task_entry_db_.resize(task_instance + TaskGroup::kVectorGrowSize);
1066 : }
1067 :
1068 7002567 : TaskEntry *entry = task_entry_db_.at(task_instance);
1069 7002567 : if (entry == NULL) {
1070 4901 : entry = new TaskEntry(task_code_id_, task_instance);
1071 4901 : task_entry_db_[task_instance] = entry;
1072 : }
1073 :
1074 7002567 : return entry;
1075 : }
1076 :
1077 7235408 : TaskEntry *TaskGroup::QueryTaskEntry(int task_instance) const {
1078 7235408 : if (task_instance == -1) {
1079 237561 : return task_entry_;
1080 : }
1081 :
1082 6997847 : if (task_instance >= (int)task_entry_db_.size())
1083 0 : return NULL;
1084 :
1085 6997847 : return task_entry_db_[task_instance];
1086 : }
1087 :
1088 49952 : void TaskGroup::AddPolicy(TaskGroup *group) {
1089 49952 : policy_.push_back(group);
1090 49952 : }
1091 :
1092 15206746 : TaskGroup *TaskGroup::ActiveGroupInPolicy() {
1093 15206746 : for (TaskGroupPolicyList::iterator it = policy_.begin();
1094 160860269 : it != policy_.end(); ++it) {
1095 149725883 : if ((*it)->run_count_ != 0) {
1096 4072360 : return (*it);
1097 : }
1098 : }
1099 11134386 : return NULL;
1100 : }
1101 :
1102 15206746 : bool TaskGroup::DeferOnPolicyFail(TaskEntry *entry, Task *task) {
1103 : TaskGroup *group;
1104 15206746 : if ((group = ActiveGroupInPolicy()) != NULL) {
1105 : // TaskEntry is inserted in the deferq_ based on the Task seqno.
1106 : // deferq_ comparison function uses the seqno of the first Task queued
1107 : // in the waitq_. Therefore, add the Task to waitq_ before adding
1108 : // TaskEntry in the deferq_.
1109 4072360 : if (0 == entry->WaitQSize()) {
1110 1622396 : entry->AddToWaitQ(task);
1111 : }
1112 4072360 : group->AddToDeferQ(entry);
1113 4072360 : return true;
1114 : }
1115 11134386 : return false;
1116 : }
1117 :
1118 4072376 : void TaskGroup::AddToDeferQ(TaskEntry *entry) {
1119 4072376 : stats_.defer_count_++;
1120 4072376 : deferq_.insert(*entry);
1121 4072376 : assert(entry->deferq_task_group_ == NULL);
1122 4072376 : entry->deferq_task_group_ = this;
1123 4072376 : }
1124 :
1125 4072376 : void TaskGroup::DeleteFromDeferQ(TaskEntry &entry) {
1126 4072376 : assert(this == entry.deferq_task_group_);
1127 8144752 : deferq_.erase(deferq_.iterator_to(entry));
1128 4072376 : entry.deferq_task_group_ = NULL;
1129 4072376 : }
1130 :
1131 16 : void TaskGroup::AddToDisableQ(TaskEntry *entry) {
1132 16 : disable_entry_->AddToDeferQ(entry);
1133 16 : }
1134 :
1135 3066 : void TaskGroup::PolicySet() {
1136 3066 : assert(policy_set_ == false);
1137 3066 : policy_set_ = true;
1138 3066 : }
1139 :
1140 1818144 : void TaskGroup::RunDeferQ() {
1141 : TaskDeferList::iterator it;
1142 :
1143 1818144 : it = deferq_.begin();
1144 9804772 : while (it != deferq_.end()) {
1145 3084242 : TaskEntry &entry = *it;
1146 3084242 : TaskDeferList::iterator it_work = it++;
1147 3084242 : DeleteFromDeferQ(*it_work);
1148 3084242 : entry.RunDeferEntry();
1149 : }
1150 :
1151 3636288 : return;
1152 : }
1153 :
1154 7234883 : inline void TaskGroup::TaskExited(Task *t) {
1155 7234883 : run_count_--;
1156 7234883 : stats_.total_tasks_completed_++;
1157 7234883 : }
1158 :
1159 4 : void TaskGroup::RunDisableEntries() {
1160 4 : disable_entry_->RunDeferQForGroupEnable();
1161 4 : }
1162 :
1163 4 : void TaskGroup::AddEntriesToDisableQ() {
1164 : TaskEntry *entry;
1165 4 : if (task_entry_->WaitQSize()) {
1166 0 : AddToDisableQ(task_entry_);
1167 : }
1168 :
1169 : // Walk thru the task_entry_db_ and add if waitq is non-empty
1170 4 : for (TaskEntryList::iterator it = task_entry_db_.begin();
1171 68 : it != task_entry_db_.end(); ++it) {
1172 64 : if ((entry = *it) == NULL) {
1173 48 : continue;
1174 : }
1175 16 : if (entry->WaitQSize()) {
1176 0 : AddToDisableQ(entry);
1177 : }
1178 : }
1179 4 : }
1180 :
1181 19124115 : bool TaskGroup::IsWaitQEmpty() {
1182 : TaskEntry *entry;
1183 :
1184 : // Check the waitq_ of the instance -1
1185 19124115 : if (task_entry_->WaitQSize()) {
1186 5 : return false;
1187 : }
1188 :
1189 : // Walk thru the task_entry_db_ until waitq_ of any of the task is non-zero
1190 19124110 : for (TaskEntryList::iterator it = task_entry_db_.begin();
1191 315589543 : it != task_entry_db_.end(); ++it) {
1192 297081375 : if ((entry = *it) == NULL) {
1193 268911435 : continue;
1194 : }
1195 28169940 : if (entry->IsDisabled()) {
1196 0 : continue;
1197 : }
1198 28169940 : if (entry->WaitQSize()) {
1199 615942 : return false;
1200 : }
1201 : }
1202 :
1203 : // Well, no task has been enqueued in this task group
1204 18508168 : return true;
1205 : }
1206 :
1207 0 : void TaskGroup::ClearTaskGroupStats() {
1208 0 : memset(&stats_, 0, sizeof(stats_));
1209 0 : }
1210 :
1211 27 : void TaskGroup::ClearTaskStats() {
1212 27 : task_entry_->ClearTaskStats();
1213 27 : }
1214 :
1215 0 : void TaskGroup::ClearTaskStats(int task_instance) {
1216 0 : TaskEntry *entry = QueryTaskEntry(task_instance);
1217 0 : if (entry != NULL)
1218 0 : entry->ClearTaskStats();
1219 0 : }
1220 :
1221 0 : TaskStats *TaskGroup::GetTaskGroupStats() {
1222 0 : return &stats_;
1223 : }
1224 :
1225 4 : TaskStats *TaskGroup::GetTaskStats() {
1226 4 : return task_entry_->GetTaskStats();
1227 : }
1228 :
1229 0 : TaskStats *TaskGroup::GetTaskStats(int task_instance) {
1230 0 : TaskEntry *entry = QueryTaskEntry(task_instance);
1231 0 : return entry->GetTaskStats();
1232 : }
1233 :
1234 : ////////////////////////////////////////////////////////////////////////////
1235 : // Implementation for class TaskEntry
1236 : ////////////////////////////////////////////////////////////////////////////
1237 :
1238 9802 : TaskEntry::TaskEntry(int task_id, int task_instance) : task_code_id_(task_id),
1239 4901 : task_data_id_(task_instance), run_count_(0), run_task_(NULL),
1240 4901 : waitq_(), deferq_task_entry_(NULL), deferq_task_group_(NULL),
1241 9802 : disable_(false) {
1242 : // When a new TaskEntry is created, adds an implicit rule into policyq_ to
1243 : // ensure that only one Task of an instance is run at a time
1244 4901 : if (task_instance != -1) {
1245 4901 : policyq_.push_back(this);
1246 : }
1247 4901 : memset(&stats_, 0, sizeof(stats_));
1248 : // allocate memory for deferq
1249 4901 : deferq_ = new TaskDeferList;
1250 4901 : }
1251 :
1252 17428 : TaskEntry::TaskEntry(int task_id) : task_code_id_(task_id),
1253 8714 : task_data_id_(-1), run_count_(0), run_task_(NULL),
1254 17428 : deferq_task_entry_(NULL), deferq_task_group_(NULL), disable_(false) {
1255 8714 : memset(&stats_, 0, sizeof(stats_));
1256 : // allocate memory for deferq
1257 8714 : deferq_ = new TaskDeferList;
1258 8714 : }
1259 :
1260 13615 : TaskEntry::~TaskEntry() {
1261 13615 : policyq_.clear();
1262 :
1263 13615 : assert(0 == deferq_->size());
1264 13615 : delete deferq_;
1265 13615 : }
1266 :
1267 9440 : void TaskEntry::AddPolicy(TaskEntry *entry) {
1268 9440 : policyq_.push_back(entry);
1269 9440 : }
1270 :
1271 11134386 : TaskEntry *TaskEntry::ActiveEntryInPolicy() {
1272 33776574 : for (TaskEntryList::iterator it = policyq_.begin(); it != policyq_.end();
1273 22642188 : ++it) {
1274 26541762 : if ((*it)->run_count_ != 0) {
1275 3899574 : return (*it);
1276 : }
1277 : }
1278 :
1279 7234812 : return NULL;
1280 : }
1281 :
1282 11134386 : bool TaskEntry::DeferOnPolicyFail(Task *task) {
1283 : TaskEntry *policy_entry;
1284 :
1285 11134386 : if ((policy_entry = ActiveEntryInPolicy()) != NULL) {
1286 : // TaskEntry is inserted in the deferq_ based on the Task seqno.
1287 : // deferq_ comparison function uses the seqno of the first Task queued
1288 : // in the waitq_. Therefore, add the Task to waitq_ before adding
1289 : // TaskEntry in the deferq_.
1290 3899574 : if (0 == WaitQSize()) {
1291 1468486 : AddToWaitQ(task);
1292 : }
1293 3899574 : policy_entry->AddToDeferQ(this);
1294 3899574 : return true;
1295 : }
1296 7234812 : return false;
1297 : }
1298 :
1299 5945713 : void TaskEntry::AddToWaitQ(Task *t) {
1300 5945713 : t->state(Task::WAIT);
1301 5945713 : stats_.wait_count_++;
1302 5945713 : waitq_.push_back(*t);
1303 :
1304 5945713 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1305 5945713 : TaskGroup *group = scheduler->GetTaskGroup(task_code_id_);
1306 5945713 : group->stats_.wait_count_++;
1307 5945713 : }
1308 :
1309 5945713 : bool TaskEntry::DeleteFromWaitQ(Task *t) {
1310 5945713 : TaskWaitQ::iterator it = waitq_.iterator_to(*t);
1311 11891426 : waitq_.erase(it);
1312 5945713 : return true;
1313 : }
1314 :
1315 6754237 : void TaskEntry::AddToDeferQ(TaskEntry *entry) {
1316 6754237 : stats_.defer_count_++;
1317 6754237 : deferq_->insert(*entry);
1318 6754237 : assert(entry->deferq_task_entry_ == NULL);
1319 6754237 : entry->deferq_task_entry_ = this;
1320 6754237 : }
1321 :
1322 6754237 : void TaskEntry::DeleteFromDeferQ(TaskEntry &entry) {
1323 6754237 : assert(this == entry.deferq_task_entry_);
1324 13508474 : deferq_->erase(deferq_->iterator_to(entry));
1325 6754237 : entry.deferq_task_entry_ = NULL;
1326 6754237 : }
1327 :
1328 7234883 : void TaskEntry::RunTask(Task *t) {
1329 7234883 : stats_.run_count_++;
1330 7234883 : if (t->task_data_id() != -1) {
1331 6997324 : assert(run_task_ == NULL);
1332 6997324 : assert (run_count_ == 0);
1333 6997324 : run_task_ = t;
1334 : }
1335 :
1336 7234883 : run_count_++;
1337 7234883 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1338 7234883 : TaskGroup *group = scheduler->QueryTaskGroup(t->task_code_id());
1339 7234883 : group->TaskStarted();
1340 :
1341 7234883 : t->StartTask(scheduler, group);
1342 7234883 : }
1343 :
1344 5945118 : void TaskEntry::RunWaitQ() {
1345 5945118 : if (waitq_.size() == 0)
1346 0 : return;
1347 :
1348 5945118 : TaskWaitQ::iterator it = waitq_.begin();
1349 :
1350 5945118 : if (task_data_id_ != -1) {
1351 5939058 : Task *t = &(*it);
1352 5939058 : DeleteFromWaitQ(t);
1353 5939058 : RunTask(t);
1354 : // If there are more tasks in waitq_, put them in deferq_
1355 5939058 : if (waitq_.size() != 0) {
1356 2849323 : AddToDeferQ(this);
1357 : }
1358 : } else {
1359 : // Run all instances in waitq_
1360 12262 : while (it != waitq_.end()) {
1361 6131 : Task *t = &(*it);
1362 6131 : DeleteFromWaitQ(t);
1363 6131 : RunTask(t);
1364 6131 : if (waitq_.size() == 0)
1365 6060 : break;
1366 142 : it = waitq_.begin();
1367 : }
1368 : }
1369 : }
1370 :
1371 10826170 : void TaskEntry::RunDeferEntry() {
1372 10826170 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1373 10826170 : TaskGroup *group = scheduler->GetTaskGroup(task_code_id_);
1374 :
1375 : // Sanity check
1376 10826170 : assert(waitq_.size());
1377 10826170 : Task *task = &(*waitq_.begin());
1378 :
1379 : // Check Task group policies
1380 10826170 : if (group->DeferOnPolicyFail(this, task)) {
1381 2449964 : return;
1382 : }
1383 :
1384 : // Check Task entry policies
1385 8376206 : if (DeferOnPolicyFail(task)) {
1386 2431088 : return;
1387 : }
1388 :
1389 5945118 : RunWaitQ();
1390 5945118 : return;
1391 : }
1392 :
1393 4407098 : void TaskEntry::RunDeferQ() {
1394 : TaskDeferList::iterator it;
1395 :
1396 4407098 : it = deferq_->begin();
1397 21112452 : while (it != deferq_->end()) {
1398 6149128 : TaskEntry &entry = *it;
1399 6149128 : TaskDeferList::iterator it_work = it++;
1400 6149128 : DeleteFromDeferQ(*it_work);
1401 6149128 : entry.RunDeferEntry();
1402 : }
1403 :
1404 8814196 : return;
1405 : }
1406 :
1407 4 : void TaskEntry::RunDeferQForGroupEnable() {
1408 : TaskDeferList::iterator it;
1409 :
1410 4 : it = deferq_->begin();
1411 40 : while (it != deferq_->end()) {
1412 16 : TaskEntry &entry = *it;
1413 16 : TaskDeferList::iterator it_work = it++;
1414 16 : DeleteFromDeferQ(*it_work);
1415 16 : if (!entry.IsDisabled()) {
1416 16 : entry.RunDeferEntry();
1417 : }
1418 : }
1419 :
1420 8 : return;
1421 : }
1422 :
1423 5835282 : void TaskEntry::RunCombinedDeferQ() {
1424 5835282 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1425 5835282 : TaskGroup *group = scheduler->QueryTaskGroup(task_code_id_);
1426 : TaskDeferEntryCmp defer_entry_compare;
1427 :
1428 5835282 : TaskDeferList::iterator group_it = group->deferq_.begin();
1429 5835282 : TaskDeferList::iterator entry_it = deferq_->begin();
1430 :
1431 : // Loop thru the deferq_ of TaskEntry and TaskGroup in the temporal order.
1432 : // Exit the loop when any of the queues become empty.
1433 18267060 : while ((group_it != group->deferq_.end()) &&
1434 14249922 : (entry_it != deferq_->end())) {
1435 1592784 : TaskEntry &g_entry = *group_it;
1436 1592784 : TaskEntry &t_entry = *entry_it;
1437 :
1438 1592784 : if (defer_entry_compare(g_entry, t_entry)) {
1439 987757 : TaskDeferList::iterator group_it_work = group_it++;
1440 1975514 : group->DeleteFromDeferQ(*group_it_work);
1441 987757 : g_entry.RunDeferEntry();
1442 : } else {
1443 605027 : TaskDeferList::iterator entry_it_work = entry_it++;
1444 605027 : DeleteFromDeferQ(*entry_it_work);
1445 605027 : t_entry.RunDeferEntry();
1446 : }
1447 : }
1448 :
1449 : // Now, walk thru the non-empty deferq_
1450 11670564 : if (group_it != group->deferq_.end()) {
1451 1818144 : group->RunDeferQ();
1452 8034276 : } else if (entry_it != deferq_->end()) {
1453 3183374 : RunDeferQ();
1454 : }
1455 5835282 : }
1456 :
1457 7234883 : void TaskEntry::TaskExited(Task *t, TaskGroup *group) {
1458 7234883 : if (task_data_id_ != -1) {
1459 6997324 : assert(run_task_ == t);
1460 6997324 : run_task_ = NULL;
1461 6997324 : assert(run_count_ == 1);
1462 : }
1463 :
1464 7234883 : run_count_--;
1465 7234883 : stats_.total_tasks_completed_++;
1466 7234883 : stats_.last_exit_time_ = UTCTimestampUsec();
1467 7234883 : group->TaskExited(t);
1468 :
1469 7234883 : if (!group->run_count_ && !run_count_) {
1470 5835282 : RunCombinedDeferQ();
1471 1399601 : } else if (!group->run_count_) {
1472 0 : group->RunDeferQ();
1473 1399601 : } else if (!run_count_) {
1474 1185995 : RunDeferQ();
1475 : }
1476 7234883 : }
1477 :
1478 0 : void TaskEntry::ClearQueues() {
1479 0 : deferq_->clear();
1480 0 : policyq_.clear();
1481 0 : waitq_.clear();
1482 0 : }
1483 :
1484 27 : void TaskEntry::ClearTaskStats() {
1485 27 : memset(&stats_, 0, sizeof(stats_));
1486 27 : }
1487 :
1488 4 : TaskStats *TaskEntry::GetTaskStats() {
1489 4 : return &stats_;
1490 : }
1491 :
1492 29278286 : boost::optional<uint64_t> TaskEntry::GetTaskDeferEntrySeqno() const {
1493 29278286 : if(waitq_.size()) {
1494 29278286 : const Task *task = &(*waitq_.begin());
1495 29278286 : return task->seqno();
1496 : }
1497 :
1498 0 : return boost::none;
1499 : }
1500 :
1501 : ////////////////////////////////////////////////////////////////////////////
1502 : // Implementation for class Task
1503 : ////////////////////////////////////////////////////////////////////////////
1504 5819006 : Task::Task(int task_id, int task_instance) : task_code_id_(task_id),
1505 11637015 : task_data_id_(task_instance), task_impl_(), state_(INIT),
1506 5818009 : tbb_state_(TBB_INIT), seqno_(0), task_recycle_(false), task_cancel_(false),
1507 5819006 : enqueue_time_(0), schedule_time_(0), execute_delay_(0), schedule_delay_(0) {
1508 5818006 : }
1509 :
1510 57156 : Task::Task(int task_id) : task_code_id_(task_id),
1511 114312 : task_data_id_(-1), task_impl_(), state_(INIT), tbb_state_(TBB_INIT),
1512 57156 : seqno_(0), task_recycle_(false), task_cancel_(false), enqueue_time_(0),
1513 57156 : schedule_time_(0), execute_delay_(0), schedule_delay_(0) {
1514 57156 : }
1515 :
1516 :
1517 7234883 : void Task::StartTask(TaskScheduler *scheduler, TaskGroup *group) {
1518 7234883 : if (enqueue_time_ != 0) {
1519 0 : schedule_time_ = ClockMonotonicUsec();
1520 0 : if ((schedule_time_ - enqueue_time_) >
1521 0 : scheduler->schedule_delay(this)) {
1522 0 : TASK_TRACE(scheduler, this, "Schedule delay(in usec) ",
1523 : (schedule_time_ - enqueue_time_));
1524 : }
1525 : }
1526 7234883 : state(RUN);
1527 7234883 : tbb_state(TBB_ENQUEUED);
1528 7234883 : task_impl_ = group->tbb_group().defer(TaskFunctor(this));
1529 7234883 : scheduler->tbb_arena().enqueue(std::move(task_impl_));
1530 7234883 : }
1531 :
1532 48437749 : Task *Task::Running() {
1533 48437749 : TaskInfo::reference running = task_running.local();
1534 48320635 : return running;
1535 : }
1536 :
1537 0 : ostream& operator<<(ostream& out, const Task &t) {
1538 0 : out << "Task <" << t.task_code_id_ << "," << t.task_data_id_ << ":"
1539 0 : << t.seqno_ << "> ";
1540 0 : return out;
1541 : }
1542 :
1543 : ////////////////////////////////////////////////////////////////////////////
1544 : // Implementation for sandesh APIs for Task
1545 : ////////////////////////////////////////////////////////////////////////////
1546 1 : void TaskEntry::GetSandeshData(SandeshTaskEntry *resp) const {
1547 1 : resp->set_instance_id(task_data_id_);
1548 1 : resp->set_tasks_created(stats_.enqueue_count_);
1549 1 : resp->set_total_tasks_completed(stats_.total_tasks_completed_);
1550 1 : resp->set_tasks_running(run_count_);
1551 1 : resp->set_waitq_size(waitq_.size());
1552 1 : resp->set_deferq_size(deferq_->size());
1553 1 : resp->set_last_exit_time(stats_.last_exit_time_);
1554 1 : }
1555 1 : void TaskGroup::GetSandeshData(SandeshTaskGroup *resp, bool summary) const {
1556 1 : if (total_run_time_)
1557 0 : resp->set_total_run_time(duration_usecs_to_string(total_run_time_));
1558 :
1559 1 : std::vector<SandeshTaskEntry> list;
1560 1 : TaskEntry *task_entry = QueryTaskEntry(-1);
1561 1 : if (task_entry) {
1562 1 : SandeshTaskEntry entry_resp;
1563 1 : task_entry->GetSandeshData(&entry_resp);
1564 1 : list.push_back(entry_resp);
1565 1 : }
1566 1 : for (TaskEntryList::const_iterator it = task_entry_db_.begin();
1567 17 : it != task_entry_db_.end(); ++it) {
1568 16 : task_entry = *it;
1569 16 : if (task_entry) {
1570 0 : SandeshTaskEntry entry_resp;
1571 0 : task_entry->GetSandeshData(&entry_resp);
1572 0 : list.push_back(entry_resp);
1573 0 : }
1574 : }
1575 1 : resp->set_task_entry_list(list);
1576 :
1577 1 : if (summary)
1578 0 : return;
1579 :
1580 1 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
1581 1 : std::vector<SandeshTaskPolicyEntry> policy_list;
1582 1 : for (TaskGroupPolicyList::const_iterator it = policy_.begin();
1583 14 : it != policy_.end(); ++it) {
1584 13 : SandeshTaskPolicyEntry policy_entry;
1585 13 : policy_entry.set_task_name(scheduler->GetTaskName((*it)->task_code_id_));
1586 13 : policy_entry.set_tasks_running((*it)->run_count_);
1587 13 : policy_list.push_back(policy_entry);
1588 13 : }
1589 1 : resp->set_task_policy_list(policy_list);
1590 1 : }
1591 :
1592 0 : void TaskScheduler::GetSandeshData(SandeshTaskScheduler *resp, bool summary) {
1593 0 : std::scoped_lock lock(mutex_);
1594 :
1595 0 : resp->set_running(running_);
1596 0 : resp->set_use_spawn(use_spawn_);
1597 0 : resp->set_total_count(seqno_);
1598 0 : resp->set_thread_count(hw_thread_count_);
1599 :
1600 0 : std::vector<SandeshTaskGroup> list;
1601 0 : for (TaskIdMap::const_iterator it = id_map_.begin(); it != id_map_.end();
1602 0 : it++) {
1603 0 : SandeshTaskGroup resp_group;
1604 0 : TaskGroup *group = QueryTaskGroup(it->second);
1605 0 : resp_group.set_task_id(it->second);
1606 0 : resp_group.set_name(it->first);
1607 0 : if (group)
1608 0 : group->GetSandeshData(&resp_group, summary);
1609 0 : list.push_back(resp_group);
1610 0 : }
1611 0 : resp->set_task_group_list(list);
1612 0 : }
|