-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTaskSystem.cpp
More file actions
148 lines (122 loc) · 4.01 KB
/
Copy pathTaskSystem.cpp
File metadata and controls
148 lines (122 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "TaskSystem.h"
#include <condition_variable>
#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <set>
#include <map>
#include <iostream>
using namespace std;
IRunnable::~IRunnable() noexcept {}
TaskSystem::TaskSystem(int num_threads)
: num_threads(num_threads), active_tasks(0), shutdown(false), last_task_id(0) {
threads.reserve(num_threads);
for (int i = 0; i < num_threads; i++) {
threads[i] = thread(&TaskSystem::workerThread, this);
}
}
TaskSystem::~TaskSystem() {
shutdown.store(true);
{
unique_lock<std::mutex> lock(mutex);
task_available_cv.notify_all();
}
for (int i = 0; i < num_threads; i++)
{
if(threads[i].joinable())
threads[i].join();
}
}
void TaskSystem::workerThread() {
while (true) {
ReadyTaskData next_task;
bool has_task = false;
{
unique_lock<std::mutex> lock(mutex);
if (!ready_queue.empty()) {
next_task = ready_queue.front();
ready_queue.pop();
has_task = true;
active_tasks ++;
}
}
if (has_task) {
next_task.runnable->runTask(next_task.index, next_task.num_total_tasks);
{
unique_lock<std::mutex> lock(mutex);
markTaskFinished(next_task);
}
}
else {
if (shutdown.load())
break;
{
unique_lock<std::mutex> lock(mutex);
// if the queue is empty, and the system isn't terminated, stay asleep
task_available_cv.wait(lock, [this](){
return !ready_queue.empty() or shutdown.load();
});
}
}
}
}
void TaskSystem::markTaskFinished(ReadyTaskData finished_task) {
// notify all tasks depending on finished_task that it has finished
for (TaskID t : tasks_depending_on[finished_task.id]) {
waiting_list[t].num_deps --;
// if finished_task is the last dependency of batch t,
// push all tasks of batch t into ready queue
if (waiting_list[t].num_deps == 0) {
WaitingTaskBatchData newly_ready_batch = waiting_list[t];
waiting_list.erase(t);
for (int i = 0; i < newly_ready_batch.num_total_tasks; i++) {
ready_queue.push({newly_ready_batch.id, newly_ready_batch.runnable, i, newly_ready_batch.num_total_tasks});
}
}
}
is_task_done[finished_task.id] = true;
active_tasks --;
if (active_tasks == 0 and ready_queue.empty()) {
completion_cv.notify_all();
}
task_available_cv.notify_one();
}
TaskID TaskSystem::run(IRunnable* runnable, int num_total_tasks,
const std::vector<TaskID>& deps) {
int this_task_id;
{
unique_lock<std::mutex> lock(mutex);
this_task_id = ++last_task_id;
int num_deps = 0;
// appending new task to depndency graph
for (TaskID t : deps) {
if (!is_task_done[t]) {
tasks_depending_on[t].push_back(this_task_id);
num_deps ++;
}
}
// push each task from the batch to the ready queue
// if has no dependencies or all deps are executed
if (num_deps == 0) {
for (int i = 0; i < num_total_tasks; i++) {
ready_queue.push({this_task_id, runnable, i, num_total_tasks});
}
}
// put the whole batch on a waiting list if it has dependencies
else {
waiting_list[this_task_id] = {this_task_id, num_deps, runnable, num_total_tasks};
}
task_available_cv.notify_all();
}
return this_task_id;
}
void TaskSystem::sync() {
{
unique_lock<std::mutex> lock(mutex);
completion_cv.wait(lock, [this]() {
return active_tasks == 0 && ready_queue.empty();
});
}
return;
}