Add threading support.

This commit is contained in:
Ian Lance Taylor 2007-11-22 00:05:51 +00:00
parent 06d063c072
commit c79126688f
31 changed files with 1494 additions and 488 deletions

View File

@ -53,7 +53,8 @@ CCFILES = \
stringpool.cc \
target-select.cc \
version.cc \
workqueue.cc
workqueue.cc \
workqueue-threads.cc
HFILES = \
archive.h \
@ -84,7 +85,8 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
workqueue.h
workqueue.h \
workqueue-internal.h
YFILES = \
yyscript.y

View File

@ -78,7 +78,8 @@ am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
output.$(OBJEXT) parameters.$(OBJEXT) readsyms.$(OBJEXT) \
reloc.$(OBJEXT) resolve.$(OBJEXT) script.$(OBJEXT) \
symtab.$(OBJEXT) stringpool.$(OBJEXT) target-select.$(OBJEXT) \
version.$(OBJEXT) workqueue.$(OBJEXT)
version.$(OBJEXT) workqueue.$(OBJEXT) \
workqueue-threads.$(OBJEXT)
am__objects_2 =
am__objects_3 = yyscript.$(OBJEXT)
am_libgold_a_OBJECTS = $(am__objects_1) $(am__objects_2) \
@ -307,7 +308,8 @@ CCFILES = \
stringpool.cc \
target-select.cc \
version.cc \
workqueue.cc
workqueue.cc \
workqueue-threads.cc
HFILES = \
archive.h \
@ -338,7 +340,8 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
workqueue.h
workqueue.h \
workqueue-internal.h
YFILES = \
yyscript.y
@ -487,6 +490,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_64.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yyscript.Po@am__quote@

View File

@ -475,6 +475,7 @@ Add_archive_symbols::run(Workqueue*)
{
// We no longer need to know about this archive.
delete this->archive_;
this->archive_ = NULL;
}
}

View File

@ -187,6 +187,14 @@ class Add_archive_symbols : public Task
void
run(Workqueue*);
std::string
get_name() const
{
if (this->archive_ == NULL)
return "Add_archive_symbols";
return "Add_archive_symbols " + this->archive_->file().filename();
}
private:
class Add_archive_symbols_locker;

View File

@ -226,12 +226,9 @@ Symbol_table::do_allocate_commons(const General_options&,
off = align_address(off, ssym->value());
Size_type symsize = ssym->symsize();
ssym->init(ssym->name(), poc, off, symsize, ssym->type(),
ssym->binding(), ssym->visibility(), ssym->nonvis(),
false);
ssym->allocate_common(poc, off);
off += symsize;
off += ssym->symsize();
}
poc->set_space_size(off);

View File

@ -54,6 +54,10 @@ class Allocate_commons_task : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Allocate_commons_task"; }
private:
class Allocate_commons_locker;

51
gold/debug.h Normal file
View File

@ -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)

View File

