fork_join_task.cpp 4.16 KB
Newer Older
1 2
#include <easy/profiler.h>

3
#include "pls/internal/scheduling/scheduler.h"
4
#include "pls/internal/scheduling/fork_join_task.h"
5 6 7 8

namespace pls {
    namespace internal {
        namespace scheduling {
9
            fork_join_sub_task::fork_join_sub_task():
10
                base::deque_item{},
11 12 13
                ref_count_{0},
                parent_{nullptr},
                tbb_task_{nullptr},
14
                stack_state_{nullptr} {}
15

16
            fork_join_sub_task::fork_join_sub_task(const fork_join_sub_task& other): base::deque_item(other) {
17
                // Do Nothing, will be inited after this anyways
18 19
            }

20
            void fork_join_sub_task::execute()  {
21
                EASY_BLOCK("execute sub_task", profiler::colors::Green);
22
                tbb_task_->currently_executing_ = this;
23
                execute_internal();
24
                tbb_task_->currently_executing_ = nullptr;
25
                EASY_END_BLOCK;
26
                wait_for_all();
27 28 29 30

                if (parent_ != nullptr) {
                    parent_->ref_count_--;
                }
31 32
            }

33
            void fork_join_sub_task::spawn_child_internal(fork_join_sub_task* sub_task) {
34 35 36 37 38 39 40 41
                // Keep our refcount up to date
                ref_count_++;

                // Assign forced values
                sub_task->parent_ = this;
                sub_task->tbb_task_ = tbb_task_;
                sub_task->stack_state_ = tbb_task_->my_stack_->save_state();

42
                tbb_task_->deque_.push_tail(sub_task);
43 44
            }

45
            void fork_join_sub_task::wait_for_all() {
46
                while (ref_count_ > 0) {
47
                    EASY_BLOCK("get local sub task", profiler::colors::Blue)
48
                    fork_join_sub_task* local_task = tbb_task_->get_local_sub_task();
49
                    EASY_END_BLOCK
50 51 52 53
                    if (local_task != nullptr) {
                        local_task->execute();
                    } else {
                        // Try to steal work.
54
                        // External steal will be executed implicitly if success
55 56 57 58
                        EASY_BLOCK("steal work", profiler::colors::Blue)
                        bool internal_steal_success = tbb_task_->steal_work();
                        EASY_END_BLOCK
                        if (internal_steal_success) {
59
                            tbb_task_->last_stolen_->execute();
60 61 62
                        }
                    }
                }
63 64 65
                tbb_task_->my_stack_->reset_state(stack_state_);
            }

66
            fork_join_sub_task* fork_join_task::get_local_sub_task() {
67
                return deque_.pop_tail();
68 69
            }

70
            fork_join_sub_task* fork_join_task::get_stolen_sub_task() {
71
                return deque_.pop_head();
72 73
            }

74
            bool fork_join_task::internal_stealing(abstract_task* other_task) {
75
                EASY_FUNCTION(profiler::colors::Blue);
76
                auto cast_other_task = reinterpret_cast<fork_join_task*>(other_task);
77 78 79 80 81

                auto stolen_sub_task = cast_other_task->get_stolen_sub_task();
                if (stolen_sub_task == nullptr) {
                    return false;
                } else {
82
                    // Make sub-task belong to our fork_join_task instance
83 84 85 86 87 88 89 90 91
                    stolen_sub_task->tbb_task_ = this;
                    stolen_sub_task->stack_state_ = my_stack_->save_state();
                    // We will execute this next without explicitly moving it onto our stack storage
                    last_stolen_ = stolen_sub_task;

                    return true;
                }
            }

92
            bool fork_join_task::split_task(base::spin_lock* lock) {
93
                EASY_FUNCTION(profiler::colors::Blue);
94
                fork_join_sub_task* stolen_sub_task = get_stolen_sub_task();
95 96 97
                if (stolen_sub_task == nullptr) {
                    return false;
                }
98
                fork_join_task task{stolen_sub_task, this->unique_id()};
99

100 101 102
                // In success case, unlock.
                // TODO: this locking is complicated and error prone.
                lock->unlock();
103 104 105

                scheduler::execute_task(task, depth());
                return true;
106 107 108 109
            }
        }
    }
}