OpenSDN source code
task.cc
Go to the documentation of this file.
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 
26 class TaskEntry;
27 struct TaskDeferEntryCmp;
28 
29 typedef oneapi::tbb::enumerable_thread_specific<Task *> TaskInfo;
30 
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 
46 class TaskFunctor {
47 public:
48 
51  TaskFunctor(Task *t) : parent_(t) {};
52 
55  : parent_(tf.parent_) {}
56 
58  : parent_(tf.parent_) {}
59 
63  virtual ~TaskFunctor();
64 
68  void operator ()() const;
69 
70 private:
71 
73  mutable Task *parent_;
74 
76  const TaskFunctor & operator = (const TaskFunctor) = delete;
77 };
78 
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  size_t WaitQSize() const { return waitq_.size(); };
114  void AddToWaitQ(Task *t);
115  bool DeleteFromWaitQ(Task *t);
116 
119  void AddToDeferQ(TaskEntry *entry);
120 
122  void DeleteFromDeferQ(TaskEntry &entry);
123 
124  TaskEntry *ActiveEntryInPolicy();
125  bool DeferOnPolicyFail(Task *t);
126 
129  void RunTask(Task *t);
130 
132  void RunDeferQ();
133 
136  void RunCombinedDeferQ();
137  void RunWaitQ();
138  void RunDeferEntry();
139 
142  void RunDeferQForGroupEnable();
143 
144  void TaskExited(Task *t, TaskGroup *group);
146  void ClearTaskStats();
147  void ClearQueues();
148 
154  boost::optional<uint64_t> GetTaskDeferEntrySeqno() const;
155 
157  int task_code_id() const { return task_code_id_; }
158 
160  int task_data_id() const { return task_data_id_; }
161 
163  int GetRunCount() const { return run_count_; }
164 
166  void SetDisable(bool disable) { disable_ = disable; }
167  bool IsDisabled() { return disable_; }
168  void GetSandeshData(SandeshTaskEntry *resp) const;
169 
170 private:
171  friend class TaskGroup;
172  friend class TaskScheduler;
173 
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<>,
183 
187  typedef boost::intrusive::set<TaskEntry, TaskDeferListOption,
188  boost::intrusive::compare<TaskDeferEntryCmp> > TaskDeferList;
189 
192 
195 
198 
201 
204 
209  bool disable_;
210 
213 
215 };
216 
219  bool operator() (const TaskEntry &lhs, const TaskEntry &rhs) const {
220  return (lhs.GetTaskDeferEntrySeqno() <
221  rhs.GetTaskDeferEntrySeqno());
222  }
223 };
224 
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 
251  void AddToDeferQ(TaskEntry *entry);
252 
254  void AddToDisableQ(TaskEntry *entry);
255 
258  void AddEntriesToDisableQ();
259 
260  TaskEntry *GetDisableEntry() { return disable_entry_; }
261 
263  void DeleteFromDeferQ(TaskEntry &entry);
264  TaskGroup *ActiveGroupInPolicy();
265  bool DeferOnPolicyFail(TaskEntry *entry, Task *t);
266 
274  bool IsWaitQEmpty();
275 
276  int TaskRunCount() const {return run_count_;};
277 
279  void RunDeferQ();
280 
283  void RunDisableEntries();
284  void TaskExited(Task *t);
285  void PolicySet();
286  void TaskStarted() {run_count_++;};
287  void IncrementTotalRunTime(int64_t rtime) { total_run_time_ += rtime; }
288  TaskStats *GetTaskGroupStats();
290  TaskStats *GetTaskStats(int task_instance);
291  void ClearTaskGroupStats();
292  void ClearTaskStats();
293  void ClearTaskStats(int instance_id);
294  void SetDisable(bool disable) { disable_ = disable; }
295  bool IsDisabled() { return disable_; }
296  void GetSandeshData(SandeshTaskGroup *resp, bool summary) const;
297 
298  int task_id() const { return task_code_id_; }
299  size_t deferq_size() const { return deferq_.size(); }
300  size_t num_tasks() const {
301  size_t count = 0;
302  for (TaskEntryList::const_iterator it = task_entry_db_.begin();
303  it != task_entry_db_.end(); ++it) {
304  if (*it != NULL) {
305  count++;
306  }
307  }
308  return count;
309  }
310 
311  oneapi::tbb::task_group &tbb_group() {
312  return tbb_group_;
313  }
314 
315 private:
316  friend class TaskEntry;
317  friend class TaskScheduler;
318 
320  typedef std::vector<TaskGroup *> TaskGroupPolicyList;
321  typedef boost::intrusive::member_hook<TaskEntry,
322  boost::intrusive::set_member_hook<>,
324 
328  typedef boost::intrusive::set<TaskEntry, TaskDeferListOption,
329  boost::intrusive::compare<TaskDeferEntryCmp> > TaskDeferList;
330 
331  static const int kVectorGrowSize = 16;
333 
335  oneapi::tbb::task_group tbb_group_;
336 
339 
342  std::atomic<uint64_t> total_run_time_;
343 
346 
349 
352 
355 
358  uint32_t execute_delay_;
359  uint32_t schedule_delay_;
360  bool disable_;
361 
364 };
365 
367 // Implementation for class TaskImpl
369 
371  TaskInfo::reference running = task_running.local();
372  running = parent_;
373  parent_->tbb_state(Task::TBB_EXEC);
374  try {
375  uint64_t t = 0;
376  if (parent_->enqueue_time() != 0) {
377  t = ClockMonotonicUsec();
379  if ((t - parent_->enqueue_time()) >
380  scheduler->schedule_delay(parent_)) {
381  TASK_TRACE(scheduler, parent_, "TBB schedule time(in usec) ",
382  (t - parent_->enqueue_time()));
383  }
384  } else if (TaskScheduler::GetInstance()->track_run_time()) {
385  t = ClockMonotonicUsec();
386  }
387  bool is_complete = parent_->Run();
389  if (t != 0) {
390  int64_t delay = ClockMonotonicUsec() - t;
392  uint32_t execute_delay = scheduler->execute_delay(parent_);
393  if (execute_delay && delay > execute_delay) {
394  TASK_TRACE(scheduler, parent_, "Run time(in usec) ", delay);
395  }
396  if (scheduler->track_run_time()) {
397  TaskGroup *group =
398  scheduler->QueryTaskGroup(parent_->task_code_id());
399  group->IncrementTotalRunTime(delay);
400  }
401  }
402  running = NULL;
403  if (is_complete == true) {
404  parent_->set_task_complete();
405  } else {
406  parent_->set_task_recycle();
407  }
408  scheduler->OnTaskExit(parent_);
409  } catch (std::exception &e) {
410 
411  // Store exception information statically, to easily read exception
412  // information from the core.
413  static std::string what = e.what();
414 
415  LOG(ERROR, "!!!! ERROR !!!! Task caught fatal exception: " << what
416  << " TaskImpl: " << this);
417  assert(0);
418  } catch (...) {
419  LOG(ERROR, "!!!! ERROR !!!! Task caught fatal unknown exception"
420  << " TaskImpl: " << this);
421  assert(0);
422  }
423 }
424 
426 }
427 
428 int TaskScheduler::GetThreadCount(int thread_count) {
429  static bool init_;
430  static int num_cores_;
431 
432  if (init_) {
433  return num_cores_ * ThreadAmpFactor_;
434  }
435 
436  char *num_cores_str = getenv("TBB_THREAD_COUNT");
437  if (!num_cores_str) {
438  if (thread_count == 0) {
439  num_cores_ = oneapi::tbb::info::default_concurrency();
440  } else {
441  num_cores_ = thread_count;
442  }
443  } else {
444  num_cores_ = strtol(num_cores_str, NULL, 0);
445  }
446 
447  init_ = true;
448  return num_cores_ * ThreadAmpFactor_;
449 }
450 
452  return oneapi::tbb::this_task_arena::max_concurrency();
453 }
454 
456  if (getenv("TBB_USE_SPAWN"))
457  return true;
458 
459  return false;
460 }
461 
463 // Implementation for class TaskScheduler
465 
469  oneapi::tbb::global_control::max_allowed_parallelism,
470  GetThreadCount(task_count) + 1),
471  task_scheduler_(GetThreadCount(task_count) + 1),
472  running_(true), seqno_(0), id_max_(0), log_fn_(), track_run_time_(false),
474  enqueue_count_(0), done_count_(0), cancel_count_(0), evm_(NULL),
475  tbb_awake_task_(NULL), task_monitor_(NULL) {
476  hw_thread_count_ = GetThreadCount(task_count);
478  stop_entry_ = new TaskEntry(-1);
479 }
480 
482  TaskGroup *group;
483 
484  for (TaskGroupDb::iterator iter = task_group_db_.begin();
485  iter != task_group_db_.end(); ++iter) {
486  if ((group = *iter) == NULL) {
487  continue;
488  }
489  *iter = NULL;
490  delete group;
491  }
492 
493  for (TaskIdMap::iterator loc = id_map_.begin(); loc != id_map_.end();
494  id_map_.erase(loc++)) {
495  }
496 
497  delete stop_entry_;
498  stop_entry_ = NULL;
499  task_group_db_.clear();
500 
501  return;
502 }
503 
504 void TaskScheduler::Initialize(uint32_t thread_count, EventManager *evm) {
505  assert(singleton_.get() == NULL);
506  singleton_.reset(new TaskScheduler((int)thread_count));
507 
508  if (evm) {
509  singleton_.get()->evm_ = evm;
510  singleton_.get()->tbb_awake_task_ = new TaskTbbKeepAwake();
511  assert(singleton_.get()->tbb_awake_task_);
512 
513  singleton_.get()->tbb_awake_task_->StartTbbKeepAwakeTask(
514  singleton_.get(), evm,
515  "TaskScheduler::TbbKeepAwake");
516  }
517 }
518 
520  assert(evm);
521  evm_ = evm;
522  if (tbb_awake_task_ == NULL) {
524  assert(tbb_awake_task_);
525 
527  "TaskScheduler::TbbKeepAwake");
528  }
529 }
530 
532  if (tbb_awake_task_) {
534  }
535 }
536 
538  uint64_t tbb_keepawake_time_msec,
539  uint64_t inactivity_time_msec,
540  uint64_t poll_interval_msec) {
541  if (task_monitor_ != NULL)
542  return;
543 
544  task_monitor_ = new TaskMonitor(this, tbb_keepawake_time_msec,
545  inactivity_time_msec, poll_interval_msec);
547 }
548 
549 void TaskScheduler::Log(const char *file_name, uint32_t line_no,
550  const Task *task, const char *description,
551  uint64_t delay) {
552  if (log_fn_.empty() == false) {
553  log_fn_(file_name, line_no, task, description, delay);
554  }
555 }
556 
558  log_fn_ = fn;
559 }
560 
562  if (task->schedule_delay() > schedule_delay_)
563  return task->schedule_delay();
564  return schedule_delay_;
565 }
566 
568  if (task->execute_delay() > execute_delay_)
569  return task->execute_delay();
570  return execute_delay_;
571 }
572 
574  if (singleton_.get() == NULL) {
575  singleton_.reset(new TaskScheduler());
576  }
577  return singleton_.get();
578 }
579 
581  assert(task_id >= 0);
582  int size = task_group_db_.size();
583  if (size <= task_id) {
585  }
586 
587  TaskGroup *group = task_group_db_[task_id];
588  if (group == NULL) {
589  group = new TaskGroup(task_id);
590  task_group_db_[task_id] = group;
591  }
592 
593  return group;
594 }
595 
597  return task_group_db_[task_id];
598 }
599 
600 bool TaskScheduler::IsTaskGroupEmpty(int task_id) const {
601  CHECK_CONCURRENCY("bgp::Config");
602  std::scoped_lock lock(mutex_);
603  TaskGroup *group = task_group_db_[task_id];
604  assert(group);
605  assert(group->TaskRunCount() == 0);
606  return group->IsWaitQEmpty();
607 }
608 
609 TaskEntry *TaskScheduler::GetTaskEntry(int task_id, int task_instance) {
610  TaskGroup *group = GetTaskGroup(task_id);
611  return group->GetTaskEntry(task_instance);
612 }
613 
614 TaskEntry *TaskScheduler::QueryTaskEntry(int task_id, int task_instance) {
615  TaskGroup *group = QueryTaskGroup(task_id);
616  if (group == NULL)
617  return NULL;
618  return group->QueryTaskEntry(task_instance);
619 }
620 
622  uint32_t schedule) {
623  execute_delay_ = execute;
624  schedule_delay_ = schedule;
626 }
627 
628 void TaskScheduler::SetLatencyThreshold(const std::string &name,
629  uint32_t execute, uint32_t schedule) {
630  int task_id = GetTaskId(name);
631  TaskGroup *group = GetTaskGroup(task_id);
632  group->execute_delay_ = execute;
633  group->schedule_delay_ = schedule;
634 }
635 
636 void TaskScheduler::SetPolicy(int task_id, TaskPolicy &policy) {
637  std::scoped_lock lock(mutex_);
638 
639  TaskGroup *group = GetTaskGroup(task_id);
640  TaskEntry *group_entry = group->GetTaskEntry(-1);
641  group->PolicySet();
642 
643  for (const auto& pol_item: policy) {
644  if (pol_item.match_data_id == -1) {
645  TaskGroup *policy_group = GetTaskGroup(pol_item.match_code_id);
646  group->AddPolicy(policy_group);
647  policy_group->AddPolicy(group);
648  } else {
649  TaskEntry *entry = GetTaskEntry(task_id, pol_item.match_data_id);
650  TaskEntry *policy_entry = GetTaskEntry(pol_item.match_code_id,
651  pol_item.match_data_id);
652  entry->AddPolicy(policy_entry);
653  policy_entry->AddPolicy(entry);
654 
655  group_entry->AddPolicy(policy_entry);
656  policy_entry->AddPolicy(group_entry);
657  }
658  }
659 }
660 
662  std::scoped_lock lock(mutex_);
663  EnqueueUnLocked(t);
664 }
665 
667  if (measure_delay_) {
669  }
670  // Ensure that task is enqueued only once.
671  assert(t->seqno() == 0);
672  enqueue_count_++;
673  t->seqno(++seqno_);
674  TaskGroup *group = GetTaskGroup(t->task_code_id());
675  t->schedule_delay_ = group->schedule_delay_;
676  t->execute_delay_ = group->execute_delay_;
677  group->stats_.enqueue_count_++;
678 
679  TaskEntry *entry = GetTaskEntry(t->task_code_id(), t->task_data_id());
680  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  if (group->IsDisabled() || entry->IsDisabled()) {
685  entry->AddToWaitQ(t);
686  if (group->IsDisabled()) {
687  group->AddToDisableQ(entry);
688  }
689  return;
690  }
691 
692  // Add task to waitq_ if its already populated
693  if (entry->WaitQSize() != 0) {
694  entry->AddToWaitQ(t);
695  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  if (!running_) {
701  entry->AddToWaitQ(t);
702  stop_entry_->AddToDeferQ(entry);
703  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  if (group->DeferOnPolicyFail(entry, t)) {
710  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  if (entry->DeferOnPolicyFail(t)) {
717  return;
718  }
719 
720 
721  entry->RunTask(t);
722 
723  return;
724 }
725 
727  std::scoped_lock lock(mutex_);
728 
729  // If the task is in RUN state, mark the task for cancellation and return.
730  if (t->state_ == Task::RUN) {
731  t->task_cancel_ = true;
732  } else if (t->state_ == Task::WAIT) {
733  TaskEntry *entry = QueryTaskEntry(t->task_code_id(), t->task_data_id());
734  TaskGroup *group = QueryTaskGroup(t->task_code_id());
735  assert(entry->WaitQSize());
736  // Get the first entry in the waitq_
737  Task *first_wait_task = &(*entry->waitq_.begin());
738  TaskEntry *disable_entry = group->GetDisableEntry();
739  assert(entry->DeleteFromWaitQ(t) == true);
740  // If the waitq_ is empty, then remove the TaskEntry from the deferq.
741  if (!entry->WaitQSize()) {
742  if (entry->deferq_task_group_) {
743  assert(entry->deferq_task_entry_ == NULL);
744  entry->deferq_task_group_->DeleteFromDeferQ(*entry);
745  } else if (entry->deferq_task_entry_) {
746  entry->deferq_task_entry_->DeleteFromDeferQ(*entry);
747  } else if (group->IsDisabled()) {
748  // Remove TaskEntry from deferq of disable_entry
749  disable_entry->DeleteFromDeferQ(*entry);
750  } else {
751  if (!entry->IsDisabled()) {
752  assert(0);
753  }
754  }
755  } 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  TaskGroup *deferq_tgroup = entry->deferq_task_group_;
762  TaskEntry *deferq_tentry = entry->deferq_task_entry_;
763  if (deferq_tgroup) {
764  assert(deferq_tentry == NULL);
765  deferq_tgroup->DeleteFromDeferQ(*entry);
766  deferq_tgroup->AddToDeferQ(entry);
767  } else if (deferq_tentry) {
768  deferq_tentry->DeleteFromDeferQ(*entry);
769  deferq_tentry->AddToDeferQ(entry);
770  } else if (group->IsDisabled()) {
771  // Remove TaskEntry from deferq of disable_entry and add back
772  disable_entry->DeleteFromDeferQ(*entry);
773  disable_entry->AddToDeferQ(entry);
774  } else {
775  if (!entry->IsDisabled()) {
776  assert(0);
777  }
778  }
779  }
780  delete t;
781  cancel_count_++;
782  return CANCELLED;
783  } else {
784  return FAILED;
785  }
786  return QUEUED;
787 }
788 
790  std::scoped_lock lock(mutex_);
791  done_count_++;
792 
794  TaskEntry *entry = QueryTaskEntry(t->task_code_id(), t->task_data_id());
795  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  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  if (t->task_cancel_ == true) {
805  t->OnTaskCancel();
806  }
807  delete t;
808  return;
809  }
810 
811  // Task is being recycled, reset the state, seq_no and TBB task handle
812  t->task_impl_ = oneapi::tbb::task_handle{};
813  t->seqno(0);
814  t->state(Task::INIT);
816  EnqueueUnLocked(t);
817 }
818 
820  std::scoped_lock lock(mutex_);
821 
822  running_ = false;
823 }
824 
826  std::scoped_lock lock(mutex_);
827 
828  running_ = true;
829 
830  // Run all tasks that may be suspended
832  return;
833 }
834 
836  for (TaskGroupDb::iterator iter = task_group_db_.begin();
837  iter != task_group_db_.end(); ++iter) {
838  TaskGroup *group = *iter;
839  if (group == NULL) {
840  continue;
841  }
842 
843  cout << "id: " << group->task_id() <<
844  " run: " << group->TaskRunCount() << endl;
845  cout << "deferq: " << group->deferq_size() <<
846  " task count: " << group->num_tasks() << endl;
847  }
848 }
849 
850 bool TaskScheduler::IsEmpty(bool running_only) {
851  TaskGroup *group;
852 
853  std::scoped_lock lock(mutex_);
854 
855  for (TaskGroupDb::iterator it = task_group_db_.begin();
856  it != task_group_db_.end(); ++it) {
857  if ((group = *it) == NULL) {
858  continue;
859  }
860  if (group->TaskRunCount()) {
861  return false;
862  }
863  if (group->IsDisabled()) {
864  continue;
865  }
866  if ((false == running_only) && (false == group->IsWaitQEmpty())) {
867  return false;
868  }
869  }
870 
871  return true;
872 }
873 std::string TaskScheduler::GetTaskName(int task_id) const {
874  for (TaskIdMap::const_iterator it = id_map_.begin(); it != id_map_.end();
875  it++) {
876  if (task_id == it->second)
877  return it->first;
878  }
879 
880  return "ERROR";
881 }
882 
883 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  std::shared_lock<std::shared_mutex> lock(id_map_mutex_);
889  TaskIdMap::iterator loc = id_map_.find(name);
890  if (loc != id_map_.end()) {
891  return loc->second;
892  }
893  }
894 
895  // Grab read-write lock to allocate a new task id and insert into the map.
896  std::unique_lock<std::shared_mutex> lock(id_map_mutex_);
897  int tid = ++id_max_;
898  id_map_.insert(make_pair(name, tid));
899  return tid;
900 }
901 
903  TaskGroup *group = GetTaskGroup(task_id);
904  if (group == NULL)
905  return;
906 
907  group->ClearTaskGroupStats();
908 }
909 
910 void TaskScheduler::ClearTaskStats(int task_id) {
911  TaskGroup *group = GetTaskGroup(task_id);
912  if (group == NULL)
913  return;
914 
915  group->ClearTaskStats();
916 }
917 
918 void TaskScheduler::ClearTaskStats(int task_id, int instance_id) {
919  TaskGroup *group = GetTaskGroup(task_id);
920  if (group == NULL)
921  return;
922 
923  group->ClearTaskStats(instance_id);
924 }
925 
927  TaskGroup *group = GetTaskGroup(task_id);
928  if (group == NULL)
929  return NULL;
930 
931  return group->GetTaskGroupStats();
932 }
933 
935  TaskGroup *group = GetTaskGroup(task_id);
936  if (group == NULL)
937  return NULL;
938 
939  return group->GetTaskStats();
940 }
941 
942 TaskStats *TaskScheduler::GetTaskStats(int task_id, int instance_id) {
943  TaskGroup *group = GetTaskGroup(task_id);
944  if (group == NULL)
945  return NULL;
946 
947  return group->GetTaskStats(instance_id);
948 }
949 
951  if (task_monitor_) {
953  delete task_monitor_;
954  task_monitor_ = NULL;
955  }
956 
957  for (int i = 0; i < 10000; i++) {
958  if (IsEmpty()) break;
959  usleep(1000);
960  }
961  assert(IsEmpty());
962  if (tbb_awake_task_) {
964  delete tbb_awake_task_;
965  tbb_awake_task_ = NULL;
966  }
967  evm_ = NULL;
968  singleton_->task_scheduler_.terminate();
969  oneapi::tbb::task_scheduler_handle handle =
970  oneapi::tbb::task_scheduler_handle{oneapi::tbb::attach{}};
971  oneapi::tbb::finalize(handle);
972  singleton_.reset(NULL);
973 }
974 
976  TaskInfo::reference running = task_running.local();
977  running = unit_test;
978 }
979 
981  TaskInfo::reference running = task_running.local();
982  running = NULL;
983 }
984 
986  ThreadAmpFactor_ = n;
987 }
988 
990  TaskGroup *group = GetTaskGroup(task_id);
991  if (!group->IsDisabled()) {
992  // Add TaskEntries(that contain enqueued tasks) which are already
993  // disabled to disable_ entry maintained at TaskGroup.
994  group->SetDisable(true);
995  group->AddEntriesToDisableQ();
996  }
997 }
998 
1000  TaskGroup *group = GetTaskGroup(task_id);
1001  group->SetDisable(false);
1002  // Run tasks that maybe suspended
1003  group->RunDisableEntries();
1004 }
1005 
1006 void TaskScheduler::DisableTaskEntry(int task_id, int instance_id) {
1007  TaskEntry *entry = GetTaskEntry(task_id, instance_id);
1008  entry->SetDisable(true);
1009 }
1010 
1011 void TaskScheduler::EnableTaskEntry(int task_id, int instance_id) {
1012  TaskEntry *entry = GetTaskEntry(task_id, instance_id);
1013  entry->SetDisable(false);
1014  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  if (group->IsDisabled()) {
1018  return;
1019  }
1020  // Run task instances that maybe suspended
1021  if (entry->WaitQSize() != 0) {
1022  entry->RunDeferEntry();
1023  }
1024 }
1025 
1027 // Implementation for class TaskGroup
1029 
1030 TaskGroup::TaskGroup(int task_id) : task_code_id_(task_id), tbb_group_(),
1031  policy_set_(false), run_count_(0), execute_delay_(0), schedule_delay_(0),
1032  disable_(false) {
1033  total_run_time_ = 0;
1035  task_entry_ = new TaskEntry(task_id);
1036  memset(&stats_, 0, sizeof(stats_));
1038 }
1039 
1041  policy_.clear();
1042  deferq_.clear();
1043 
1044  delete task_entry_;
1045  task_entry_ = NULL;
1046 
1047  for (size_t i = 0; i < task_entry_db_.size(); i++) {
1048  if (task_entry_db_[i] != NULL) {
1049  delete task_entry_db_[i];
1050  task_entry_db_[i] = NULL;
1051  }
1052  }
1053 
1054  delete disable_entry_;
1055  disable_entry_ = NULL;
1056  task_entry_db_.clear();
1057 }
1058 
1059 TaskEntry *TaskGroup::GetTaskEntry(int task_instance) {
1060  if (task_instance == -1)
1061  return task_entry_;
1062 
1063  int size = task_entry_db_.size();
1064  if (size <= task_instance) {
1065  task_entry_db_.resize(task_instance + TaskGroup::kVectorGrowSize);
1066  }
1067 
1068  TaskEntry *entry = task_entry_db_.at(task_instance);
1069  if (entry == NULL) {
1070  entry = new TaskEntry(task_code_id_, task_instance);
1071  task_entry_db_[task_instance] = entry;
1072  }
1073 
1074  return entry;
1075 }
1076 
1077 TaskEntry *TaskGroup::QueryTaskEntry(int task_instance) const {
1078  if (task_instance == -1) {
1079  return task_entry_;
1080  }
1081 
1082  if (task_instance >= (int)task_entry_db_.size())
1083  return NULL;
1084 
1085  return task_entry_db_[task_instance];
1086 }
1087 
1089  policy_.push_back(group);
1090 }
1091 
1093  for (TaskGroupPolicyList::iterator it = policy_.begin();
1094  it != policy_.end(); ++it) {
1095  if ((*it)->run_count_ != 0) {
1096  return (*it);
1097  }
1098  }
1099  return NULL;
1100 }
1101 
1103  TaskGroup *group;
1104  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  if (0 == entry->WaitQSize()) {
1110  entry->AddToWaitQ(task);
1111  }
1112  group->AddToDeferQ(entry);
1113  return true;
1114  }
1115  return false;
1116 }
1117 
1119  stats_.defer_count_++;
1120  deferq_.insert(*entry);
1121  assert(entry->deferq_task_group_ == NULL);
1122  entry->deferq_task_group_ = this;
1123 }
1124 
1126  assert(this == entry.deferq_task_group_);
1127  deferq_.erase(deferq_.iterator_to(entry));
1128  entry.deferq_task_group_ = NULL;
1129 }
1130 
1132  disable_entry_->AddToDeferQ(entry);
1133 }
1134 
1136  assert(policy_set_ == false);
1137  policy_set_ = true;
1138 }
1139 
1141  TaskDeferList::iterator it;
1142 
1143  it = deferq_.begin();
1144  while (it != deferq_.end()) {
1145  TaskEntry &entry = *it;
1146  TaskDeferList::iterator it_work = it++;
1147  DeleteFromDeferQ(*it_work);
1148  entry.RunDeferEntry();
1149  }
1150 
1151  return;
1152 }
1153 
1154 inline void TaskGroup::TaskExited(Task *t) {
1155  run_count_--;
1157 }
1158 
1161 }
1162 
1164  TaskEntry *entry;
1165  if (task_entry_->WaitQSize()) {
1167  }
1168 
1169  // Walk thru the task_entry_db_ and add if waitq is non-empty
1170  for (TaskEntryList::iterator it = task_entry_db_.begin();
1171  it != task_entry_db_.end(); ++it) {
1172  if ((entry = *it) == NULL) {
1173  continue;
1174  }
1175  if (entry->WaitQSize()) {
1176  AddToDisableQ(entry);
1177  }
1178  }
1179 }
1180 
1182  TaskEntry *entry;
1183 
1184  // Check the waitq_ of the instance -1
1185  if (task_entry_->WaitQSize()) {
1186  return false;
1187  }
1188 
1189  // Walk thru the task_entry_db_ until waitq_ of any of the task is non-zero
1190  for (TaskEntryList::iterator it = task_entry_db_.begin();
1191  it != task_entry_db_.end(); ++it) {
1192  if ((entry = *it) == NULL) {
1193  continue;
1194  }
1195  if (entry->IsDisabled()) {
1196  continue;
1197  }
1198  if (entry->WaitQSize()) {
1199  return false;
1200  }
1201  }
1202 
1203  // Well, no task has been enqueued in this task group
1204  return true;
1205 }
1206 
1208  memset(&stats_, 0, sizeof(stats_));
1209 }
1210 
1213 }
1214 
1215 void TaskGroup::ClearTaskStats(int task_instance) {
1216  TaskEntry *entry = QueryTaskEntry(task_instance);
1217  if (entry != NULL)
1218  entry->ClearTaskStats();
1219 }
1220 
1222  return &stats_;
1223 }
1224 
1226  return task_entry_->GetTaskStats();
1227 }
1228 
1229 TaskStats *TaskGroup::GetTaskStats(int task_instance) {
1230  TaskEntry *entry = QueryTaskEntry(task_instance);
1231  return entry->GetTaskStats();
1232 }
1233 
1235 // Implementation for class TaskEntry
1237 
1238 TaskEntry::TaskEntry(int task_id, int task_instance) : task_code_id_(task_id),
1239  task_data_id_(task_instance), run_count_(0), run_task_(NULL),
1240  waitq_(), deferq_task_entry_(NULL), deferq_task_group_(NULL),
1241  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  if (task_instance != -1) {
1245  policyq_.push_back(this);
1246  }
1247  memset(&stats_, 0, sizeof(stats_));
1248  // allocate memory for deferq
1249  deferq_ = new TaskDeferList;
1250 }
1251 
1252 TaskEntry::TaskEntry(int task_id) : task_code_id_(task_id),
1253  task_data_id_(-1), run_count_(0), run_task_(NULL),
1254  deferq_task_entry_(NULL), deferq_task_group_(NULL), disable_(false) {
1255  memset(&stats_, 0, sizeof(stats_));
1256  // allocate memory for deferq
1257  deferq_ = new TaskDeferList;
1258 }
1259 
1261  policyq_.clear();
1262 
1263  assert(0 == deferq_->size());
1264  delete deferq_;
1265 }
1266 
1268  policyq_.push_back(entry);
1269 }
1270 
1272  for (TaskEntryList::iterator it = policyq_.begin(); it != policyq_.end();
1273  ++it) {
1274  if ((*it)->run_count_ != 0) {
1275  return (*it);
1276  }
1277  }
1278 
1279  return NULL;
1280 }
1281 
1283  TaskEntry *policy_entry;
1284 
1285  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  if (0 == WaitQSize()) {
1291  AddToWaitQ(task);
1292  }
1293  policy_entry->AddToDeferQ(this);
1294  return true;
1295  }
1296  return false;
1297 }
1298 
1300  t->state(Task::WAIT);
1301  stats_.wait_count_++;
1302  waitq_.push_back(*t);
1303 
1305  TaskGroup *group = scheduler->GetTaskGroup(task_code_id_);
1306  group->stats_.wait_count_++;
1307 }
1308 
1310  TaskWaitQ::iterator it = waitq_.iterator_to(*t);
1311  waitq_.erase(it);
1312  return true;
1313 }
1314 
1316  stats_.defer_count_++;
1317  deferq_->insert(*entry);
1318  assert(entry->deferq_task_entry_ == NULL);
1319  entry->deferq_task_entry_ = this;
1320 }
1321 
1323  assert(this == entry.deferq_task_entry_);
1324  deferq_->erase(deferq_->iterator_to(entry));
1325  entry.deferq_task_entry_ = NULL;
1326 }
1327 
1329  stats_.run_count_++;
1330  if (t->task_data_id() != -1) {
1331  assert(run_task_ == NULL);
1332  assert (run_count_ == 0);
1333  run_task_ = t;
1334  }
1335 
1336  run_count_++;
1338  TaskGroup *group = scheduler->QueryTaskGroup(t->task_code_id());
1339  group->TaskStarted();
1340 
1341  t->StartTask(scheduler, group);
1342 }
1343 
1345  if (waitq_.size() == 0)
1346  return;
1347 
1348  TaskWaitQ::iterator it = waitq_.begin();
1349 
1350  if (task_data_id_ != -1) {
1351  Task *t = &(*it);
1352  DeleteFromWaitQ(t);
1353  RunTask(t);
1354  // If there are more tasks in waitq_, put them in deferq_
1355  if (waitq_.size() != 0) {
1356  AddToDeferQ(this);
1357  }
1358  } else {
1359  // Run all instances in waitq_
1360  while (it != waitq_.end()) {
1361  Task *t = &(*it);
1362  DeleteFromWaitQ(t);
1363  RunTask(t);
1364  if (waitq_.size() == 0)
1365  break;
1366  it = waitq_.begin();
1367  }
1368  }
1369 }
1370 
1373  TaskGroup *group = scheduler->GetTaskGroup(task_code_id_);
1374 
1375  // Sanity check
1376  assert(waitq_.size());
1377  Task *task = &(*waitq_.begin());
1378 
1379  // Check Task group policies
1380  if (group->DeferOnPolicyFail(this, task)) {
1381  return;
1382  }
1383 
1384  // Check Task entry policies
1385  if (DeferOnPolicyFail(task)) {
1386  return;
1387  }
1388 
1389  RunWaitQ();
1390  return;
1391 }
1392 
1394  TaskDeferList::iterator it;
1395 
1396  it = deferq_->begin();
1397  while (it != deferq_->end()) {
1398  TaskEntry &entry = *it;
1399  TaskDeferList::iterator it_work = it++;
1400  DeleteFromDeferQ(*it_work);
1401  entry.RunDeferEntry();
1402  }
1403 
1404  return;
1405 }
1406 
1408  TaskDeferList::iterator it;
1409 
1410  it = deferq_->begin();
1411  while (it != deferq_->end()) {
1412  TaskEntry &entry = *it;
1413  TaskDeferList::iterator it_work = it++;
1414  DeleteFromDeferQ(*it_work);
1415  if (!entry.IsDisabled()) {
1416  entry.RunDeferEntry();
1417  }
1418  }
1419 
1420  return;
1421 }
1422 
1425  TaskGroup *group = scheduler->QueryTaskGroup(task_code_id_);
1426  TaskDeferEntryCmp defer_entry_compare;
1427 
1428  TaskDeferList::iterator group_it = group->deferq_.begin();
1429  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  while ((group_it != group->deferq_.end()) &&
1434  (entry_it != deferq_->end())) {
1435  TaskEntry &g_entry = *group_it;
1436  TaskEntry &t_entry = *entry_it;
1437 
1438  if (defer_entry_compare(g_entry, t_entry)) {
1439  TaskDeferList::iterator group_it_work = group_it++;
1440  group->DeleteFromDeferQ(*group_it_work);
1441  g_entry.RunDeferEntry();
1442  } else {
1443  TaskDeferList::iterator entry_it_work = entry_it++;
1444  DeleteFromDeferQ(*entry_it_work);
1445  t_entry.RunDeferEntry();
1446  }
1447  }
1448 
1449  // Now, walk thru the non-empty deferq_
1450  if (group_it != group->deferq_.end()) {
1451  group->RunDeferQ();
1452  } else if (entry_it != deferq_->end()) {
1453  RunDeferQ();
1454  }
1455 }
1456 
1458  if (task_data_id_ != -1) {
1459  assert(run_task_ == t);
1460  run_task_ = NULL;
1461  assert(run_count_ == 1);
1462  }
1463 
1464  run_count_--;
1467  group->TaskExited(t);
1468 
1469  if (!group->run_count_ && !run_count_) {
1471  } else if (!group->run_count_) {
1472  group->RunDeferQ();
1473  } else if (!run_count_) {
1474  RunDeferQ();
1475  }
1476 }
1477 
1479  deferq_->clear();
1480  policyq_.clear();
1481  waitq_.clear();
1482 }
1483 
1485  memset(&stats_, 0, sizeof(stats_));
1486 }
1487 
1489  return &stats_;
1490 }
1491 
1492 boost::optional<uint64_t> TaskEntry::GetTaskDeferEntrySeqno() const {
1493  if(waitq_.size()) {
1494  const Task *task = &(*waitq_.begin());
1495  return task->seqno();
1496  }
1497 
1498  return boost::none;
1499 }
1500 
1502 // Implementation for class Task
1504 Task::Task(int task_id, int task_instance) : task_code_id_(task_id),
1505  task_data_id_(task_instance), task_impl_(), state_(INIT),
1506  tbb_state_(TBB_INIT), seqno_(0), task_recycle_(false), task_cancel_(false),
1507  enqueue_time_(0), schedule_time_(0), execute_delay_(0), schedule_delay_(0) {
1508 }
1509 
1510 Task::Task(int task_id) : task_code_id_(task_id),
1511  task_data_id_(-1), task_impl_(), state_(INIT), tbb_state_(TBB_INIT),
1512  seqno_(0), task_recycle_(false), task_cancel_(false), enqueue_time_(0),
1513  schedule_time_(0), execute_delay_(0), schedule_delay_(0) {
1514 }
1515 
1516 
1517 void Task::StartTask(TaskScheduler *scheduler, TaskGroup *group) {
1518  if (enqueue_time_ != 0) {
1520  if ((schedule_time_ - enqueue_time_) >
1521  scheduler->schedule_delay(this)) {
1522  TASK_TRACE(scheduler, this, "Schedule delay(in usec) ",
1524  }
1525  }
1526  state(RUN);
1528  task_impl_ = group->tbb_group().defer(TaskFunctor(this));
1529  scheduler->tbb_arena().enqueue(std::move(task_impl_));
1530 }
1531 
1533  TaskInfo::reference running = task_running.local();
1534  return running;
1535 }
1536 
1537 ostream& operator<<(ostream& out, const Task &t) {
1538  out << "Task <" << t.task_code_id_ << "," << t.task_data_id_ << ":"
1539  << t.seqno_ << "> ";
1540  return out;
1541 }
1542 
1544 // Implementation for sandesh APIs for Task
1546 void TaskEntry::GetSandeshData(SandeshTaskEntry *resp) const {
1547  resp->set_instance_id(task_data_id_);
1548  resp->set_tasks_created(stats_.enqueue_count_);
1549  resp->set_total_tasks_completed(stats_.total_tasks_completed_);
1550  resp->set_tasks_running(run_count_);
1551  resp->set_waitq_size(waitq_.size());
1552  resp->set_deferq_size(deferq_->size());
1553  resp->set_last_exit_time(stats_.last_exit_time_);
1554 }
1555 void TaskGroup::GetSandeshData(SandeshTaskGroup *resp, bool summary) const {
1556  if (total_run_time_)
1557  resp->set_total_run_time(duration_usecs_to_string(total_run_time_));
1558 
1559  std::vector<SandeshTaskEntry> list;
1560  TaskEntry *task_entry = QueryTaskEntry(-1);
1561  if (task_entry) {
1562  SandeshTaskEntry entry_resp;
1563  task_entry->GetSandeshData(&entry_resp);
1564  list.push_back(entry_resp);
1565  }
1566  for (TaskEntryList::const_iterator it = task_entry_db_.begin();
1567  it != task_entry_db_.end(); ++it) {
1568  task_entry = *it;
1569  if (task_entry) {
1570  SandeshTaskEntry entry_resp;
1571  task_entry->GetSandeshData(&entry_resp);
1572  list.push_back(entry_resp);
1573  }
1574  }
1575  resp->set_task_entry_list(list);
1576 
1577  if (summary)
1578  return;
1579 
1581  std::vector<SandeshTaskPolicyEntry> policy_list;
1582  for (TaskGroupPolicyList::const_iterator it = policy_.begin();
1583  it != policy_.end(); ++it) {
1584  SandeshTaskPolicyEntry policy_entry;
1585  policy_entry.set_task_name(scheduler->GetTaskName((*it)->task_code_id_));
1586  policy_entry.set_tasks_running((*it)->run_count_);
1587  policy_list.push_back(policy_entry);
1588  }
1589  resp->set_task_policy_list(policy_list);
1590 }
1591 
1592 void TaskScheduler::GetSandeshData(SandeshTaskScheduler *resp, bool summary) {
1593  std::scoped_lock lock(mutex_);
1594 
1595  resp->set_running(running_);
1596  resp->set_use_spawn(use_spawn_);
1597  resp->set_total_count(seqno_);
1598  resp->set_thread_count(hw_thread_count_);
1599 
1600  std::vector<SandeshTaskGroup> list;
1601  for (TaskIdMap::const_iterator it = id_map_.begin(); it != id_map_.end();
1602  it++) {
1603  SandeshTaskGroup resp_group;
1604  TaskGroup *group = QueryTaskGroup(it->second);
1605  resp_group.set_task_id(it->second);
1606  resp_group.set_name(it->first);
1607  if (group)
1608  group->GetSandeshData(&resp_group, summary);
1609  list.push_back(resp_group);
1610  }
1611  resp->set_task_group_list(list);
1612 }
static void GetTaskStats(TaskProfileStats *stats, int index, ProfileData *data)
A class maintaning information for every <task, instance>
Definition: task.cc:106
boost::optional< uint64_t > GetTaskDeferEntrySeqno() const
Addition/deletion of TaskEntry in the deferq_ is based on the seqno. seqno of the first Task in the w...
Definition: task.cc:1492
bool DeleteFromWaitQ(Task *t)
Definition: task.cc:1309
TaskEntryList policyq_
Policy rules for a task.
Definition: task.cc:203
void SetDisable(bool disable)
Disables this task entry.
Definition: task.cc:166
bool IsDisabled()
Definition: task.cc:167
void RunDeferQForGroupEnable()
Starts executing tasks from deferq_ of TaskEntries which are enabled.
Definition: task.cc:1407
int task_code_id() const
Returns the code ID of this task entry.
Definition: task.cc:157
int task_data_id_
Definition: task.cc:191
void RunDeferQ()
Starts executing tasks from deferq_ of a TaskEntry.
Definition: task.cc:1393
void AddPolicy(TaskEntry *entry)
Definition: task.cc:1267
TaskWaitQ waitq_
Tasks waiting to run on some condition.
Definition: task.cc:200
TaskEntry * ActiveEntryInPolicy()
Definition: task.cc:1271
TaskDeferList * deferq_
Tasks deferred for this to exit.
Definition: task.cc:206
int task_data_id() const
Returns the data ID of this task entry.
Definition: task.cc:160
TaskStats * GetTaskStats()
Definition: task.cc:1488
DISALLOW_COPY_AND_ASSIGN(TaskEntry)
void AddToWaitQ(Task *t)
Definition: task.cc:1299
boost::intrusive::list< Task, WaitQHook > TaskWaitQ
Definition: task.cc:177
void GetSandeshData(SandeshTaskEntry *resp) const
Definition: task.cc:1546
void ClearQueues()
Definition: task.cc:1478
Task * run_task_
Task currently running.
Definition: task.cc:197
void TaskExited(Task *t, TaskGroup *group)
Definition: task.cc:1457
TaskGroup * deferq_task_group_
Definition: task.cc:208
int run_count_
No. of tasks running.
Definition: task.cc:194
bool DeferOnPolicyFail(Task *t)
Definition: task.cc:1282
TaskStats stats_
Cummulative Maintenance stats.
Definition: task.cc:212
void RunDeferEntry()
Definition: task.cc:1371
boost::intrusive::member_hook< TaskEntry, boost::intrusive::set_member_hook<>, &TaskEntry::task_defer_node > TaskDeferListOption
Definition: task.cc:182
void ClearTaskStats()
Definition: task.cc:1484
bool disable_
Definition: task.cc:209
TaskEntry(int task_id)
Definition: task.cc:1252
boost::intrusive::member_hook< Task, boost::intrusive::list_member_hook<>, &Task::waitq_hook_ > WaitQHook
List of Task's in waitq_.
Definition: task.cc:176
void DeleteFromDeferQ(TaskEntry &entry)
Deletes a task from deferq_.
Definition: task.cc:1322
void RunWaitQ()
Definition: task.cc:1344
boost::intrusive::set< TaskEntry, TaskDeferListOption, boost::intrusive::compare< TaskDeferEntryCmp > > TaskDeferList
It is a tree of TaskEntries deferred and waiting on the containing task to exit. The tree is sorted b...
Definition: task.cc:188
TaskEntry * deferq_task_entry_
Definition: task.cc:207
void RunCombinedDeferQ()
Starts executing tasks from deferq_ of TaskEntry and TaskGroup in the temporal order.
Definition: task.cc:1423
void AddToDeferQ(TaskEntry *entry)
Adds a task to deferq_. Only one task of a given instance goes into deferq_ for its policies.
Definition: task.cc:1315
void RunTask(Task *t)
Starts a task. If there are more entries in waitq_ add them to deferq_.
Definition: task.cc:1328
~TaskEntry()
Definition: task.cc:1260
size_t WaitQSize() const
Definition: task.cc:113
boost::intrusive::set_member_hook task_defer_node
Definition: task.cc:179
int task_code_id_
Definition: task.cc:190
int GetRunCount() const
Returns the count of runs for this task entry.
Definition: task.cc:163
A private class used to implement tbb::task An object is created when task is ready for execution and...
Definition: task.cc:46
Task * parent_
Definition: task.cc:73
TaskFunctor(const TaskFunctor &tf)
Creates a copy of the object.
Definition: task.cc:54
TaskFunctor(TaskFunctor &&tf)
Definition: task.cc:57
void operator()() const
Method called from TBB to execute the task. Invokes Run() method of the parent (an implementation)....
Definition: task.cc:370
virtual ~TaskFunctor()
Destructor is called when a task execution is compeleted. Invoked implicitly by tbb::task....
Definition: task.cc:425
TaskFunctor(Task *t)
Creates a new instance of TaskFunctor using the provided implementaton (a parent).
Definition: task.cc:51
TaskGroup maintains per <task-id> information including,.
Definition: task.cc:240
int task_code_id_
Definition: task.cc:332
uint32_t execute_delay_
Definition: task.cc:358
DISALLOW_COPY_AND_ASSIGN(TaskGroup)
void SetDisable(bool disable)
Definition: task.cc:294
void IncrementTotalRunTime(int64_t rtime)
Definition: task.cc:287
void ClearTaskStats()
Definition: task.cc:1211
bool disable_
Definition: task.cc:360
friend class TaskEntry
Definition: task.cc:316
void ClearTaskGroupStats()
Definition: task.cc:1207
int task_id() const
Definition: task.cc:298
bool DeferOnPolicyFail(TaskEntry *entry, Task *t)
Definition: task.cc:1102
void RunDisableEntries()
Run tasks that maybe suspended. Schedule tasks only for TaskEntries which are enabled.
Definition: task.cc:1159
void AddPolicy(TaskGroup *group)
Definition: task.cc:1088
TaskEntry * disable_entry_
Task entry for disabled group.
Definition: task.cc:354
size_t num_tasks() const
Definition: task.cc:300
int TaskRunCount() const
Definition: task.cc:276
void RunDeferQ()
Starts executing tasks from deferq_ of a TaskGroup.
Definition: task.cc:1140
void TaskExited(Task *t)
Definition: task.cc:1154
void AddToDisableQ(TaskEntry *entry)
Enqueue TaskEntry in disable_entry's deferQ.
Definition: task.cc:1131
void DeleteFromDeferQ(TaskEntry &entry)
Delete task from deferq_.
Definition: task.cc:1125
static const int kVectorGrowSize
Definition: task.cc:331
oneapi::tbb::task_group & tbb_group()
Definition: task.cc:311
uint32_t schedule_delay_
Definition: task.cc:359
bool IsWaitQEmpty()
Returns true, if the waiq_ of all the tasks in the group are empty.
Definition: task.cc:1181
void AddEntriesToDisableQ()
Add TaskEntries to disable_entry_ which have tasks enqueued and are already disabled.
Definition: task.cc:1163
bool IsDisabled()
Definition: task.cc:295
void GetSandeshData(SandeshTaskGroup *resp, bool summary) const
Definition: task.cc:1555
void AddToDeferQ(TaskEntry *entry)
Add task to deferq_ Only one task of a given instance goes into deferq_ for its policies.
Definition: task.cc:1118
std::vector< TaskGroup * > TaskGroupPolicyList
Vector of Task Group policies.
Definition: task.cc:320
TaskEntry * GetDisableEntry()
Definition: task.cc:260
boost::intrusive::member_hook< TaskEntry, boost::intrusive::set_member_hook<>, &TaskEntry::task_defer_node > TaskDeferListOption
Definition: task.cc:323
~TaskGroup()
Definition: task.cc:1040
TaskStats * GetTaskStats()
Definition: task.cc:1225
oneapi::tbb::task_group tbb_group_
A TBB object to store executing tasks.
Definition: task.cc:335
TaskDeferList deferq_
Tasks deferred till run_count_ is 0.
Definition: task.cc:348
bool policy_set_
Specifies if policy is already set.
Definition: task.cc:338
TaskEntry * GetTaskEntry(int task_instance)
Definition: task.cc:1059
TaskEntryList task_entry_db_
task-entries in this group
Definition: task.cc:357
std::atomic< uint64_t > total_run_time_
Definition: task.cc:342
TaskEntry * task_entry_
Tasks deferred till run_count_ is 0.
Definition: task.cc:351
TaskStats stats_
Definition: task.cc:362
TaskGroup(int task_id)
Definition: task.cc:1030
int run_count_
No. of tasks running in the group.
Definition: task.cc:341
TaskEntry * QueryTaskEntry(int task_instance) const
Definition: task.cc:1077
size_t deferq_size() const
Definition: task.cc:299
void TaskStarted()
Definition: task.cc:286
TaskGroupPolicyList policy_
Policy rules for the group.
Definition: task.cc:345
boost::intrusive::set< TaskEntry, TaskDeferListOption, boost::intrusive::compare< TaskDeferEntryCmp > > TaskDeferList
It is a tree of TaskEntries deferred and waiting on the containing task to exit. The tree is sorted b...
Definition: task.cc:329
void PolicySet()
Definition: task.cc:1135
TaskGroup * ActiveGroupInPolicy()
Definition: task.cc:1092
TaskStats * GetTaskGroupStats()
Definition: task.cc:1221
void Terminate()
Definition: task_monitor.cc:61
void Start(EventManager *evm)
Definition: task_monitor.cc:51
The TaskScheduler keeps track of what tasks are currently schedulable. When a task is enqueued it is ...
Definition: task.h:306
void EnqueueUnLocked(Task *task)
Definition: task.cc:666
uint64_t enqueue_count_
Definition: task.h:509
uint64_t done_count_
Definition: task.h:510
bool IsTaskGroupEmpty(int task_id) const
Check if there are any Tasks in the given TaskGroup. Assumes that all task ids are mutually exclusive...
Definition: task.cc:600
void Stop()
Stops scheduling of all tasks.
Definition: task.cc:819
bool measure_delay_
Definition: task.h:501
TaskTbbKeepAwake * tbb_awake_task_
Definition: task.h:518
TaskEntry * stop_entry_
Definition: task.h:484
oneapi::tbb::task_arena task_scheduler_
Definition: task.h:487
uint64_t cancel_count_
Definition: task.h:511
int hw_thread_count_
Definition: task.h:498
static boost::scoped_ptr< TaskScheduler > singleton_
Definition: task.h:469
TaskIdMap id_map_
Definition: task.h:494
void Terminate()
Definition: task.cc:950
void EnableTaskGroup(int task_id)
Definition: task.cc:999
int GetTaskId(const std::string &name)
Definition: task.cc:883
oneapi::tbb::global_control tbb_global_control_
Definition: task.h:486
static int GetThreadCount(int thread_count=0)
Get number of tbb worker threads. For testing purposes only. Limit the number of tbb worker threads.
Definition: task.cc:428
CancelReturnCode
Definition: task.h:333
@ CANCELLED
Definition: task.h:334
std::mutex mutex_
Definition: task.h:488
void Log(const char *file_name, uint32_t line_no, const Task *task, const char *description, uint64_t delay)
Definition: task.cc:549
TaskGroup * QueryTaskGroup(int task_id)
Query TaskGroup for a task_id.Assumes valid entry is present for task_id.
Definition: task.cc:596
LogFn log_fn_
Definition: task.h:497
static void SetThreadAmpFactor(int n)
following function allows one to increase max num of threads used by TBB
Definition: task.cc:985
boost::function< void(const char *file_name, uint32_t line_no, const Task *task, const char *description, uint64_t delay)> LogFn
Definition: task.h:310
TaskMonitor * task_monitor_
Definition: task.h:519
oneapi::tbb::task_arena & tbb_arena()
returns current TBB arena.
Definition: task.h:459
void GetSandeshData(SandeshTaskScheduler *resp, bool summary)
Definition: task.cc:1592
TaskScheduler(int thread_count=0)
TaskScheduler constructor. TBB assumes it can use the "thread" invoking tbb::scheduler can be used fo...
Definition: task.cc:466
void DisableTaskEntry(int task_id, int instance_id)
Definition: task.cc:1006
void DisableTaskGroup(int task_id)
Definition: task.cc:989
uint32_t execute_delay_
Log if time taken to execute exceeds the delay.
Definition: task.h:507
void ModifyTbbKeepAwakeTimeout(uint32_t timeout)
Definition: task.cc:531
void Enqueue(Task *task)
Enqueues a task for running. Starts task if all policy rules are met else puts task in waitq....
Definition: task.cc:661
static int GetDefaultThreadCount()
Definition: task.cc:451
TaskStats * GetTaskGroupStats(int task_id)
Definition: task.cc:926
void EnableMonitor(EventManager *evm, uint64_t tbb_keepawake_time_msec, uint64_t inactivity_time_msec, uint64_t poll_interval_msec)
Enable Task monitoring.
Definition: task.cc:537
TaskGroupDb task_group_db_
Definition: task.h:491
EventManager * evm_
Definition: task.h:512
void ClearRunningTask()
Definition: task.cc:980
~TaskScheduler()
Frees up the task_entry_db_ allocated for scheduler.
Definition: task.cc:481
static void Initialize(uint32_t thread_count=0, EventManager *evm=NULL)
Definition: task.cc:504
void SetPolicy(int task_id, TaskPolicy &policy)
Sets the task exclusion policy. Adds policy entries for the task Examples:
Definition: task.cc:636
std::string GetTaskName(int task_id) const
Definition: task.cc:873
void EnableTaskEntry(int task_id, int instance_id)
Definition: task.cc:1011
int id_max_
Definition: task.h:495
uint64_t seqno_
Definition: task.h:490
TaskEntry * GetTaskEntry(int task_id, int instance_id)
Get TaskGroup for a task_id. Grows task_entry_db_ if necessary.
Definition: task.cc:609
void SetLatencyThreshold(const std::string &name, uint32_t execute, uint32_t schedule)
Definition: task.cc:628
static TaskScheduler * GetInstance()
Definition: task.cc:573
static int ThreadAmpFactor_
following variable allows one to increase max num of threads used by TBB
Definition: task.h:516
void Start()
Starts scheduling of all tasks.
Definition: task.cc:825
TaskGroup * GetTaskGroup(int task_id)
Get TaskGroup for a task_id. Grows task_entry_db_ if necessary.
Definition: task.cc:580
TaskEntry * QueryTaskEntry(int task_id, int instance_id)
Query TaskEntry for a task-id and task-instance.
Definition: task.cc:614
uint32_t schedule_delay_
Log if time between enqueue and task-execute exceeds the delay.
Definition: task.h:504
void ClearTaskStats(int task_id)
Definition: task.cc:910
TaskStats * GetTaskStats(int task_id)
Definition: task.cc:934
bool running_
Definition: task.h:489
void OnTaskExit(Task *task)
Method invoked on exit of a Task. Exit of a task can potentially start tasks in pendingq.
Definition: task.cc:789
bool track_run_time_
Definition: task.h:500
bool track_run_time() const
Definition: task.h:425
bool use_spawn_
Use spawn() to run a tbb::task instead of enqueue()
Definition: task.h:483
void RegisterLog(LogFn fn)
Definition: task.cc:557
static bool ShouldUseSpawn()
Definition: task.cc:455
void set_event_manager(EventManager *evm)
Definition: task.cc:519
uint32_t schedule_delay() const
Definition: task.h:429
void Print()
Debug print routine.
Definition: task.cc:835
uint32_t execute_delay() const
Definition: task.h:430
CancelReturnCode Cancel(Task *task)
Cancels a Task that can be in RUN/WAIT state. The caller needs to ensure that the task exists when Ca...
Definition: task.cc:726
static const int kVectorGrowSize
Definition: task.h:468
bool IsEmpty(bool running_only=false)
Returns true if there are no tasks running and/or enqueued If running_only is true,...
Definition: task.cc:850
std::shared_mutex id_map_mutex_
Definition: task.h:493
void ClearTaskGroupStats(int task_id)
Definition: task.cc:902
void EnableLatencyThresholds(uint32_t execute, uint32_t schedule)
Enable logging of tasks exceeding configured latency.
Definition: task.cc:621
void SetRunningTask(Task *)
This function should not be called in production code. It is only for unit testing to control current...
Definition: task.cc:975
bool StartTbbKeepAwakeTask(TaskScheduler *ts, EventManager *event_mgr, const std::string task_name, uint32_t tbbKeepawakeTimeout=1000)
void ModifyTbbKeepAwakeTimeout(uint32_t timeout)
Task is a class to describe a computational task within OpenSDN control plane applications....
Definition: task.h:81
static Task * Running()
Returns a pointer to the current task the code is executing under.
Definition: task.cc:1532
uint64_t seqno() const
Returns the sequence number of this task.
Definition: task.h:141
bool task_recycle_
Determines if the task must be rescheduled (reused) after its completion.
Definition: task.h:220
void tbb_state(TbbState s)
Sets a TBB state for the task.
Definition: task.h:185
uint64_t seqno_
Stores the sequence number.
Definition: task.h:216
State state_
Stores a state of the task.
Definition: task.h:210
friend class TaskFunctor
Gives access to private members for TaskImpl class.
Definition: task.h:179
int task_data_id_
The dataset id within a code path.
Definition: task.h:203
oneapi::tbb::task_handle task_impl_
A handle to a oneTBB object storing low-level information about the managed task.
Definition: task.h:207
virtual void OnTaskCancel()
Called on task exit, if it is marked for cancellation. If the user wants to do any cleanup on task ca...
Definition: task.h:127
int task_code_id() const
Returns the code ID of this task.
Definition: task.h:135
int task_data_id() const
Returns the data ID of this task.
Definition: task.h:138
Task(int task_id, int task_data_id)
Creates a new task with the given values of task code ID and task data ID.
Definition: task.cc:1504
uint64_t schedule_time_
Contains the time when the task was started.
Definition: task.h:230
@ TBB_DONE
Definition: task.h:102
@ TBB_INIT
Definition: task.h:99
@ TBB_EXEC
Definition: task.h:101
@ TBB_ENQUEUED
Definition: task.h:100
bool task_cancel_
Determines if the task's execution was canceled.
Definition: task.h:223
uint32_t execute_delay_
Sets threshold for the task's execution time. If the threshold is exceeded, the event is logged.
Definition: task.h:234
State state() const
Returns a state value of a task.
Definition: task.h:132
uint32_t schedule_delay_
Sets threshold for delay between enqueueing and execution. If the threshold is exceeded,...
Definition: task.h:238
void StartTask(TaskScheduler *scheduler, TaskGroup *group)
Starts execution of a task.
Definition: task.cc:1517
int task_code_id_
The code path executed by the task.
Definition: task.h:200
uint64_t enqueue_time_
Contains the time when the task was enqueued for execution.
Definition: task.h:227
@ WAIT
A task is waiting in a queue.
Definition: task.h:91
@ RUN
A task is being run.
Definition: task.h:94
@ INIT
A task was initialized.
Definition: task.h:88
boost::intrusive::list_member_hook waitq_hook_
Definition: task.h:241
static EventManager evm
#define LOG(_Level, _Msg)
Definition: logging.h:34
bool unit_test()
Definition: bgp_log.cc:53
Comparison routine for the TaskDeferList.
Definition: task.cc:218
uint64_t total_tasks_completed_
Number of total tasks ran.
Definition: task.h:291
uint64_t last_exit_time_
Number of time stamp of latest exist.
Definition: task.h:294
int run_count_
Number of entries currently running.
Definition: task.h:282
int defer_count_
Number of entries in deferq.
Definition: task.h:285
int wait_count_
Number of entries in waitq.
Definition: task.h:279
uint64_t enqueue_count_
Number of tasks enqueued.
Definition: task.h:288
Definition: task_int.h:10
oneapi::tbb::enumerable_thread_specific< Task * > TaskInfo
Definition: task.cc:27
#define TASK_TRACE(scheduler, task, msg, delay)
Definition: task.cc:38
std::vector< TaskEntry * > TaskEntryList
Definition: task.cc:34
ostream & operator<<(ostream &out, const Task &t)
Definition: task.cc:1537
static TaskInfo task_running
Definition: task.cc:31
std::vector< TaskExclusion > TaskPolicy
Defines a type to store an execution policy (a list of task exclusions).
Definition: task.h:272
#define CHECK_CONCURRENCY(...)
static uint64_t ClockMonotonicUsec()
Definition: time_util.h:29
static const std::string duration_usecs_to_string(const uint64_t usecs)
Definition: time_util.h:62
static uint64_t UTCTimestampUsec()
Definition: time_util.h:13