@ -158,7 +158,7 @@ Dir_caches::lookup(const char* dirname) const
// The caches.
Dir_caches caches;
Dir_caches* caches;
// A Task to read the directory.
@ -169,11 +169,18 @@ class Dir_cache_task : public gold::Task
: dir_(dir), token_(token)
{ }
Is_runnable_type is_runnable(gold::Workqueue*);
Is_runnable_type
is_runnable(gold::Workqueue*);
gold::Task_locker* locks(gold::Workqueue*);
gold::Task_locker*
locks(gold::Workqueue*);
void run(gold::Workqueue*);
void
run(gold::Workqueue*);
std::string
get_name() const
{ return std::string("Dir_cache_task ") + this->dir_; }
private:
const char* dir_;
@ -202,7 +209,7 @@ Dir_cache_task::locks(gold::Workqueue* workqueue)
void
Dir_cache_task::run(gold::Workqueue*)
{
caches.add(this->dir_);
caches->add(this->dir_);
}
}
@ -214,6 +221,8 @@ void
Dirsearch::initialize(Workqueue* workqueue,
const General_options::Dir_list* directories)
{
gold_assert(caches == NULL);
caches = new Dir_caches;
this->directories_ = directories;
for (General_options::Dir_list::const_iterator p = directories->begin();
p != directories->end();
@ -235,7 +244,7 @@ Dirsearch::find(const std::string& n1, const std::string& n2,
p != this->directories_->end();
++p)
{
Dir_cache* pdc = caches.lookup(p->name().c_str());
Dir_cache* pdc = caches->lookup(p->name().c_str());
gold_assert(pdc != NULL);
if (pdc->find(n1))
{

View File

@ -39,11 +39,20 @@ namespace gold
const int Errors::max_undefined_error_report;
Errors::Errors(const char* program_name)
: program_name_(program_name), lock_(), error_count_(0), warning_count_(0),
undefined_symbols_()
: program_name_(program_name), lock_(NULL), error_count_(0),
warning_count_(0), undefined_symbols_()
{
}
// Initialize the lock_ field.
void
Errors::initialize_lock()
{
if (this->lock_ == NULL)
this->lock_ = new Lock;
}
// Report a fatal error.
void
@ -63,8 +72,10 @@ Errors::error(const char* format, va_list args)
fprintf(stderr, "%s: ", this->program_name_);
vfprintf(stderr, format, args);
fputc('\n', stderr);
this->initialize_lock();
{
Hold_lock h(this->lock_);
Hold_lock h(*this->lock_);
++this->error_count_;
}
}
@ -77,8 +88,10 @@ Errors::warning(const char* format, va_list args)
fprintf(stderr, _("%s: warning: "), this->program_name_);
vfprintf(stderr, format, args);
fputc('\n', stderr);
this->initialize_lock();
{
Hold_lock h(this->lock_);
Hold_lock h(*this->lock_);
++this->warning_count_;
}
}
@ -95,8 +108,10 @@ Errors::error_at_location(const Relocate_info<size, big_endian>* relinfo,
relinfo->location(relnum, reloffset).c_str());
vfprintf(stderr, format, args);
fputc('\n', stderr);
this->initialize_lock();
{
Hold_lock h(this->lock_);
Hold_lock h(*this->lock_);
++this->error_count_;
}
}
@ -113,8 +128,10 @@ Errors::warning_at_location(const Relocate_info<size, big_endian>* relinfo,
relinfo->location(relnum, reloffset).c_str());
vfprintf(stderr, format, args);
fputc('\n', stderr);
this->initialize_lock();
{
Hold_lock h(this->lock_);
Hold_lock h(*this->lock_);
++this->warning_count_;
}
}
@ -127,8 +144,9 @@ Errors::undefined_symbol(const Symbol* sym,
const Relocate_info<size, big_endian>* relinfo,
size_t relnum, off_t reloffset)
{
this->initialize_lock();
{
Hold_lock h(this->lock_);
Hold_lock h(*this->lock_);
if (++this->undefined_symbols_[sym] >= max_undefined_error_report)
return;
++this->error_count_;
@ -138,6 +156,20 @@ Errors::undefined_symbol(const Symbol* sym,
sym->demangled_name().c_str());
}
// Issue a debugging message.
void
Errors::debug(const char* format, ...)
{
fprintf(stderr, _("%s: "), this->program_name_);
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fputc('\n', stderr);
}
// The functions which the rest of the code actually calls.

View File

@ -80,6 +80,10 @@ class Errors
const Relocate_info<size, big_endian>* relinfo,
size_t relnum, off_t reloffset);
// Report a debugging message.
void
debug(const char* format, ...) ATTRIBUTE_PRINTF_2;
// Return the number of errors.
int
error_count() const
@ -89,6 +93,12 @@ class Errors
Errors(const Errors&);
Errors& operator=(const Errors&);
// Initialize the lock. We don't do this in the constructor because
// lock initialization wants to know whether we are using threads or
// not.
void
initialize_lock();
// The number of times we report an undefined symbol.
static const int max_undefined_error_report = 5;
@ -96,7 +106,7 @@ class Errors
const char* program_name_;
// This class can be accessed from multiple threads. This lock is
// used to control access to the data structures.
Lock lock_;
Lock* lock_;
// Numbers of errors reported.
int error_count_;
// Number of warnings reported.

View File

