task_manager_impl.h 3.26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

#ifndef PLS_TASK_MANAGER_IMPL_H_
#define PLS_TASK_MANAGER_IMPL_H_

#include <memory>
#include <utility>
#include <array>

#include "context_switcher/continuation.h"

#include "pls/internal/scheduling/scheduler.h"
#include "pls/internal/scheduling/task.h"
#include "pls/internal/scheduling/thread_state.h"

namespace pls {
namespace internal {
namespace scheduling {

template<typename F>
void task_manager::spawn_child(F &&lambda) {
21
  auto *spawning_task_manager = this;
22 23
  auto *last_task = spawning_task_manager->active_task_;
  auto *spawned_task = spawning_task_manager->active_task_->next_;
24

25 26 27
  auto continuation =
      spawned_task->run_as_task([=](context_switcher::continuation cont) {
        // allow stealing threads to continue the last task.
28 29
        last_task->continuation_ = std::move(cont);

30
        // we are now executing the new task, allow others to steal the last task continuation.
31
        spawned_task->is_synchronized_ = true;
32
        spawning_task_manager->active_task_ = spawned_task;
33
        spawning_task_manager->deque_.push_bot(last_task);
34 35

        // execute the lambda itself, which could lead to a different thread returning.
36 37
        lambda();
        auto *syncing_task_manager = &thread_state::get().get_task_manager();
38 39
        PLS_ASSERT(syncing_task_manager->active_task_ == spawned_task,
                   "Task manager must always point its active task onto whats executing.");
40

41 42 43 44 45
        // try to pop a task of the syncing task manager.
        // possible outcomes:
        // - this is a different task manager, it must have an empty deque and fail
        // - this is the same task manager and someone stole last tasks, thus this will fail
        // - this is the same task manager and no one stole the last task, this this will succeed
46 47
        auto pop_result = syncing_task_manager->deque_.pop_bot();
        if (pop_result) {
48
          // Fast path, simply continue execution where we left of before spawn.
49 50 51 52 53 54 55
          PLS_ASSERT(*pop_result == last_task,
                     "Fast path, nothing can have changed until here.");
          PLS_ASSERT(spawning_task_manager == syncing_task_manager,
                     "Fast path, nothing can have changed here.");
          PLS_ASSERT(last_task->continuation_.valid(),
                     "Fast path, no one can have continued working on the last task.");

56 57 58
          syncing_task_manager->active_task_ = last_task;
          return std::move(last_task->continuation_);
        } else {
59
          // Slow path, the last task was stolen. Sync using the resource stack.
60 61 62
          context_switcher::continuation result_cont;
          if (syncing_task_manager->try_clean_return(result_cont)) {
            // We return back to the main scheduling loop
63 64
            PLS_ASSERT(result_cont.valid(), "Must only return valid continuations...");
            return std::move(result_cont);
65
          } else {
66
            // We finish up the last task and are the sole owner again
67 68
            PLS_ASSERT(result_cont.valid(), "Must only return valid continuations...");
            return std::move(result_cont);
69 70
          }
        }
71
      });
72 73 74

  if (continuation.valid()) {
    // We jumped in here from the main loop, keep track!
75
    thread_state::get().main_continuation() = std::move(continuation);
76 77 78 79 80 81 82 83
  }
}

}
}
}

#endif //PLS_TASK_MANAGER_IMPL_H_