task.cpp 3.79 KB
Newer Older
1 2 3 4 5
#include "pls/internal/scheduling/lock_free/task.h"
#include "pls/internal/scheduling/scheduler.h"

namespace pls::internal::scheduling::lock_free {

6
task *task::find_task(unsigned id, unsigned depth) {
7 8 9
  return thread_state::get().get_scheduler().thread_state_for(id).get_task_manager().get_task(depth);
}

10 11 12 13 14 15 16 17
void task::prepare_for_push(unsigned int pushing_thread_id) {
  data_structures::stamped_integer target_next;
  target_next.stamp_ = pushing_thread_id + 1;
  target_next.value_ = 0;
  resource_stack_next_.store(target_next, std::memory_order_relaxed);
}

bool task::push_task_chain(task *spare_task_chain, unsigned pushing_thread_id) {
18 19
  num_resources_++;

20 21 22 23 24
  PLS_ASSERT(this->thread_id_ != spare_task_chain->thread_id_,
             "Makes no sense to push task onto itself, as it is not clean by definition.");
  PLS_ASSERT(this->depth_ == spare_task_chain->depth_,
             "Must only push tasks with correct depth.");

25 26
  data_structures::stamped_integer current_root;
  data_structures::stamped_integer target_root;
27 28 29 30 31 32

  data_structures::stamped_integer expected_next_field;
  data_structures::stamped_integer target_next_field;
  expected_next_field.stamp_ = pushing_thread_id + 1;
  expected_next_field.value_ = 0;

33
  int iteration = 0;
34
  do {
35
    iteration++;
36
    current_root = this->resource_stack_root_.load();
37
    target_root.stamp_ = current_root.stamp_ + 1;
38 39
    target_root.value_ = spare_task_chain->thread_id_ + 1;

40
    // Prepare the target_next_field as required
41 42
    if (current_root.value_ == 0) {
      // Empty, simply push in with no successor.
43 44
      target_next_field.stamp_ = pushing_thread_id + 1;
      target_next_field.value_ = 0;
45 46 47
    } else {
      // Already an entry. Find it's corresponding task and set it as our successor.
      auto *current_root_task = find_task(current_root.value_ - 1, this->depth_);
48 49 50 51 52 53 54 55 56 57

      target_next_field.stamp_ = pushing_thread_id + 1;
      target_next_field.value_ = current_root_task->thread_id_ + 1;
    }

    if (!spare_task_chain->resource_stack_next_.compare_exchange_strong(expected_next_field, target_next_field)) {
      num_resources_--;
      return false;
    } else {
      expected_next_field = target_next_field;
58 59 60
    }

  } while (!this->resource_stack_root_.compare_exchange_strong(current_root, target_root));
61 62

  return true;
63 64
}

65 66
void task::reset_task_chain(task *expected_content) {
  num_resources_--;
67

68 69 70
  data_structures::stamped_integer current_root = this->resource_stack_root_.load();
  PLS_ASSERT(current_root.value_ == expected_content->thread_id_ + 1,
             "Must only reset the task chain if we exactly know its state! (current_root.value_)");
71

72 73 74 75
  data_structures::stamped_integer target_root;
  target_root.stamp_ = current_root.stamp_ + 1;
  bool success = this->resource_stack_root_.compare_exchange_strong(current_root, target_root);
  PLS_ASSERT(success, "Must always succeed in resetting the chain, as we must be the sole one operating on it!");
76 77 78
}

task *task::pop_task_chain() {
79 80
  data_structures::stamped_integer current_root;
  data_structures::stamped_integer target_root;
81 82 83
  task *output_task;
  do {
    current_root = this->resource_stack_root_.load();
84 85 86
    if (current_root.value_ == 0) {
      // Empty...
      return nullptr;
87
    } else {
88 89
      // Found something, try to pop it
      auto *current_root_task = find_task(current_root.value_ - 1, this->depth_);
90
      auto next_stack_cas = current_root_task->resource_stack_next_.load();
91

92
      target_root.stamp_ = current_root.stamp_ + 1;
93
      target_root.value_ = next_stack_cas.value_;
94

95
      output_task = current_root_task;
96 97 98
    }
  } while (!this->resource_stack_root_.compare_exchange_strong(current_root, target_root));

99
  PLS_ASSERT(num_resources_.fetch_add(-1) > 0, "Must only return an task from the chain if there are items!");
100

101
  output_task->resource_stack_next_.store({0, 0});
102 103 104 105
  return output_task;
}

}