@ -22,25 +22,63 @@
#include "gold.h"
#include <cerrno>
#include <cstring>
#ifdef ENABLE_THREADS
#include <pthread.h>
#endif
#include "parameters.h"
#include "gold-threads.h"
namespace gold
{
// Class Lock_impl.
class Condvar_impl_nothreads;
class Lock_impl
// The non-threaded version of Lock_impl.
class Lock_impl_nothreads : public Lock_impl
{
public:
Lock_impl();
~Lock_impl();
Lock_impl_nothreads()
: acquired_(false)
{ }
~Lock_impl_nothreads()
{ gold_assert(!this->acquired_); }
void
acquire()
{
gold_assert(!this->acquired_);
this->acquired_ = true;
}
void
release()
{
gold_assert(this->acquired_);
this->acquired_ = false;
}
private:
friend class Condvar_impl_nothreads;
bool acquired_;
};
#ifdef ENABLE_THREADS
class Condvar_impl_threads;
// The threaded version of Lock_impl.
class Lock_impl_threads : public Lock_impl
{
public:
Lock_impl_threads();
~Lock_impl_threads();
void acquire();
@ -48,90 +86,74 @@ class Lock_impl
private:
// This class can not be copied.
Lock_impl(const Lock_impl&);
Lock_impl& operator=(const Lock_impl&);
Lock_impl_threads(const Lock_impl_threads&);
Lock_impl_threads& operator=(const Lock_impl_threads&);
friend class Condvar_impl;
friend class Condvar_impl_threads;
#ifdef ENABLE_THREADS
pthread_mutex_t mutex_;
#else
bool acquired_;
#endif
};
#ifdef ENABLE_THREADS
Lock_impl::Lock_impl()
Lock_impl_threads::Lock_impl_threads()
{
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0)
gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(errno));
int err = pthread_mutexattr_init(&attr);
if (err != 0)
gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(err));
#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP
if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0)
gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(errno));
err = pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
if (err != 0)
gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(err));
#endif
if (pthread_mutex_init (&this->mutex_, &attr) != 0)
gold_fatal(_("pthread_mutex_init failed: %s"), strerror(errno));
err = pthread_mutex_init (&this->mutex_, &attr);
if (err != 0)
gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err));
if (pthread_mutexattr_destroy(&attr) != 0)
gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(errno));
err = pthread_mutexattr_destroy(&attr);
if (err != 0)
gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(err));
}
Lock_impl::~Lock_impl()
Lock_impl_threads::~Lock_impl_threads()
{
if (pthread_mutex_destroy(&this->mutex_) != 0)
gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(errno));
int err = pthread_mutex_destroy(&this->mutex_);
if (err != 0)
gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(err));
}
void
Lock_impl::acquire()
Lock_impl_threads::acquire()
{
if (pthread_mutex_lock(&this->mutex_) != 0)
gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(errno));
int err = pthread_mutex_lock(&this->mutex_);
if (err != 0)
gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err));
}
void
Lock_impl::release()
Lock_impl_threads::release()
{
if (pthread_mutex_unlock(&this->mutex_) != 0)
gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(errno));
int err = pthread_mutex_unlock(&this->mutex_);
if (err != 0)
gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err));
}
#else // !defined(ENABLE_THREADS)
#endif // defined(ENABLE_THREADS)
Lock_impl::Lock_impl()
: acquired_(false)
{
}
Lock_impl::~Lock_impl()
{
gold_assert(!this->acquired_);
}
void
Lock_impl::acquire()
{
gold_assert(!this->acquired_);
this->acquired_ = true;
}
void
Lock_impl::release()
{
gold_assert(this->acquired_);
this->acquired_ = false;
}
#endif // !defined(ENABLE_THREADS)
// Methods for Lock class.
// Class Lock.
Lock::Lock()
{
this->lock_ = new Lock_impl;
if (!parameters->threads())
this->lock_ = new Lock_impl_nothreads;
else
{
#ifdef ENABLE_THREADS
this->lock_ = new Lock_impl_threads;
#else
gold_unreachable();
#endif
}
}
Lock::~Lock()
@ -139,96 +161,113 @@ Lock::~Lock()
delete this->lock_;
}
void
Lock::acquire()
{
this->lock_->acquire();
}
// The non-threaded version of Condvar_impl.
void
Lock::release()
{
this->lock_->release();
}
// Class Condvar_impl.
class Condvar_impl
class Condvar_impl_nothreads : public Condvar_impl
{
public:
Condvar_impl();
~Condvar_impl();
Condvar_impl_nothreads()
{ }
void wait(Lock_impl*);
void signal();
~Condvar_impl_nothreads()
{ }
private:
// This class can not be copied.
Condvar_impl(const Condvar_impl&);
Condvar_impl& operator=(const Condvar_impl&);
void
wait(Lock_impl* li)
{ gold_assert(static_cast<Lock_impl_nothreads*>(li)->acquired_); }
#ifdef ENABLE_THREADS
pthread_cond_t cond_;
#endif
void
signal()
{ }
void
broadcast()
{ }
};
#ifdef ENABLE_THREADS
Condvar_impl::Condvar_impl()
// The threaded version of Condvar_impl.
class Condvar_impl_threads : public Condvar_impl
{
if (pthread_cond_init(&this->cond_, NULL) != 0)
gold_fatal(_("pthread_cond_init failed: %s"), strerror(errno));
public:
Condvar_impl_threads();
~Condvar_impl_threads();
void
wait(Lock_impl*);
void
signal();
void
broadcast();
private:
// This class can not be copied.
Condvar_impl_threads(const Condvar_impl_threads&);
Condvar_impl_threads& operator=(const Condvar_impl_threads&);
pthread_cond_t cond_;
};
Condvar_impl_threads::Condvar_impl_threads()
{
int err = pthread_cond_init(&this->cond_, NULL);
if (err != 0)
gold_fatal(_("pthread_cond_init failed: %s"), strerror(err));
}
Condvar_impl::~Condvar_impl()
Condvar_impl_threads::~Condvar_impl_threads()
{
if (pthread_cond_destroy(&this->cond_) != 0)
gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(errno));
int err = pthread_cond_destroy(&this->cond_);
if (err != 0)
gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(err));
}
void
Condvar_impl::wait(Lock_impl* li)
Condvar_impl_threads::wait(Lock_impl* li)
{
if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0)
gold_fatal(_("pthread_cond_wait failed: %s"), strerror(errno));
Lock_impl_threads* lit = static_cast<Lock_impl_threads*>(li);
int err = pthread_cond_wait(&this->cond_, &lit->mutex_);
if (err != 0)
gold_fatal(_("pthread_cond_wait failed: %s"), strerror(err));
}
void
Condvar_impl::signal()
{
if (pthread_cond_signal(&this->cond_) != 0)
gold_fatal(_("pthread_cond_signal failed: %s"), strerror(errno));
}
#else // !defined(ENABLE_THREADS)
Condvar_impl::Condvar_impl()
{
}
Condvar_impl::~Condvar_impl()
Condvar_impl_threads::signal()
{
int err = pthread_cond_signal(&this->cond_);
if (err != 0)
gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err));
}
void
Condvar_impl::wait(Lock_impl* li)
Condvar_impl_threads::broadcast()
{
gold_assert(li->acquired_);
int err = pthread_cond_broadcast(&this->cond_);
if (err != 0)
gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err));
}
void
Condvar_impl::signal()
{
}
#endif // !defined(ENABLE_THREADS)
#endif // defined(ENABLE_THREADS)
// Methods for Condvar class.
Condvar::Condvar(Lock& lock)
: lock_(lock)
{
this->condvar_ = new Condvar_impl;
if (!parameters->threads())
this->condvar_ = new Condvar_impl_nothreads;
else
{
#ifdef ENABLE_THREADS
this->condvar_ = new Condvar_impl_threads;
#else
gold_unreachable();
#endif
}
}
Condvar::~Condvar()
@ -236,16 +275,4 @@ Condvar::~Condvar()
delete this->condvar_;
}
void
Condvar::wait()
{
this->condvar_->wait(this->lock_.get_impl());
}
void
Condvar::signal()
{
this->condvar_->signal();
}
} // End namespace gold.

View File

