
#ifndef PLS_SCHEDULER_IMPL_H
#define PLS_SCHEDULER_IMPL_H

#include "pls/internal/scheduling/lambda_task.h"

namespace pls {
namespace internal {
namespace scheduling {

// TODO: generally look into the performance implications of using many thread_state::get() calls

template<typename Function>
void scheduler::perform_work(Function work_section) {
  PROFILE_WORK_BLOCK("scheduler::perform_work")

  // Prepare main root task
  lambda_task_by_reference<Function> root_task{work_section};
  main_thread_root_task_ = &root_task;
  work_section_done_ = false;

  if (reuse_thread_) {
    // TODO: See if we should change thread-states to not make our state override the current thread state
    auto my_state = memory_->thread_state_for(0);
    base::this_thread::set_state(my_state); // Make THIS THREAD become the main worker

    sync_barrier_.wait(); // Trigger threads to wake up

    // Do work (see if we can remove this duplicated code)
    root_task.parent_ = nullptr;
    root_task.deque_offset_ = my_state->deque_.save_offset();
    root_task.execute();
    work_section_done_ = true;

    sync_barrier_.wait(); // Wait for threads to finish
  } else {
    // Simply trigger the others to do the work, this thread will sleep/wait for the time being
    sync_barrier_.wait(); // Trigger threads to wake up
    sync_barrier_.wait(); // Wait for threads to finish
  }
}

template<typename T, typename ...ARGS>
void scheduler::spawn_child(ARGS &&... args) {
  thread_state::get()->current_task_->spawn_child<T>(std::forward<ARGS>(args)...);
}

template<typename T, typename ...ARGS>
void scheduler::spawn_child_and_wait(ARGS &&... args) {
  thread_state::get()->current_task_->spawn_child_and_wait<T>(std::forward<ARGS>(args)...);
}

}
}
}

#endif //PLS_SCHEDULER_IMPL_H
