Commit | Line | Data |
---|---|---|
bae7f79e ILT |
1 | // workqueue.cc -- the workqueue for gold |
2 | ||
6f2750fe | 3 | // Copyright (C) 2006-2016 Free Software Foundation, Inc. |
6cb15b7f ILT |
4 | // Written by Ian Lance Taylor <iant@google.com>. |
5 | ||
6 | // This file is part of gold. | |
7 | ||
8 | // This program is free software; you can redistribute it and/or modify | |
9 | // it under the terms of the GNU General Public License as published by | |
10 | // the Free Software Foundation; either version 3 of the License, or | |
11 | // (at your option) any later version. | |
12 | ||
13 | // This program is distributed in the hope that it will be useful, | |
14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | // GNU General Public License for more details. | |
17 | ||
18 | // You should have received a copy of the GNU General Public License | |
19 | // along with this program; if not, write to the Free Software | |
20 | // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
21 | // MA 02110-1301, USA. | |
22 | ||
bae7f79e | 23 | #include "gold.h" |
c33feb2b | 24 | |
c7912668 | 25 | #include "debug.h" |
17a1d0a9 | 26 | #include "options.h" |
d675ff46 | 27 | #include "timer.h" |
bae7f79e | 28 | #include "workqueue.h" |
c7912668 | 29 | #include "workqueue-internal.h" |
bae7f79e ILT |
30 | |
31 | namespace gold | |
32 | { | |
33 | ||
17a1d0a9 | 34 | // Class Task_list. |
bae7f79e | 35 | |
17a1d0a9 | 36 | // Add T to the end of the list. |
bae7f79e | 37 | |
17a1d0a9 ILT |
38 | inline void |
39 | Task_list::push_back(Task* t) | |
bae7f79e | 40 | { |
17a1d0a9 ILT |
41 | gold_assert(t->list_next() == NULL); |
42 | if (this->head_ == NULL) | |
43 | { | |
44 | this->head_ = t; | |
45 | this->tail_ = t; | |
46 | } | |
bae7f79e | 47 | else |
17a1d0a9 ILT |
48 | { |
49 | this->tail_->set_list_next(t); | |
50 | this->tail_ = t; | |
51 | } | |
bae7f79e ILT |
52 | } |
53 | ||
da769d56 ILT |
54 | // Add T to the front of the list. |
55 | ||
56 | inline void | |
57 | Task_list::push_front(Task* t) | |
58 | { | |
59 | gold_assert(t->list_next() == NULL); | |
60 | if (this->head_ == NULL) | |
61 | { | |
62 | this->head_ = t; | |
63 | this->tail_ = t; | |
64 | } | |
65 | else | |
66 | { | |
67 | t->set_list_next(this->head_); | |
68 | this->head_ = t; | |
69 | } | |
70 | } | |
71 | ||
17a1d0a9 ILT |
72 | // Remove and return the first Task waiting for this lock to be |
73 | // released. | |
bae7f79e | 74 | |
17a1d0a9 ILT |
75 | inline Task* |
76 | Task_list::pop_front() | |
bae7f79e | 77 | { |
17a1d0a9 ILT |
78 | Task* ret = this->head_; |
79 | if (ret != NULL) | |
bae7f79e | 80 | { |
17a1d0a9 ILT |
81 | if (ret == this->tail_) |
82 | { | |
83 | gold_assert(ret->list_next() == NULL); | |
84 | this->head_ = NULL; | |
85 | this->tail_ = NULL; | |
86 | } | |
87 | else | |
88 | { | |
89 | this->head_ = ret->list_next(); | |
90 | gold_assert(this->head_ != NULL); | |
91 | ret->clear_list_next(); | |
92 | } | |
bae7f79e | 93 | } |
17a1d0a9 | 94 | return ret; |
bae7f79e ILT |
95 | } |
96 | ||
17a1d0a9 | 97 | // The simple single-threaded implementation of Workqueue_threader. |
bae7f79e | 98 | |
17a1d0a9 | 99 | class Workqueue_threader_single : public Workqueue_threader |
bae7f79e ILT |
100 | { |
101 | public: | |
17a1d0a9 ILT |
102 | Workqueue_threader_single(Workqueue* workqueue) |
103 | : Workqueue_threader(workqueue) | |
bae7f79e | 104 | { } |
17a1d0a9 | 105 | ~Workqueue_threader_single() |
bae7f79e ILT |
106 | { } |
107 | ||
fe9a4c12 | 108 | void |
17a1d0a9 ILT |
109 | set_thread_count(int thread_count) |
110 | { gold_assert(thread_count > 0); } | |
fe9a4c12 | 111 | |
17a1d0a9 | 112 | bool |
dcd8d12e | 113 | should_cancel_thread(int) |
17a1d0a9 | 114 | { return false; } |
bae7f79e ILT |
115 | }; |
116 | ||
bae7f79e ILT |
117 | // Workqueue methods. |
118 | ||
fe9a4c12 | 119 | Workqueue::Workqueue(const General_options& options) |
17a1d0a9 | 120 | : lock_(), |
c7912668 | 121 | first_tasks_(), |
bae7f79e | 122 | tasks_(), |
bae7f79e | 123 | running_(0), |
17a1d0a9 ILT |
124 | waiting_(0), |
125 | condvar_(this->lock_), | |
126 | threader_(NULL) | |
bae7f79e | 127 | { |
fe9a4c12 ILT |
128 | bool threads = options.threads(); |
129 | #ifndef ENABLE_THREADS | |
130 | threads = false; | |
131 | #endif | |
132 | if (!threads) | |
17a1d0a9 | 133 | this->threader_ = new Workqueue_threader_single(this); |
fe9a4c12 | 134 | else |
c7912668 ILT |
135 | { |
136 | #ifdef ENABLE_THREADS | |
17a1d0a9 | 137 | this->threader_ = new Workqueue_threader_threadpool(this); |
c7912668 ILT |
138 | #else |
139 | gold_unreachable(); | |
140 | #endif | |
141 | } | |
bae7f79e ILT |
142 | } |
143 | ||
144 | Workqueue::~Workqueue() | |
145 | { | |
17a1d0a9 ILT |
146 | } |
147 | ||
148 | // Add a task to the end of a specific queue, or put it on the list | |
149 | // waiting for a Token. | |
150 | ||
151 | void | |
2ea97941 | 152 | Workqueue::add_to_queue(Task_list* queue, Task* t, bool front) |
17a1d0a9 ILT |
153 | { |
154 | Hold_lock hl(this->lock_); | |
155 | ||
156 | Task_token* token = t->is_runnable(); | |
157 | if (token != NULL) | |
158 | { | |
da769d56 ILT |
159 | if (front) |
160 | token->add_waiting_front(t); | |
161 | else | |
162 | token->add_waiting(t); | |
17a1d0a9 ILT |
163 | ++this->waiting_; |
164 | } | |
165 | else | |
166 | { | |
da769d56 | 167 | if (front) |
2ea97941 | 168 | queue->push_front(t); |
da769d56 | 169 | else |
2ea97941 | 170 | queue->push_back(t); |
17a1d0a9 ILT |
171 | // Tell any waiting thread that there is work to do. |
172 | this->condvar_.signal(); | |
173 | } | |
bae7f79e ILT |
174 | } |
175 | ||
92e059d8 ILT |
176 | // Add a task to the queue. |
177 | ||
bae7f79e ILT |
178 | void |
179 | Workqueue::queue(Task* t) | |
180 | { | |
da769d56 ILT |
181 | this->add_to_queue(&this->tasks_, t, false); |
182 | } | |
183 | ||
184 | // Queue a task which should run soon. | |
185 | ||
186 | void | |
187 | Workqueue::queue_soon(Task* t) | |
188 | { | |
189 | t->set_should_run_soon(); | |
190 | this->add_to_queue(&this->first_tasks_, t, false); | |
bae7f79e ILT |
191 | } |
192 | ||
da769d56 | 193 | // Queue a task which should run next. |
92e059d8 ILT |
194 | |
195 | void | |
da769d56 | 196 | Workqueue::queue_next(Task* t) |
92e059d8 | 197 | { |
17a1d0a9 | 198 | t->set_should_run_soon(); |
da769d56 | 199 | this->add_to_queue(&this->first_tasks_, t, true); |
92e059d8 ILT |
200 | } |
201 | ||
17a1d0a9 | 202 | // Return whether to cancel the current thread. |
bae7f79e | 203 | |
17a1d0a9 | 204 | inline bool |
dcd8d12e | 205 | Workqueue::should_cancel_thread(int thread_number) |
bae7f79e | 206 | { |
dcd8d12e | 207 | return this->threader_->should_cancel_thread(thread_number); |
bae7f79e ILT |
208 | } |
209 | ||
17a1d0a9 ILT |
210 | // Find a runnable task in TASKS. Return NULL if none could be found. |
211 | // If we find a Task waiting for a Token, add it to the list for that | |
212 | // Token. The workqueue lock must be held when this is called. | |
bae7f79e ILT |
213 | |
214 | Task* | |
17a1d0a9 | 215 | Workqueue::find_runnable_in_list(Task_list* tasks) |
bae7f79e | 216 | { |
c7912668 | 217 | Task* t; |
17a1d0a9 | 218 | while ((t = tasks->pop_front()) != NULL) |
bae7f79e | 219 | { |
17a1d0a9 ILT |
220 | Task_token* token = t->is_runnable(); |
221 | ||
222 | if (token == NULL) | |
223 | return t; | |
224 | ||
225 | token->add_waiting(t); | |
226 | ++this->waiting_; | |
227 | } | |
228 | ||
229 | // We couldn't find any runnable task. | |
230 | return NULL; | |
231 | } | |
232 | ||
233 | // Find a runnable task. Return NULL if none could be found. The | |
234 | // workqueue lock must be held when this is called. | |
bae7f79e | 235 | |
17a1d0a9 ILT |
236 | Task* |
237 | Workqueue::find_runnable() | |
238 | { | |
239 | Task* t = this->find_runnable_in_list(&this->first_tasks_); | |
240 | if (t == NULL) | |
241 | t = this->find_runnable_in_list(&this->tasks_); | |
242 | return t; | |
243 | } | |
244 | ||
245 | // Find a runnable a task, and wait until we find one. Return NULL if | |
246 | // we should exit. The workqueue lock must be held when this is | |
247 | // called. | |
248 | ||
249 | Task* | |
250 | Workqueue::find_runnable_or_wait(int thread_number) | |
251 | { | |
252 | Task* t = this->find_runnable(); | |
253 | ||
254 | while (t == NULL) | |
255 | { | |
256 | if (this->running_ == 0 | |
257 | && this->first_tasks_.empty() | |
258 | && this->tasks_.empty()) | |
bae7f79e | 259 | { |
17a1d0a9 ILT |
260 | // Kick all the threads to make them exit. |
261 | this->condvar_.broadcast(); | |
bae7f79e | 262 | |
17a1d0a9 ILT |
263 | gold_assert(this->waiting_ == 0); |
264 | return NULL; | |
bae7f79e | 265 | } |
c7912668 | 266 | |
dcd8d12e | 267 | if (this->should_cancel_thread(thread_number)) |
17a1d0a9 ILT |
268 | return NULL; |
269 | ||
270 | gold_debug(DEBUG_TASK, "%3d sleeping", thread_number); | |
c7912668 | 271 | |
17a1d0a9 ILT |
272 | this->condvar_.wait(); |
273 | ||
274 | gold_debug(DEBUG_TASK, "%3d awake", thread_number); | |
275 | ||
276 | t = this->find_runnable(); | |
bae7f79e | 277 | } |
c7912668 | 278 | |
17a1d0a9 | 279 | return t; |
bae7f79e ILT |
280 | } |
281 | ||
17a1d0a9 ILT |
282 | // Find and run tasks. If we can't find a runnable task, wait for one |
283 | // to become available. If we run a task, and it frees up another | |
284 | // runnable task, then run that one too. This returns true if we | |
285 | // should look for another task, false if we are cancelling this | |
286 | // thread. | |
bae7f79e | 287 | |
17a1d0a9 ILT |
288 | bool |
289 | Workqueue::find_and_run_task(int thread_number) | |
bae7f79e | 290 | { |
17a1d0a9 ILT |
291 | Task* t; |
292 | Task_locker tl; | |
293 | ||
294 | { | |
295 | Hold_lock hl(this->lock_); | |
296 | ||
297 | // Find a runnable task. | |
298 | t = this->find_runnable_or_wait(thread_number); | |
299 | ||
300 | if (t == NULL) | |
301 | return false; | |
302 | ||
303 | // Get the locks for the task. This must be called while we are | |
304 | // still holding the Workqueue lock. | |
305 | t->locks(&tl); | |
306 | ||
307 | ++this->running_; | |
308 | } | |
309 | ||
310 | while (t != NULL) | |
bae7f79e | 311 | { |
17a1d0a9 ILT |
312 | gold_debug(DEBUG_TASK, "%3d running task %s", thread_number, |
313 | t->name().c_str()); | |
bae7f79e | 314 | |
d675ff46 RÁE |
315 | Timer timer; |
316 | if (is_debugging_enabled(DEBUG_TASK)) | |
317 | timer.start(); | |
318 | ||
17a1d0a9 | 319 | t->run(this); |
c7912668 | 320 | |
d675ff46 RÁE |
321 | if (is_debugging_enabled(DEBUG_TASK)) |
322 | { | |
323 | Timer::TimeStats elapsed = timer.get_elapsed_time(); | |
324 | ||
325 | gold_debug(DEBUG_TASK, | |
326 | "%3d completed task %s " | |
327 | "(user: %ld.%06ld sys: %ld.%06ld wall: %ld.%06ld)", | |
328 | thread_number, t->name().c_str(), | |
329 | elapsed.user / 1000, (elapsed.user % 1000) * 1000, | |
3e1b9a8a | 330 | elapsed.sys / 1000, (elapsed.sys % 1000) * 1000, |
d675ff46 RÁE |
331 | elapsed.wall / 1000, (elapsed.wall % 1000) * 1000); |
332 | } | |
c7912668 | 333 | |
17a1d0a9 | 334 | Task* next; |
bae7f79e | 335 | { |
17a1d0a9 | 336 | Hold_lock hl(this->lock_); |
bae7f79e | 337 | |
17a1d0a9 | 338 | --this->running_; |
c7912668 | 339 | |
17a1d0a9 ILT |
340 | // Release the locks for the task. This must be done with the |
341 | // workqueue lock held. Get the next Task to run if any. | |
342 | next = this->release_locks(t, &tl); | |
343 | ||
344 | if (next == NULL) | |
345 | next = this->find_runnable(); | |
346 | ||
347 | // If we have another Task to run, get the Locks. This must | |
348 | // be called while we are still holding the Workqueue lock. | |
349 | if (next != NULL) | |
c7912668 | 350 | { |
17a1d0a9 ILT |
351 | tl.clear(); |
352 | next->locks(&tl); | |
353 | ||
354 | ++this->running_; | |
bae7f79e ILT |
355 | } |
356 | } | |
357 | ||
17a1d0a9 ILT |
358 | // We are done with this task. |
359 | delete t; | |
bae7f79e | 360 | |
17a1d0a9 | 361 | t = next; |
bae7f79e | 362 | } |
17a1d0a9 ILT |
363 | |
364 | return true; | |
bae7f79e ILT |
365 | } |
366 | ||
17a1d0a9 ILT |
367 | // Handle the return value of release_locks, and get tasks ready to |
368 | // run. | |
bae7f79e | 369 | |
17a1d0a9 | 370 | // 1) If T is not runnable, queue it on the appropriate token. |
c7912668 | 371 | |
17a1d0a9 ILT |
372 | // 2) Otherwise, T is runnable. If *PRET is not NULL, then we have |
373 | // already decided which Task to run next. Add T to the list of | |
374 | // runnable tasks, and signal another thread. | |
bae7f79e | 375 | |
17a1d0a9 ILT |
376 | // 3) Otherwise, *PRET is NULL. If IS_BLOCKER is false, then T was |
377 | // waiting on a write lock. We can grab that lock now, so we run T | |
378 | // now. | |
bae7f79e | 379 | |
17a1d0a9 ILT |
380 | // 4) Otherwise, IS_BLOCKER is true. If we should run T soon, then |
381 | // run it now. | |
382 | ||
383 | // 5) Otherwise, check whether there are other tasks to run. If there | |
384 | // are, then we generally get a better ordering if we run those tasks | |
385 | // now, before T. A typical example is tasks waiting on the Dirsearch | |
386 | // blocker. We don't want to run those tasks right away just because | |
387 | // the Dirsearch was unblocked. | |
388 | ||
389 | // 6) Otherwise, there are no other tasks to run, so we might as well | |
390 | // run this one now. | |
391 | ||
392 | // This function must be called with the Workqueue lock held. | |
393 | ||
394 | // Return true if we set *PRET to T, false otherwise. | |
395 | ||
396 | bool | |
397 | Workqueue::return_or_queue(Task* t, bool is_blocker, Task** pret) | |
bae7f79e | 398 | { |
17a1d0a9 | 399 | Task_token* token = t->is_runnable(); |
c7912668 | 400 | |
17a1d0a9 ILT |
401 | if (token != NULL) |
402 | { | |
403 | token->add_waiting(t); | |
404 | ++this->waiting_; | |
405 | return false; | |
406 | } | |
407 | ||
408 | bool should_queue = false; | |
409 | bool should_return = false; | |
410 | ||
411 | if (*pret != NULL) | |
412 | should_queue = true; | |
413 | else if (!is_blocker) | |
414 | should_return = true; | |
415 | else if (t->should_run_soon()) | |
416 | should_return = true; | |
417 | else if (!this->first_tasks_.empty() || !this->tasks_.empty()) | |
418 | should_queue = true; | |
419 | else | |
420 | should_return = true; | |
c7912668 | 421 | |
17a1d0a9 ILT |
422 | if (should_return) |
423 | { | |
424 | gold_assert(*pret == NULL); | |
425 | *pret = t; | |
426 | return true; | |
427 | } | |
428 | else if (should_queue) | |
429 | { | |
430 | if (t->should_run_soon()) | |
431 | this->first_tasks_.push_back(t); | |
432 | else | |
433 | this->tasks_.push_back(t); | |
434 | this->condvar_.signal(); | |
435 | return false; | |
436 | } | |
437 | ||
438 | gold_unreachable(); | |
bae7f79e ILT |
439 | } |
440 | ||
17a1d0a9 ILT |
441 | // Release the locks associated with a Task. Return the first |
442 | // runnable Task that we find. If we find more runnable tasks, add | |
443 | // them to the run queue and signal any other threads. This must be | |
444 | // called with the Workqueue lock held. | |
bae7f79e | 445 | |
17a1d0a9 ILT |
446 | Task* |
447 | Workqueue::release_locks(Task* t, Task_locker* tl) | |
bae7f79e | 448 | { |
17a1d0a9 ILT |
449 | Task* ret = NULL; |
450 | for (Task_locker::iterator p = tl->begin(); p != tl->end(); ++p) | |
451 | { | |
452 | Task_token* token = *p; | |
453 | if (token->is_blocker()) | |
454 | { | |
455 | if (token->remove_blocker()) | |
456 | { | |
457 | // The token has been unblocked. Every waiting Task may | |
458 | // now be runnable. | |
2ea97941 ILT |
459 | Task* t; |
460 | while ((t = token->remove_first_waiting()) != NULL) | |
17a1d0a9 ILT |
461 | { |
462 | --this->waiting_; | |
2ea97941 | 463 | this->return_or_queue(t, true, &ret); |
17a1d0a9 ILT |
464 | } |
465 | } | |
466 | } | |
467 | else | |
468 | { | |
469 | token->remove_writer(t); | |
470 | ||
471 | // One more waiting Task may now be runnable. If we are | |
472 | // going to run it next, we can stop. Otherwise we need to | |
473 | // move all the Tasks to the runnable queue, to avoid a | |
474 | // potential deadlock if the locking status changes before | |
475 | // we run the next thread. | |
2ea97941 ILT |
476 | Task* t; |
477 | while ((t = token->remove_first_waiting()) != NULL) | |
17a1d0a9 ILT |
478 | { |
479 | --this->waiting_; | |
2ea97941 | 480 | if (this->return_or_queue(t, false, &ret)) |
17a1d0a9 ILT |
481 | break; |
482 | } | |
483 | } | |
484 | } | |
485 | return ret; | |
bae7f79e ILT |
486 | } |
487 | ||
17a1d0a9 ILT |
488 | // Process all the tasks on the workqueue. Keep going until the |
489 | // workqueue is empty, or until we have been told to exit. This | |
490 | // function is called by all threads. | |
fe9a4c12 ILT |
491 | |
492 | void | |
17a1d0a9 | 493 | Workqueue::process(int thread_number) |
fe9a4c12 | 494 | { |
17a1d0a9 ILT |
495 | while (this->find_and_run_task(thread_number)) |
496 | ; | |
fe9a4c12 ILT |
497 | } |
498 | ||
17a1d0a9 ILT |
499 | // Set the number of threads to use for the workqueue, if we are using |
500 | // threads. | |
c7912668 ILT |
501 | |
502 | void | |
17a1d0a9 | 503 | Workqueue::set_thread_count(int threads) |
c7912668 | 504 | { |
17a1d0a9 ILT |
505 | Hold_lock hl(this->lock_); |
506 | ||
507 | this->threader_->set_thread_count(threads); | |
508 | // Wake up all the threads, since something has changed. | |
509 | this->condvar_.broadcast(); | |
c7912668 ILT |
510 | } |
511 | ||
15f8229b ILT |
512 | // Add a new blocker to an existing Task_token. |
513 | ||
514 | void | |
515 | Workqueue::add_blocker(Task_token* token) | |
516 | { | |
517 | Hold_lock hl(this->lock_); | |
518 | token->add_blocker(); | |
519 | } | |
520 | ||
bae7f79e | 521 | } // End namespace gold. |