@ -34,24 +34,45 @@
namespace gold
{
class Lock_impl;
class Condvar;
// The interface for the implementation of a Lock.
class Lock_impl
{
public:
Lock_impl()
{ }
virtual
~Lock_impl()
{ }
virtual void
acquire() = 0;
virtual void
release() = 0;
};
// A simple lock class.
class Lock
{
public:
Lock();
~Lock();
// Acquire the lock.
void
acquire();
acquire()
{ this->lock_->acquire(); }
// Release the lock.
void
release();
release()
{ this->lock_->release(); }
private:
// This class can not be copied.
@ -86,7 +107,27 @@ class Hold_lock
Lock& lock_;
};
class Condvar_impl;
// The interface for the implementation of a condition variable.
class Condvar_impl
{
public:
Condvar_impl()
{ }
virtual
~Condvar_impl()
{ }
virtual void
wait(Lock_impl*) = 0;
virtual void
signal() = 0;
virtual void
broadcast() = 0;
};
// A simple condition variable class. It is always associated with a
// specific lock.
@ -100,12 +141,22 @@ class Condvar
// Wait for the condition variable to be signalled. This should
// only be called when the lock is held.
void
wait();
wait()
{ this->condvar_->wait(this->lock_.get_impl()); }
// Signal the condition variable. This should only be called when
// the lock is held.
// Signal the condition variable--wake up at least one thread
// waiting on the condition variable. This should only be called
// when the lock is held.
void
signal();
signal()
{ this->condvar_->signal(); }
// Broadcast the condition variable--wake up all threads waiting on
// the condition variable. This should only be called when the lock
// is held.
void
broadcast()
{ this->condvar_->broadcast(); }
private:
// This class can not be copied.

View File

@ -143,7 +143,8 @@ queue_initial_tasks(const General_options& options,
input_objects,
symtab,
layout),
this_blocker));
this_blocker,
"Task_function Middle_runner"));
}
// Queue up the middle set of tasks. These are the tasks which run
@ -239,7 +240,8 @@ queue_middle_tasks(const General_options& options,
input_objects,
symtab,
layout),
blocker));
blocker,
"Task_function Layout_task_runner"));
}
// Queue up the final set of tasks. This is called at the end of
@ -312,7 +314,8 @@ queue_final_tasks(const General_options& options,
// Queue a task to close the output file. This will be blocked by
// FINAL_BLOCKER.
workqueue->queue(new Task_function(new Close_task_runner(of),
final_blocker));
final_blocker,
"Task_function Close_task_runner"));
}
} // End namespace gold.

View File

@ -463,6 +463,10 @@ class Write_sections_task : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Write_sections_task"; }
private:
class Write_sections_locker;
@ -494,6 +498,10 @@ class Write_data_task : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Write_data_task"; }
private:
const Layout* layout_;
const Symbol_table* symtab_;
@ -525,6 +533,10 @@ class Write_symbols_task : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Write_symbols_task"; }
private:
const Symbol_table* symtab_;
const Input_objects* input_objects_;
@ -561,6 +573,10 @@ class Write_after_input_sections_task : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Write_after_input_sections_task"; }
private:
class Write_sections_locker;

View File

@ -28,6 +28,7 @@
#include "filenames.h"
#include "libiberty.h"
#include "debug.h"
#include "options.h"
namespace gold
@ -106,6 +107,17 @@ struct options::One_z_option
void (General_options::*set)();
};
// We have a separate table for --debug options.
struct options::One_debug_option
{
// The name of the option.
const char* name;
// The flags to turn on.
unsigned int debug_flags;
};
class options::Command_line_options
{
public:
@ -113,6 +125,8 @@ class options::Command_line_options
static const int options_size;
static const One_z_option z_options[];
static const int z_options_size;
static const One_debug_option debug_options[];
static const int debug_options_size;
};
} // End namespace gold.
@ -247,7 +261,7 @@ help(int, char**, char*, bool, gold::Command_line*)
std::puts(options[i].doc);
}
::exit(0);
::exit(EXIT_SUCCESS);
return 0;
}
@ -258,7 +272,7 @@ int
version(int, char**, char* opt, bool, gold::Command_line*)
{
gold::print_version(opt[0] == 'v' && opt[1] == '\0');
::exit(0);
::exit(EXIT_SUCCESS);
return 0;
}
@ -466,7 +480,10 @@ options::Command_line_options::options[] =
SPECIAL('\0', "help", N_("Report usage information"), NULL,
TWO_DASHES, &help),
SPECIAL('v', "version", N_("Report version information"), NULL,
TWO_DASHES, &version)
TWO_DASHES, &version),
GENERAL_ARG('\0', "debug", N_("Turn on debugging (all,task)"),
N_("--debug=TYPE"), TWO_DASHES,
&General_options::handle_debug_option)
};
const int options::Command_line_options::options_size =
@ -484,6 +501,18 @@ options::Command_line_options::z_options[] =
const int options::Command_line_options::z_options_size =
sizeof(z_options) / sizeof(z_options[0]);
// The --debug options.
const options::One_debug_option
options::Command_line_options::debug_options[] =
{
{ "all", DEBUG_ALL },
{ "task", DEBUG_TASK },
};
const int options::Command_line_options::debug_options_size =
sizeof(debug_options) / sizeof(debug_options[0]);
// The default values for the general options.
General_options::General_options()
@ -509,7 +538,8 @@ General_options::General_options()
thread_count_initial_(0),
thread_count_middle_(0),
thread_count_final_(0),
execstack_(EXECSTACK_FROM_INPUT)
execstack_(EXECSTACK_FROM_INPUT),
debug_(0)
{
// We initialize demangle_ based on the environment variable
// COLLECT_NO_DEMANGLE. The gcc collect2 program will demangle the
@ -547,7 +577,30 @@ General_options::handle_z_option(const char* arg)
fprintf(stderr, _("%s: unrecognized -z subcommand: %s\n"),
program_name, arg);
::exit(1);
::exit(EXIT_FAILURE);
}
// Handle the --debug option.
void
General_options::handle_debug_option(const char* arg)
{
const int debug_options_size =
options::Command_line_options::debug_options_size;
const gold::options::One_debug_option* debug_options =
options::Command_line_options::debug_options;
for (int i = 0; i < debug_options_size; ++i)
{
if (strcmp(arg, debug_options[i].name) == 0)
{
this->set_debug(debug_options[i].debug_flags);
return;
}
}
fprintf(stderr, _("%s: unrecognized --debug subcommand: %s\n"),
program_name, arg);
::exit(EXIT_FAILURE);
}
// Add the sysroot, if any, to the search paths.
@ -961,7 +1014,7 @@ Command_line::usage()
fprintf(stderr,
_("%s: use the --help option for usage information\n"),
program_name);
::exit(1);
::exit(EXIT_FAILURE);
}
void

