summaryrefslogtreecommitdiff
path: root/gold/workqueue-threads.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-11-22 00:05:51 +0000
committerIan Lance Taylor <iant@google.com>2007-11-22 00:05:51 +0000
commitc79126688f8211ab17a893c5e80b09811d424fc1 (patch)
tree23a727f6718dc203a4e3b9ef1575e8a10b6f0d80 /gold/workqueue-threads.cc
parent06d063c072d0e247751535bc5e394aa7b8be3b0f (diff)
Add threading support.
Diffstat (limited to 'gold/workqueue-threads.cc')
-rw-r--r--gold/workqueue-threads.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/gold/workqueue-threads.cc b/gold/workqueue-threads.cc
new file mode 100644
index 0000000000..a4f347de5d
--- /dev/null
+++ b/gold/workqueue-threads.cc
@@ -0,0 +1,266 @@
+// workqueue-threads.cc -- the threaded workqueue for gold
+
+// Copyright 2007 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// This file holds the workqueue implementation which may be used when
+// using threads.
+
+#include "gold.h"
+
+#ifdef ENABLE_THREADS
+
+#include <cstring>
+#include <pthread.h>
+
+#include "debug.h"
+#include "gold-threads.h"
+#include "workqueue.h"
+#include "workqueue-internal.h"
+
+namespace gold
+{
+
+// Class Workqueue_thread represents a single thread. Creating an
+// instance of this spawns a new thread.
+
+class Workqueue_thread
+{
+ public:
+ Workqueue_thread(Workqueue_runner_threadpool*);
+
+ ~Workqueue_thread();
+
+ private:
+ // This class can not be copied.
+ Workqueue_thread(const Workqueue_thread&);
+ Workqueue_thread& operator=(const Workqueue_thread&);
+
+ // Check for error from a pthread function.
+ void
+ check(const char* function, int err) const;
+
+ // A function to pass to pthread_create. This is called with a
+ // pointer to an instance of this object.
+ static void*
+ thread_body(void*);
+
+ // The main loop of the thread.
+ void
+ run();
+
+ // A pointer to the threadpool that this thread is part of.
+ Workqueue_runner_threadpool* threadpool_;
+ // The thread ID.
+ pthread_t tid_;
+};
+
+// Create the thread in the constructor.
+
+Workqueue_thread::Workqueue_thread(Workqueue_runner_threadpool* threadpool)
+ : threadpool_(threadpool)
+{
+ pthread_attr_t attr;
+ int err = pthread_attr_init(&attr);
+ this->check("pthread_attr_init", err);
+
+ err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ this->check("pthread_attr_setdetachstate", err);
+
+ err = pthread_create(&this->tid_, &attr, &Workqueue_thread::thread_body,
+ reinterpret_cast<void*>(this));
+ this->check("pthread_create", err);
+
+ err = pthread_attr_destroy(&attr);
+ this->check("pthread_attr_destroy", err);
+}
+
+// The destructor will be called when the thread is exiting.
+
+Workqueue_thread::~Workqueue_thread()
+{
+}
+
+// Check for an error.
+
+void
+Workqueue_thread::check(const char* function, int err) const
+{
+ if (err != 0)
+ gold_fatal(_("%s failed: %s"), function, strerror(err));
+}
+
+// Passed to pthread_create.
+
+extern "C"
+void*
+Workqueue_thread::thread_body(void* arg)
+{
+ Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg);
+ pwt->run();
+
+ // Delete the thread object as we exit.
+ delete pwt;
+
+ return NULL;
+}
+
+// This is the main loop of a worker thread. It picks up a new Task
+// and runs it.
+
+void
+Workqueue_thread::run()
+{
+ Workqueue_runner_threadpool* threadpool = this->threadpool_;
+ Workqueue* workqueue = threadpool->get_workqueue();
+
+ while (true)
+ {
+ Task* t;
+ Task_locker* tl;
+ if (!threadpool->get_next(&t, &tl))
+ return;
+
+ gold_debug(DEBUG_TASK, "running task %s", t->name().c_str());
+
+ t->run(workqueue);
+ threadpool->thread_completed(t, tl);
+ }
+}
+
+// Class Workqueue_runner_threadpool.
+
+// Constructor.
+
+Workqueue_runner_threadpool::Workqueue_runner_threadpool(Workqueue* workqueue)
+ : Workqueue_runner(workqueue),
+ desired_thread_count_(0),
+ lock_(),
+ actual_thread_count_(0),
+ running_thread_count_(0),
+ task_queue_(),
+ task_queue_condvar_(this->lock_)
+{
+}
+
+// Destructor.
+
+Workqueue_runner_threadpool::~Workqueue_runner_threadpool()
+{
+ // Tell the threads to exit.
+ Hold_lock hl(this->lock_);
+ this->desired_thread_count_ = 0;
+ this->task_queue_condvar_.broadcast();
+}
+
+// Run a task. This doesn't actually run the task: it pushes on the
+// queue of tasks to run. This is always called in the main thread.
+
+void
+Workqueue_runner_threadpool::run(Task* t, Task_locker* tl)
+{
+ Hold_lock hl(this->lock_);
+
+ // This is where we create threads as needed, subject to the limit
+ // of the desired thread count.
+ gold_assert(this->desired_thread_count_ > 0);
+ gold_assert(this->actual_thread_count_ >= this->running_thread_count_);
+ if (this->actual_thread_count_ == this->running_thread_count_
+ && this->actual_thread_count_ < this->desired_thread_count_)
+ {
+ // Note that threads delete themselves when they exit, so we
+ // don't keep pointers to them.
+ new Workqueue_thread(this);
+ ++this->actual_thread_count_;
+ }
+
+ this->task_queue_.push(std::make_pair(t, tl));
+ this->task_queue_condvar_.signal();
+}
+
+// Set the thread count. This is only called in the main thread, and
+// is only called when there are no threads running.
+
+void
+Workqueue_runner_threadpool::set_thread_count(int thread_count)
+{
+ gold_assert(this->running_thread_count_ <= 1);
+ gold_assert(thread_count > 0);
+ this->desired_thread_count_ = thread_count;
+}
+
+// Get the next task to run. This is always called by an instance of
+// Workqueue_thread, and is never called in the main thread. It
+// returns false if the calling thread should exit.
+
+bool
+Workqueue_runner_threadpool::get_next(Task** pt, Task_locker** ptl)
+{
+ Hold_lock hl(this->lock_);
+
+ // This is where we destroy threads, by telling them to exit.
+ gold_assert(this->actual_thread_count_ > this->running_thread_count_);
+ if (this->actual_thread_count_ > this->desired_thread_count_)
+ {
+ --this->actual_thread_count_;
+ return false;
+ }
+
+ while (this->task_queue_.empty() && this->desired_thread_count_ > 0)
+ {
+ // Wait for a new task to become available.
+ this->task_queue_condvar_.wait();
+ }
+
+ // Check whether we are exiting.
+ if (this->desired_thread_count_ == 0)
+ {
+ gold_assert(this->actual_thread_count_ > 0);
+ --this->actual_thread_count_;
+ return false;
+ }
+
+ *pt = this->task_queue_.front().first;
+ *ptl = this->task_queue_.front().second;
+ this->task_queue_.pop();
+
+ ++this->running_thread_count_;
+
+ return true;
+}
+
+// This is called when a thread completes its task.
+
+void
+Workqueue_runner_threadpool::thread_completed(Task* t, Task_locker* tl)
+{
+ {
+ Hold_lock hl(this->lock_);
+ gold_assert(this->actual_thread_count_ > 0);
+ gold_assert(this->running_thread_count_ > 0);
+ --this->running_thread_count_;
+ }
+
+ this->completed(t, tl);
+}
+
+} // End namespace gold.
+
+#endif // defined(ENABLE_THREADS)