31 changed files with 1443 additions and 437 deletions
@ -0,0 +1,51 @@ |
|||
// debug.h -- gold internal debugging support -*- C++ -*-
|
|||
|
|||
// 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.
|
|||
|
|||
#ifndef GOLD_DEBUG_H |
|||
#define GOLD_DEBUG_H |
|||
|
|||
#include "parameters.h" |
|||
#include "errors.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
// The different types of debugging we support. These are bitflags.
|
|||
|
|||
const int DEBUG_TASK = 1; |
|||
|
|||
const int DEBUG_ALL = DEBUG_TASK; |
|||
|
|||
// Print a debug message if TYPE is enabled. This is a macro so that
|
|||
// we only evaluate the arguments if necessary.
|
|||
|
|||
#define gold_debug(TYPE, FORMAT, ...) \ |
|||
do \ |
|||
{ \ |
|||
if (is_debugging_enabled(TYPE)) \ |
|||
parameters->errors()->debug(FORMAT, __VA_ARGS__); \ |
|||
} \ |
|||
while (0) |
|||
|
|||
} // End namespace gold.
|
|||
|
|||
#endif // !defined(GOLD_DEBUG_H)
|
|||
File diff suppressed because it is too large
@ -0,0 +1,129 @@ |
|||
// workqueue-internal.h -- internal work queue header for gold -*- C++ -*-
|
|||
|
|||
// Copyright 2006, 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.
|
|||
|
|||
#ifndef GOLD_WORKQUEUE_INTERNAL_H |
|||
#define GOLD_WORKQUEUE_INTERNAL_H |
|||
|
|||
#include <queue> |
|||
|
|||
#include "gold-threads.h" |
|||
#include "workqueue.h" |
|||
|
|||
// This is an internal header file for different gold workqueue
|
|||
// implementations.
|
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
class Workqueue_thread; |
|||
|
|||
// The Workqueue_runner abstract class. This is the interface used by
|
|||
// the general workqueue code to actually run a task.
|
|||
|
|||
class Workqueue_runner |
|||
{ |
|||
public: |
|||
Workqueue_runner(Workqueue* workqueue) |
|||
: workqueue_(workqueue) |
|||
{ } |
|||
virtual ~Workqueue_runner() |
|||
{ } |
|||
|
|||
// Run a task. This is always called in the main thread.
|
|||
virtual void |
|||
run(Task*, Task_locker*) = 0; |
|||
|
|||
// Set the number of threads to use. This is ignored when not using
|
|||
// threads.
|
|||
virtual void |
|||
set_thread_count(int) = 0; |
|||
|
|||
protected: |
|||
// This is called by an implementation when a task is completed.
|
|||
void completed(Task* t, Task_locker* tl) |
|||
{ this->workqueue_->completed(t, tl); } |
|||
|
|||
Workqueue* get_workqueue() const |
|||
{ return this->workqueue_; } |
|||
|
|||
private: |
|||
Workqueue* workqueue_; |
|||
}; |
|||
|
|||
// The threaded instantiation of Workqueue_runner.
|
|||
|
|||
class Workqueue_runner_threadpool : public Workqueue_runner |
|||
{ |
|||
public: |
|||
Workqueue_runner_threadpool(Workqueue* workqueue); |
|||
|
|||
~Workqueue_runner_threadpool(); |
|||
|
|||
void |
|||
run(Task*, Task_locker*); |
|||
|
|||
void |
|||
set_thread_count(int); |
|||
|
|||
private: |
|||
// This class can not be copied.
|
|||
Workqueue_runner_threadpool(const Workqueue_runner_threadpool&); |
|||
Workqueue_runner_threadpool& operator=(const Workqueue_runner_threadpool&); |
|||
|
|||
// Return the next Task and Task_locker to run. This returns false
|
|||
// if the calling thread should simply exit.
|
|||
bool |
|||
get_next(Task**, Task_locker**); |
|||
|
|||
// This is called when the thread completes a task.
|
|||
void |
|||
thread_completed(Task*, Task_locker*); |
|||
|
|||
// The Workqueue_thread class calls functions from this and from the
|
|||
// parent Workqueue_runner.
|
|||
friend class Workqueue_thread; |
|||
|
|||
// An entry on the queue of tasks to run.
|
|||
typedef std::pair<Task*, Task_locker*> Task_queue_entry; |
|||
|
|||
// A queue of tasks to run.
|
|||
typedef std::queue<Task_queue_entry> Task_queue; |
|||
|
|||
// The number of threads we want to create. This is only changed in
|
|||
// the main thread or when only one thread is running. This is set
|
|||
// to zero when all threads should exit.
|
|||
int desired_thread_count_; |
|||
// A lock controlling access to the remaining fields.
|
|||
Lock lock_; |
|||
// The number of threads we have created.
|
|||
int actual_thread_count_; |
|||
// The number of threads which are running a task.
|
|||
int running_thread_count_; |
|||
// A queue of tasks to run.
|
|||
Task_queue task_queue_; |
|||
// A condition variable which signals when the task_queue_ changed.
|
|||
Condvar task_queue_condvar_; |
|||
}; |
|||
|
|||
} // End namespace gold.
|
|||
|
|||
#endif // !defined(GOLD_WORKQUEUE_INTERNAL_H)
|
|||
@ -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)
|
|||
Loading…
Reference in new issue