View File

@ -46,11 +46,13 @@ class Command_line;
class Input_file_group;
class Position_dependent_options;
namespace options {
namespace options
{
class Command_line_options;
struct One_option;
struct One_z_option;
struct One_debug_option;
} // End namespace gold::options.
@ -249,6 +251,11 @@ class General_options
is_stack_executable() const
{ return this->execstack_ == EXECSTACK_YES; }
// --debug
unsigned int
debug() const
{ return this->debug_; }
private:
// Don't copy this structure.
General_options(const General_options&);
@ -444,10 +451,18 @@ class General_options
set_noexecstack()
{ this->execstack_ = EXECSTACK_NO; }
void
set_debug(unsigned int flags)
{ this->debug_ = flags; }
// Handle the -z option.
void
handle_z_option(const char*);
// Handle the --debug option.
void
handle_debug_option(const char*);
// Apply any sysroot to the directory lists.
void
add_sysroot();
@ -476,6 +491,7 @@ class General_options
int thread_count_middle_;
int thread_count_final_;
Execstack execstack_;
unsigned int debug_;
};
// The current state of the position dependent options.

View File

@ -31,11 +31,11 @@ namespace gold
// Initialize the parameters from the options.
Parameters::Parameters(Errors* errors)
: errors_(errors), output_file_name_(NULL),
: errors_(errors), threads_(false), output_file_name_(NULL),
output_file_type_(OUTPUT_INVALID), sysroot_(),
strip_(STRIP_INVALID), allow_shlib_undefined_(false),
symbolic_(false), demangle_(false), detect_odr_violations_(false),
optimization_level_(0), export_dynamic_(false),
optimization_level_(0), export_dynamic_(false), debug_(0),
is_doing_static_link_valid_(false), doing_static_link_(false),
is_size_and_endian_valid_(false), size_(0), is_big_endian_(false)
{
@ -46,6 +46,7 @@ Parameters::Parameters(Errors* errors)
void
Parameters::set_from_options(const General_options* options)
{
this->threads_ = options->threads();
this->output_file_name_ = options->output_file_name();
this->sysroot_ = options->sysroot();
this->allow_shlib_undefined_ = options->allow_shlib_undefined();
@ -54,6 +55,7 @@ Parameters::set_from_options(const General_options* options)
this->detect_odr_violations_ = options->detect_odr_violations();
this->optimization_level_ = options->optimization_level();
this->export_dynamic_ = options->export_dynamic();
this->debug_ = options->debug();
if (options->is_shared())
this->output_file_type_ = OUTPUT_SHARED;

View File

@ -47,6 +47,14 @@ class Parameters
errors() const
{ return this->errors_; }
// Whether to use threads.
bool
threads() const
{
gold_assert(this->options_valid_);
return this->threads_;
}
// Return the output file name.
const char*
output_file_name() const
@ -166,6 +174,15 @@ class Parameters
return this->export_dynamic_;
}
// Return the debug flags. These are the flags for which we should
// report internal debugging information.
unsigned int
debug() const
{
gold_assert(this->options_valid_);
return this->debug_;
}
// Whether we are doing a static link--a link in which none of the
// input files are shared libraries. This is only known after we
// have seen all the input files.
@ -239,6 +256,8 @@ class Parameters
// Whether the fields set from the options are valid.
bool options_valid_;
// Whether to use threads.
bool threads_;
// The output file name.
const char* output_file_name_;
// The type of the output file.
@ -259,6 +278,8 @@ class Parameters
int optimization_level_;
// Whether the -E/--export-dynamic flag is set.
bool export_dynamic_;
// The debug flags.
unsigned int debug_;
// Whether the doing_static_link_ field is valid.
bool is_doing_static_link_valid_;
@ -287,6 +308,13 @@ extern void set_parameters_size_and_endianness(int size, bool is_big_endian);
// Set whether we are doing a static link.
extern void set_parameters_doing_static_link(bool doing_static_link);
// Return whether we are doing a particular debugging type. The
// argument is one of the flags from debug.h.
inline bool
is_debugging_enabled(unsigned int type)
{ return (parameters->debug() & type) != 0; }
} // End namespace gold.
#endif // !defined(GOLD_PARAMETERS_H)

View File

@ -54,4 +54,6 @@ tls.h
version.cc
workqueue.cc
workqueue.h
workqueue-internal.h
workqueue-threads.cc
x86_64.cc

