task_manager.cpp 3.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include "pls/internal/scheduling/thread_state.h"
#include "pls/internal/scheduling/scheduler.h"

#include "pls/internal/scheduling/lock_free/task_manager.h"
#include "pls/internal/scheduling/lock_free/task.h"

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

task_manager::task_manager(unsigned thread_id,
                           size_t num_tasks,
                           size_t stack_size,
                           std::shared_ptr<stack_allocator> &stack_allocator) : stack_allocator_{stack_allocator},
                                                                                tasks_{},
                                                                                deque_{thread_id, num_tasks} {
  tasks_.reserve(num_tasks);

17
  for (size_t i = 0; i < num_tasks; i++) {
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
    char *stack_memory = stack_allocator->allocate_stack(stack_size);
    tasks_.emplace_back(std::make_unique<task>(stack_memory, stack_size, i, thread_id));

    if (i > 0) {
      tasks_[i - 1]->next_ = tasks_[i].get();
      tasks_[i]->prev_ = tasks_[i - 1].get();
    }
  }
}

task_manager::~task_manager() {
  for (auto &task : tasks_) {
    stack_allocator_->free_stack(task->stack_size_, task->stack_memory_);
  }
}

void task_manager::push_local_task(base_task *pushed_task) {
  deque_.push_bot(static_cast<task *>(pushed_task));
}

base_task *task_manager::pop_local_task() {
39
  return deque_.pop_bot();
40 41
}

42
std::tuple<base_task *, base_task *, bool> task_manager::steal_task(thread_state &stealing_state) {
43 44 45 46 47
  PLS_ASSERT(stealing_state.get_active_task()->depth_ == 0, "Must only steal with clean task chain.");
  PLS_ASSERT(scheduler::check_task_chain(*stealing_state.get_active_task()), "Must only steal with clean task chain.");

  auto peek = deque_.peek_top();
  if (peek.top_task_) {
48
    task *stolen_task = peek.top_task_;
49 50
    // get a suitable task to trade in
    // TODO: opt. add debug marker to traded in tasks that we do not accidentally use them.
51
    task *traded_task = static_cast<task *>(&scheduler::task_chain_at(stolen_task->depth_, stealing_state));
52
    base_task *chain_after_stolen_task = traded_task->next_;
53 54

    // perform the actual pop operation
55
    task *pop_result_task = deque_.pop_top(traded_task, peek);
56 57 58
    if (pop_result_task) {
      PLS_ASSERT(stolen_task->thread_id_ != traded_task->thread_id_,
                 "It is impossible to steal an task we already own!");
59
      PLS_ASSERT(pop_result_task == stolen_task,
60 61 62
                 "We must only steal the task that we peeked at!");

      // update the resource stack associated with the stolen task
63
      stolen_task->push_task_chain(traded_task);
64

65
      task *optional_exchanged_task = external_trading_deque::get_trade_object(stolen_task);
66 67
      if (optional_exchanged_task) {
        // All good, we pushed the task over to the stack, nothing more to do
68
        PLS_ASSERT(optional_exchanged_task == traded_task,
69 70 71 72
                   "We are currently executing this, no one else can put another task in this field!");
      } else {
        // The last other active thread took it as its spare resource...
        // ...remove our traded object from the stack again (it must be empty now and no one must access it anymore).
73
        stolen_task->reset_task_chain();
74 75
      }

76
      return std::tuple{stolen_task, chain_after_stolen_task, true};
77
    } else {
78
      return std::tuple{nullptr, nullptr, false};
79 80
    }
  } else {
81
    return std::tuple{nullptr, nullptr, true};
82 83 84 85 86 87
  }
}

base_task *task_manager::pop_clean_task_chain(base_task *base_task) {
  task *popped_task = static_cast<task *>(base_task);
  // Try to get a clean resource chain to go back to the main stealing loop
88
  task *clean_chain = popped_task->pop_task_chain();
89 90
  if (clean_chain == nullptr) {
    // double-check if we are really last one or we only have unlucky timing
91
    task *optional_cas_task = external_trading_deque::get_trade_object(popped_task);
92
    if (optional_cas_task) {
93
      clean_chain = optional_cas_task;
94
    } else {
95
      clean_chain = popped_task->pop_task_chain();
96 97 98 99 100 101 102
    }
  }

  return clean_chain;
}

}