File diff suppressed because it is too large Load Diff

View File

@ -72,6 +72,10 @@ class Unblock_token : public Task
run(Workqueue*)
{ }
std::string
get_name() const
{ return "Unblock_token"; }
private:
Task_token* this_blocker_;
Task_token* next_blocker_;
@ -273,6 +277,35 @@ Read_symbols::do_group(Workqueue* workqueue)
this->next_blocker_));
}
// Return a debugging name for a Read_symbols task.
std::string
Read_symbols::get_name() const
{
if (!this->input_argument_->is_group())
{
std::string ret("Read_symbols ");
if (this->input_argument_->file().is_lib())
ret += "-l";
ret += this->input_argument_->file().name();
return ret;
}
std::string ret("Read_symbols group (");
bool add_space = false;
const Input_file_group* group = this->input_argument_->group();
for (Input_file_group::const_iterator p = group->begin();
p != group->end();
++p)
{
if (add_space)
ret += ' ';
ret += p->file().name();
add_space = true;
}
return ret + ')';
}
// Class Add_symbols.
Add_symbols::~Add_symbols()

View File

@ -77,6 +77,9 @@ class Read_symbols : public Task
void
run(Workqueue*);
std::string
get_name() const;
private:
// Handle an archive group.
void
@ -129,6 +132,10 @@ class Add_symbols : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Add_symbols " + this->object_->name(); }
private:
class Add_symbols_locker;
@ -201,6 +208,10 @@ class Finish_group : public Task
void
run(Workqueue*);
std::string
get_name() const
{ return "Finish_group"; }
private:
Input_objects* input_objects_;
Symbol_table* symtab_;

View File

@ -63,6 +63,14 @@ Read_relocs::run(Workqueue* workqueue)
this->symtab_lock_, this->blocker_));
}
// Return a debugging name for the task.
std::string
Read_relocs::get_name() const
{
return "Read_relocs " + this->object_->name();
}
// Scan_relocs methods.
// These tasks scan the relocations read by Read_relocs and mark up
@ -114,6 +122,14 @@ Scan_relocs::run(Workqueue*)
this->rd_ = NULL;
}
// Return a debugging name for the task.
std::string
Scan_relocs::get_name() const
{
return "Scan_relocs " + this->object_->name();
}
// Relocate_task methods.
// We may have to wait for the output sections to be written.
@ -125,6 +141,9 @@ Relocate_task::is_runnable(Workqueue*)
&& this->output_sections_blocker_->is_blocked())
return IS_BLOCKED;
if (this->object_->is_locked())
return IS_LOCKED;
return IS_RUNNABLE;
}
@ -166,6 +185,14 @@ Relocate_task::run(Workqueue*)
this->of_);
}
// Return a debugging name for the task.
std::string
Relocate_task::get_name() const
{
return "Relocate_task " + this->object_->name();
}
// Read the relocs and local symbols from the object file and store
// the information in RD.

View File

@ -78,6 +78,9 @@ class Read_relocs : public Task
void
run(Workqueue*);
std::string
get_name() const;
private:
const General_options& options_;
Symbol_table* symtab_;
@ -113,6 +116,9 @@ class Scan_relocs : public Task
void
run(Workqueue*);
std::string
get_name() const;
private:
class Scan_relocs_locker;
@ -151,6 +157,9 @@ class Relocate_task : public Task
void
run(Workqueue*);
std::string
get_name() const;
private:
class Relocate_locker;

View File

@ -815,6 +815,10 @@ class Script_unblock : public Task
run(Workqueue*)
{ }
std::string
get_name() const
{ return "Script_unblock"; }
private:
Task_token* this_blocker_;
Task_token* next_blocker_;

View File

@ -159,6 +159,17 @@ Symbol::init_base(const char* name, elfcpp::STT type,
this->in_reg_ = true;
}
// Allocate a common symbol in the base.
void
Symbol::allocate_base_common(Output_data* od)
{
gold_assert(this->is_common());
this->source_ = IN_OUTPUT_DATA;
this->u_.in_output_data.output_data = od;
this->u_.in_output_data.offset_is_from_end = false;
}
// Initialize the fields in Sized_symbol for SYM in OBJECT.
template<int size>
@ -219,6 +230,16 @@ Sized_symbol<size>::init(const char* name, Value_type value, Size_type symsize,
this->symsize_ = symsize;
}
// Allocate a common symbol.
template<int size>
void
Sized_symbol<size>::allocate_common(Output_data* od, Value_type value)
{
this->allocate_base_common(od);
this->value_ = value;
}
// Return true if this symbol should be added to the dynamic symbol
// table.
@ -2017,6 +2038,18 @@ Warnings::issue_warning(const Symbol* sym,
// script to restrict this to only the ones needed for implemented
// targets.
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
template
void
Sized_symbol<32>::allocate_common(Output_data*, Value_type);
#endif
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
template
void
Sized_symbol<64>::allocate_common(Output_data*, Value_type);
#endif
#ifdef HAVE_TARGET_32_LITTLE
template
void

View File

@ -598,6 +598,11 @@ class Symbol
void
override_base_with_special(const Symbol* from);
// Allocate a common symbol by giving it a location in the output
// file.
void
allocate_base_common(Output_data*);
private:
Symbol(const Symbol&);
Symbol& operator=(const Symbol&);
@ -798,6 +803,11 @@ class Sized_symbol : public Symbol
set_value(Value_type value)
{ this->value_ = value; }
// Allocate a common symbol by giving it a location in the output
// file.
void
allocate_common(Output_data*, Value_type value);
private:
Sized_symbol(const Sized_symbol&);
Sized_symbol& operator=(const Sized_symbol&);

129
gold/workqueue-internal.h Normal file
View File

@ -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)

266
gold/workqueue-threads.cc Normal file
View File

@ -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)

View File

@ -22,11 +22,9 @@
#include "gold.h"
#ifdef ENABLE_THREADS
#include <pthread.h>
#endif
#include "debug.h"
#include "workqueue.h"
#include "workqueue-internal.h"
namespace gold
{
@ -145,38 +143,6 @@ Task_block_token::~Task_block_token()
}
}
// The Workqueue_runner abstract class.
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 simple single-threaded implementation of Workqueue_runner.
class Workqueue_runner_single : public Workqueue_runner
@ -212,12 +178,15 @@ Workqueue_runner_single::set_thread_count(int thread_count)
Workqueue::Workqueue(const General_options& options)
: tasks_lock_(),
first_tasks_(),
tasks_(),
completed_lock_(),
completed_(),
running_(0),
queued_(0),
completed_condvar_(this->completed_lock_),
cleared_blockers_(0)
cleared_blockers_(0),
desired_thread_count_(1)
{
bool threads = options.threads();
#ifndef ENABLE_THREADS
@ -226,11 +195,18 @@ Workqueue::Workqueue(const General_options& options)
if (!threads)
this->runner_ = new Workqueue_runner_single(this);
else
gold_unreachable();
{
#ifdef ENABLE_THREADS
this->runner_ = new Workqueue_runner_threadpool(this);
#else
gold_unreachable();
#endif
}
}
Workqueue::~Workqueue()
{
gold_assert(this->first_tasks_.empty());
gold_assert(this->tasks_.empty());
gold_assert(this->completed_.empty());
gold_assert(this->running_ == 0);
@ -241,8 +217,14 @@ Workqueue::~Workqueue()
void
Workqueue::queue(Task* t)
{
Hold_lock hl(this->tasks_lock_);
this->tasks_.push_back(t);
{
Hold_lock hl(this->tasks_lock_);
this->tasks_.push_back(t);
}
{
Hold_lock hl(this->completed_lock_);
++this->queued_;
}
}
// Add a task to the front of the queue.
@ -250,8 +232,14 @@ Workqueue::queue(Task* t)
void
Workqueue::queue_front(Task* t)
{
Hold_lock hl(this->tasks_lock_);
this->tasks_.push_front(t);
{
Hold_lock hl(this->tasks_lock_);
this->first_tasks_.push_front(t);
}
{
Hold_lock hl(this->completed_lock_);
++this->queued_;
}
}
// Clear the list of completed tasks. Return whether we cleared
@ -277,48 +265,36 @@ Workqueue::clear_completed()
// a blocker.
Task*
Workqueue::find_runnable(Task_list& tasks, bool* all_blocked)
Workqueue::find_runnable(Task_list* tasks, bool* all_blocked)
{
Task* tlast = tasks.back();
Task* tlast = tasks->back();
*all_blocked = true;
while (true)
Task* t;
do
{
Task* t = tasks.front();
tasks.pop_front();
t = tasks->front();
tasks->pop_front();
Task::Is_runnable_type is_runnable = t->is_runnable(this);
if (is_runnable == Task::IS_RUNNABLE)
return t;
{
{
Hold_lock hl(this->completed_lock_);
--this->queued_;
}
return t;
}
if (is_runnable != Task::IS_BLOCKED)
*all_blocked = false;
tasks.push_back(t);
if (t == tlast)
{
// We couldn't find any runnable task. If there are any
// completed tasks, free their locks and try again.
{
Hold_lock hl2(this->completed_lock_);
if (!this->clear_completed())
{
// There had better be some tasks running, or we will
// never find a runnable task.
gold_assert(this->running_ > 0);
// We couldn't find any runnable tasks, and we
// couldn't release any locks.
return NULL;
}
}
// We're going around again, so recompute ALL_BLOCKED.
*all_blocked = true;
}
tasks->push_back(t);
}
while (t != tlast);
// We couldn't find any runnable task.
return NULL;
}
// Process all the tasks on the workqueue. This is the main loop in
@ -334,28 +310,56 @@ Workqueue::process()
bool empty;
bool all_blocked;
// Don't start more tasks than desired.
{
Hold_lock hl(this->completed_lock_);
this->clear_completed();
while (this->running_ >= this->desired_thread_count_)
{
this->completed_condvar_.wait();
this->clear_completed();
}
}
{
Hold_lock hl(this->tasks_lock_);
if (this->tasks_.empty())
bool first_empty;
bool all_blocked_first;
if (this->first_tasks_.empty())
{
t = NULL;
empty = true;
all_blocked = false;
first_empty = true;
all_blocked_first = false;
}
else
{
t = this->find_runnable(this->tasks_, &all_blocked);
t = this->find_runnable(&this->first_tasks_, &all_blocked_first);
empty = false;
first_empty = false;
}
if (t == NULL)
{
if (this->tasks_.empty())
all_blocked = false;
else
{
t = this->find_runnable(&this->tasks_, &all_blocked);
if (!first_empty && !all_blocked_first)
all_blocked = false;
empty = false;
}
}
}
// If T != NULL, it is a task we can run.
// If T == NULL && empty, then there are no tasks waiting to
// be run at this level.
// be run.
// If T == NULL && !empty, then there tasks waiting to be
// run at this level, but they are waiting for something to
// unlock.
// run, but they are waiting for something to unlock.
if (t != NULL)
this->run(t);
@ -371,10 +375,16 @@ Workqueue::process()
if (all_blocked)
{
this->cleared_blockers_ = 0;
int queued = this->queued_;
this->clear_completed();
while (this->cleared_blockers_ == 0)
while (this->cleared_blockers_ == 0
&& queued == this->queued_)
{
gold_assert(this->running_ > 0);
if (this->running_ <= 0)
{
this->show_queued_tasks();
gold_unreachable();
}
this->completed_condvar_.wait();
this->clear_completed();
}
@ -416,7 +426,12 @@ Workqueue::process()
void
Workqueue::run(Task* t)
{
++this->running_;
gold_debug(DEBUG_TASK, "starting task %s", t->name().c_str());
{
Hold_lock hl(this->completed_lock_);
++this->running_;
}
this->runner_->run(t, t->locks(this));
}
@ -427,6 +442,8 @@ Workqueue::run(Task* t)
void
Workqueue::completed(Task* t, Task_locker* tl)
{
gold_debug(DEBUG_TASK, "completed task %s", t->name().c_str());
{
Hold_lock hl(this->completed_lock_);
gold_assert(this->running_ > 0);
@ -434,6 +451,7 @@ Workqueue::completed(Task* t, Task_locker* tl)
this->completed_.push_back(tl);
this->completed_condvar_.signal();
}
delete t;
}
@ -452,7 +470,40 @@ Workqueue::cleared_blocker()
void
Workqueue::set_thread_count(int threads)
{
gold_assert(threads > 0);
this->desired_thread_count_ = threads;
this->runner_->set_thread_count(threads);
}
// Dump the list of queued tasks and their current state, for
// debugging purposes.
void
Workqueue::show_queued_tasks()
{
fprintf(stderr, _("gold task queue:\n"));
Hold_lock hl(this->tasks_lock_);
for (Task_list::const_iterator p = this->tasks_.begin();
p != this->tasks_.end();
++p)
{
fprintf(stderr, " %s ", (*p)->name().c_str());
switch ((*p)->is_runnable(this))
{
case Task::IS_RUNNABLE:
fprintf(stderr, "runnable");
break;
case Task::IS_BLOCKED:
fprintf(stderr, "blocked");
break;
case Task::IS_LOCKED:
fprintf(stderr, "locked");
break;
default:
gold_unreachable();
}
putc('\n', stderr);
}
}
} // End namespace gold.

View File

@ -275,6 +275,7 @@ class Task
{
public:
Task()
: name_()
{ }
virtual ~Task()
{ }
@ -307,9 +308,29 @@ class Task
virtual void
run(Workqueue*) = 0;
// Return the name of the Task. This is only used for debugging
// purposes.
const std::string&
name()
{
if (this->name_.empty())
this->name_ = this->get_name();
return this->name_;
}
protected:
// Get the name of the task. This must be implemented by the child
// class.
virtual std::string
get_name() const = 0;
private:
// This task may not be copied.
Task(const Task&);
Task& operator=(const Task&);
// Task name, for debugging purposes.
std::string name_;
};
// A simple task which waits for a blocker and then runs a function.
@ -329,8 +350,9 @@ class Task_function : public Task
public:
// Both points should be allocated using new, and will be deleted
// after the task runs.
Task_function(Task_function_runner* runner, Task_token* blocker)
: runner_(runner), blocker_(blocker)
Task_function(Task_function_runner* runner, Task_token* blocker,
const char* name)
: runner_(runner), blocker_(blocker), name_(name)
{ }
~Task_function()
@ -356,12 +378,18 @@ class Task_function : public Task
run(Workqueue* workqueue)
{ this->runner_->run(workqueue); }
// The debugging name.
std::string
get_name() const
{ return this->name_; }
private:
Task_function(const Task_function&);
Task_function& operator=(const Task_function&);
Task_function_runner* runner_;
Task_token* blocker_;
const char* name_;
};
// The workqueue
@ -403,39 +431,56 @@ class Workqueue
typedef std::list<Task*> Task_list;
// Run a task.
void run(Task*);
void
run(Task*);
friend class Workqueue_runner;
// Find a runnable task.
Task* find_runnable(Task_list&, bool*);
Task*
find_runnable(Task_list*, bool*);
// Add a lock to the completed queue.
void completed(Task*, Task_locker*);
void
completed(Task*, Task_locker*);
// Clear the completed queue.
bool clear_completed();
bool
clear_completed();
// Print the list of queued tasks.
void
show_queued_tasks();
// How to run a task. Only accessed from main thread.
Workqueue_runner* runner_;
// Lock for access to tasks_ members.
Lock tasks_lock_;
// List of tasks to execute at each link level.
// List of tasks to execute soon.
Task_list first_tasks_;
// List of tasks to execute after the ones in first_tasks_.
Task_list tasks_;
// Lock for access to completed_ and running_ members.
// Lock for access to completed_, running_, and queued_.
Lock completed_lock_;
// List of Task_locker objects for main thread to free.
std::list<Task_locker*> completed_;
// Number of tasks currently running.
int running_;
// Number of tasks currently on queue (both first_tasks_ and
// tasks_).
int queued_;
// Condition variable signalled when a new entry is added to completed_.
Condvar completed_condvar_;
// Number of blocker tokens which were fully cleared. Only accessed
// from main thread.
int cleared_blockers_;
// The desired thread count. Only set by the main thread or by a
// singleton thread. Only accessed from the main thread.
int desired_thread_count_;
};
} // End namespace gold.