From: Ian Lance Taylor Date: Thu, 22 Nov 2007 00:05:51 +0000 (+0000) Subject: Add threading support. X-Git-Tag: sid-snapshot-20071201~53 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c79126688f8211ab17a893c5e80b09811d424fc1;p=thirdparty%2Fbinutils-gdb.git Add threading support. --- diff --git a/gold/Makefile.am b/gold/Makefile.am index a91a1c80cc8..de9be2062bf 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -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 diff --git a/gold/Makefile.in b/gold/Makefile.in index 9ca37010131..fd981d6b987 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -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@ diff --git a/gold/archive.cc b/gold/archive.cc index 1442731f045..dc12ea95701 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -475,6 +475,7 @@ Add_archive_symbols::run(Workqueue*) { // We no longer need to know about this archive. delete this->archive_; + this->archive_ = NULL; } } diff --git a/gold/archive.h b/gold/archive.h index 61d4d4b891f..57af1673563 100644 --- a/gold/archive.h +++ b/gold/archive.h @@ -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; diff --git a/gold/common.cc b/gold/common.cc index f723de30e19..3b616b154a1 100644 --- a/gold/common.cc +++ b/gold/common.cc @@ -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); diff --git a/gold/common.h b/gold/common.h index e7fe84e4c66..bd24985c9be 100644 --- a/gold/common.h +++ b/gold/common.h @@ -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; diff --git a/gold/debug.h b/gold/debug.h new file mode 100644 index 00000000000..c6bfb7ae678 --- /dev/null +++ b/gold/debug.h @@ -0,0 +1,51 @@ +// debug.h -- gold internal debugging support -*- C++ -*- + +// Copyright 2007 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// 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) diff --git a/gold/dirsearch.cc b/gold/dirsearch.cc index 45caf86daf2..dd1c7e6eb2c 100644 --- a/gold/dirsearch.cc +++ b/gold/dirsearch.cc @@ -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)) { diff --git a/gold/errors.cc b/gold/errors.cc index 2da1a2569ed..c979463dfc2 100644 --- a/gold/errors.cc +++ b/gold/errors.cc @@ -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* 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* 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* 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. diff --git a/gold/errors.h b/gold/errors.h index 545c463bad3..17b72e10b36 100644 --- a/gold/errors.h +++ b/gold/errors.h @@ -80,6 +80,10 @@ class Errors const Relocate_info* 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. diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc index 396e6c10631..4ebbdd84960 100644 --- a/gold/gold-threads.cc +++ b/gold/gold-threads.cc @@ -22,25 +22,63 @@ #include "gold.h" -#include #include #ifdef ENABLE_THREADS #include #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,187 +86,188 @@ 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) -{ -} +// Class Lock. -Lock_impl::~Lock_impl() +Lock::Lock() { - gold_assert(!this->acquired_); + if (!parameters->threads()) + this->lock_ = new Lock_impl_nothreads; + else + { +#ifdef ENABLE_THREADS + this->lock_ = new Lock_impl_threads; +#else + gold_unreachable(); +#endif + } } -void -Lock_impl::acquire() +Lock::~Lock() { - gold_assert(!this->acquired_); - this->acquired_ = true; + delete this->lock_; } -void -Lock_impl::release() +// The non-threaded version of Condvar_impl. + +class Condvar_impl_nothreads : public Condvar_impl { - gold_assert(this->acquired_); - this->acquired_ = false; -} + public: + Condvar_impl_nothreads() + { } -#endif // !defined(ENABLE_THREADS) + ~Condvar_impl_nothreads() + { } -// Methods for Lock class. + void + wait(Lock_impl* li) + { gold_assert(static_cast(li)->acquired_); } -Lock::Lock() -{ - this->lock_ = new Lock_impl; -} - -Lock::~Lock() -{ - delete this->lock_; -} + void + signal() + { } -void -Lock::acquire() -{ - this->lock_->acquire(); -} + void + broadcast() + { } +}; -void -Lock::release() -{ - this->lock_->release(); -} +#ifdef ENABLE_THREADS -// Class Condvar_impl. +// The threaded version of Condvar_impl. -class Condvar_impl +class Condvar_impl_threads : public Condvar_impl { public: - Condvar_impl(); - ~Condvar_impl(); + Condvar_impl_threads(); + ~Condvar_impl_threads(); - void wait(Lock_impl*); - void signal(); + void + wait(Lock_impl*); + + void + signal(); + + void + broadcast(); private: // This class can not be copied. - Condvar_impl(const Condvar_impl&); - Condvar_impl& operator=(const Condvar_impl&); + Condvar_impl_threads(const Condvar_impl_threads&); + Condvar_impl_threads& operator=(const Condvar_impl_threads&); -#ifdef ENABLE_THREADS pthread_cond_t cond_; -#endif }; -#ifdef ENABLE_THREADS - -Condvar_impl::Condvar_impl() -{ - if (pthread_cond_init(&this->cond_, NULL) != 0) - gold_fatal(_("pthread_cond_init failed: %s"), strerror(errno)); -} - -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_init(&this->cond_, NULL); + if (err != 0) + gold_fatal(_("pthread_cond_init failed: %s"), strerror(err)); } -void -Condvar_impl::wait(Lock_impl* li) +Condvar_impl_threads::~Condvar_impl_threads() { - if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0) - gold_fatal(_("pthread_cond_wait 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::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::wait(Lock_impl* li) { + Lock_impl_threads* lit = static_cast(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::wait(Lock_impl* li) +Condvar_impl_threads::signal() { - gold_assert(li->acquired_); + int err = pthread_cond_signal(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err)); } void -Condvar_impl::signal() +Condvar_impl_threads::broadcast() { + int err = pthread_cond_broadcast(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err)); } -#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. diff --git a/gold/gold-threads.h b/gold/gold-threads.h index a6f1752efe8..45f18ad2c5c 100644 --- a/gold/gold-threads.h +++ b/gold/gold-threads.h @@ -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--wake up at least one thread + // waiting on the condition variable. This should only be called + // when the lock is held. + void + signal() + { this->condvar_->signal(); } - // Signal the condition variable. This should only be called when - // the lock is held. + // Broadcast the condition variable--wake up all threads waiting on + // the condition variable. This should only be called when the lock + // is held. void - signal(); + broadcast() + { this->condvar_->broadcast(); } private: // This class can not be copied. diff --git a/gold/gold.cc b/gold/gold.cc index a4f145efba6..9baebaf6022 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -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. diff --git a/gold/layout.h b/gold/layout.h index 5b9f28defe6..9e0bcf72543 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -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; diff --git a/gold/options.cc b/gold/options.cc index c962188f1c2..fb7990182c9 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -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 diff --git a/gold/options.h b/gold/options.h index 48047c2aa3b..c7b08e8633f 100644 --- a/gold/options.h +++ b/gold/options.h @@ -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. diff --git a/gold/parameters.cc b/gold/parameters.cc index 7fbbf836778..cd05ffee8dd 100644 --- a/gold/parameters.cc +++ b/gold/parameters.cc @@ -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; diff --git a/gold/parameters.h b/gold/parameters.h index 3186b6d454d..4e135c61e27 100644 --- a/gold/parameters.h +++ b/gold/parameters.h @@ -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) diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index a78d65d0650..3797b1a3b22 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -54,4 +54,6 @@ tls.h version.cc workqueue.cc workqueue.h +workqueue-internal.h +workqueue-threads.cc x86_64.cc diff --git a/gold/po/gold.pot b/gold/po/gold.pot index c9e1e0d10de..73f1f015310 100644 --- a/gold/po/gold.pot +++ b/gold/po/gold.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-09 15:55-0800\n" +"POT-Creation-Date: 2007-11-20 16:37-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,47 +16,47 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: archive.cc:88 +#: archive.cc:95 #, c-format msgid "%s: no archive symbol table (run ranlib)" msgstr "" -#: archive.cc:138 +#: archive.cc:145 #, c-format msgid "%s: bad archive symbol table names" msgstr "" -#: archive.cc:168 +#: archive.cc:175 #, c-format msgid "%s: malformed archive header at %zu" msgstr "" -#: archive.cc:188 +#: archive.cc:195 #, c-format msgid "%s: malformed archive header size at %zu" msgstr "" -#: archive.cc:199 +#: archive.cc:206 #, c-format msgid "%s: malformed archive header name at %zu" msgstr "" -#: archive.cc:224 +#: archive.cc:231 #, c-format msgid "%s: bad extended name index at %zu" msgstr "" -#: archive.cc:234 +#: archive.cc:241 #, c-format msgid "%s: bad extended name entry at header %zu" msgstr "" -#: archive.cc:327 +#: archive.cc:334 #, c-format msgid "%s: short archive header at %zu" msgstr "" -#: archive.cc:378 archive.cc:392 +#: archive.cc:385 archive.cc:399 #, c-format msgid "%s: member at %zu is not an ELF object" msgstr "" @@ -66,110 +66,115 @@ msgstr "" msgid "%s: can not read directory: %s" msgstr "" -#: dynobj.cc:151 +#: dynobj.cc:145 #, c-format msgid "unexpected duplicate type %u section: %u, %u" msgstr "" -#: dynobj.cc:187 +#: dynobj.cc:181 #, c-format msgid "unexpected link in section %u header: %u != %u" msgstr "" -#: dynobj.cc:222 +#: dynobj.cc:217 #, c-format msgid "DYNAMIC section %u link out of range: %u" msgstr "" -#: dynobj.cc:230 +#: dynobj.cc:225 #, c-format msgid "DYNAMIC section %u link %u is not a strtab" msgstr "" -#: dynobj.cc:250 +#: dynobj.cc:253 #, c-format msgid "DT_SONAME value out of range: %lld >= %lld" msgstr "" #: dynobj.cc:265 +#, c-format +msgid "DT_NEEDED value out of range: %lld >= %lld" +msgstr "" + +#: dynobj.cc:278 msgid "missing DT_NULL in dynamic segment" msgstr "" -#: dynobj.cc:309 +#: dynobj.cc:322 #, c-format msgid "invalid dynamic symbol table name index: %u" msgstr "" -#: dynobj.cc:316 +#: dynobj.cc:329 #, c-format msgid "dynamic symbol table name section has wrong type: %u" msgstr "" -#: dynobj.cc:389 object.cc:234 object.cc:569 +#: dynobj.cc:402 object.cc:236 object.cc:571 #, c-format msgid "bad section name offset for section %u: %lu" msgstr "" -#: dynobj.cc:418 +#: dynobj.cc:431 #, c-format msgid "duplicate definition for version %u" msgstr "" -#: dynobj.cc:447 +#: dynobj.cc:460 #, c-format msgid "unexpected verdef version %u" msgstr "" -#: dynobj.cc:463 +#: dynobj.cc:476 #, c-format msgid "verdef vd_cnt field too small: %u" msgstr "" -#: dynobj.cc:470 +#: dynobj.cc:483 #, c-format msgid "verdef vd_aux field out of range: %u" msgstr "" -#: dynobj.cc:480 +#: dynobj.cc:493 #, c-format msgid "verdaux vda_name field out of range: %u" msgstr "" -#: dynobj.cc:489 +#: dynobj.cc:502 #, c-format msgid "verdef vd_next field out of range: %u" msgstr "" -#: dynobj.cc:522 +#: dynobj.cc:535 #, c-format msgid "unexpected verneed version %u" msgstr "" -#: dynobj.cc:531 +#: dynobj.cc:544 #, c-format msgid "verneed vn_aux field out of range: %u" msgstr "" -#: dynobj.cc:544 +#: dynobj.cc:557 #, c-format msgid "vernaux vna_name field out of range: %u" msgstr "" -#: dynobj.cc:555 +#: dynobj.cc:568 #, c-format msgid "verneed vna_next field out of range: %u" msgstr "" -#: dynobj.cc:566 +#: dynobj.cc:579 #, c-format msgid "verneed vn_next field out of range: %u" msgstr "" -#: dynobj.cc:613 +#: dynobj.cc:626 msgid "size of dynamic symbols is not multiple of symbol size" msgstr "" -#: dynobj.cc:1290 +#: dynobj.cc:1303 #, c-format msgid "symbol %s has undefined version %s" msgstr "" @@ -189,6 +194,11 @@ msgstr "" msgid "%s: %s: undefined reference to '%s'\n" msgstr "" +#: errors.cc:146 +#, c-format +msgid "%s: " +msgstr "" + #: fileread.cc:49 #, c-format msgid "munmap failed: %s" @@ -251,12 +261,12 @@ msgstr "" #. We had some input files, but we weren't able to open any of #. them. -#: gold.cc:118 gold.cc:164 +#: gold.cc:118 gold.cc:165 msgid "no input files" msgstr "" #. We print out just the first .so we see; there may be others. -#: gold.cc:179 +#: gold.cc:180 #, c-format msgid "cannot mix -static with dynamic object %s" msgstr "" @@ -266,97 +276,98 @@ msgstr "" msgid "pthead_mutextattr_init failed: %s" msgstr "" -#: gold-threads.cc:72 +#: gold-threads.cc:73 #, c-format msgid "pthread_mutextattr_settype failed: %s" msgstr "" -#: gold-threads.cc:76 +#: gold-threads.cc:78 #, c-format msgid "pthread_mutex_init failed: %s" msgstr "" -#: gold-threads.cc:79 +#: gold-threads.cc:82 #, c-format msgid "pthread_mutexattr_destroy failed: %s" msgstr "" -#: gold-threads.cc:85 +#: gold-threads.cc:89 #, c-format msgid "pthread_mutex_destroy failed: %s" msgstr "" -#: gold-threads.cc:92 +#: gold-threads.cc:97 #, c-format msgid "pthread_mutex_lock failed: %s" msgstr "" -#: gold-threads.cc:99 +#: gold-threads.cc:105 #, c-format msgid "pthread_mutex_unlock failed: %s" msgstr "" -#: gold-threads.cc:180 +#: gold-threads.cc:193 #, c-format msgid "pthread_cond_init failed: %s" msgstr "" -#: gold-threads.cc:186 +#: gold-threads.cc:200 #, c-format msgid "pthread_cond_destroy failed: %s" msgstr "" -#: gold-threads.cc:193 +#: gold-threads.cc:208 #, c-format msgid "pthread_cond_wait failed: %s" msgstr "" -#: gold-threads.cc:200 +#: gold-threads.cc:216 #, c-format msgid "pthread_cond_signal failed: %s" msgstr "" +#: gold-threads.cc:224 +#, c-format +msgid "pthread_cond_broadcast failed: %s" +msgstr "" + #. FIXME: This needs to specify the location somehow. -#: i386.cc:153 i386.cc:1310 x86_64.cc:165 x86_64.cc:1254 +#: i386.cc:160 i386.cc:1439 x86_64.cc:172 x86_64.cc:1269 msgid "missing expected TLS relocation" msgstr "" -#: i386.cc:761 x86_64.cc:723 x86_64.cc:898 +#: i386.cc:779 x86_64.cc:732 x86_64.cc:910 #, c-format msgid "%s: unsupported reloc %u against local symbol" msgstr "" -#: i386.cc:862 i386.cc:1096 x86_64.cc:839 x86_64.cc:1079 +#: i386.cc:882 i386.cc:1169 x86_64.cc:851 x86_64.cc:1093 #, c-format msgid "%s: unexpected reloc %u in object file" msgstr "" -#: i386.cc:948 x86_64.cc:912 x86_64.cc:1138 +#: i386.cc:1018 x86_64.cc:924 x86_64.cc:1152 #, c-format msgid "%s: unsupported reloc %u against global symbol %s" msgstr "" -#: i386.cc:1193 +#: i386.cc:1322 #, c-format msgid "%s: unsupported RELA reloc section" msgstr "" -#: i386.cc:1449 x86_64.cc:1452 +#: i386.cc:1579 x86_64.cc:1467 #, c-format msgid "unexpected reloc %u in object file" msgstr "" -#: i386.cc:1481 i386.cc:1528 i386.cc:1535 i386.cc:1555 i386.cc:1584 -#: x86_64.cc:1473 x86_64.cc:1522 x86_64.cc:1533 +#: i386.cc:1611 i386.cc:1687 i386.cc:1694 i386.cc:1735 i386.cc:1791 +#: x86_64.cc:1488 x86_64.cc:1537 x86_64.cc:1548 #, c-format msgid "unsupported reloc %u" msgstr "" -#: i386.cc:1506 x86_64.cc:1498 -msgid "TLS reloc but no TLS segment" -msgstr "" - -#: i386.cc:1543 +#: i386.cc:1702 msgid "both SUN and GNU model TLS relocations" msgstr "" @@ -368,550 +379,602 @@ msgstr "" msgid "entry in mergeable string section not null terminated" msgstr "" -#: object.cc:51 +#: object.cc:53 #, c-format msgid "%s: unsupported ELF machine number %d" msgstr "" -#: object.cc:69 script.cc:1222 +#: object.cc:71 script.cc:1226 #, c-format msgid "%s: %s" msgstr "" -#: object.cc:104 +#: object.cc:106 #, c-format msgid "section name section has wrong type: %u" msgstr "" -#: object.cc:306 +#: object.cc:308 #, c-format msgid "invalid symbol table name index: %u" msgstr "" -#: object.cc:312 +#: object.cc:314 #, c-format msgid "symbol table name section has wrong type: %u" msgstr "" -#: object.cc:392 +#: object.cc:394 #, c-format msgid "section group %u info %u out of range" msgstr "" -#: object.cc:410 +#: object.cc:412 #, c-format msgid "symbol %u name offset %u out of range" msgstr "" -#: object.cc:442 +#: object.cc:444 #, c-format msgid "section %u in section group %u out of range" msgstr "" -#: object.cc:532 reloc.cc:202 reloc.cc:469 +#: object.cc:534 reloc.cc:226 reloc.cc:493 #, c-format msgid "relocation section %u has bad info %u" msgstr "" -#: object.cc:704 +#: object.cc:706 msgid "size of symbols is not multiple of symbol size" msgstr "" #. FIXME: Handle SHN_XINDEX. -#: object.cc:796 +#: object.cc:798 #, c-format msgid "unknown section index %u for local symbol %u" msgstr "" -#: object.cc:805 +#: object.cc:807 #, c-format msgid "local symbol %u section index %u out of range" msgstr "" -#: object.cc:837 +#: object.cc:839 #, c-format msgid "local symbol %u section name out of range: %u >= %u" msgstr "" -#: object.cc:1055 +#: object.cc:1070 #, c-format msgid "%s: incompatible target" msgstr "" -#: object.cc:1213 +#: object.cc:1226 #, c-format msgid "%s: unsupported ELF file type %d" msgstr "" -#: object.cc:1232 object.cc:1278 object.cc:1312 +#: object.cc:1245 object.cc:1291 object.cc:1325 #, c-format msgid "%s: ELF file too short" msgstr "" -#: object.cc:1240 +#: object.cc:1253 #, c-format msgid "%s: invalid ELF version 0" msgstr "" -#: object.cc:1242 +#: object.cc:1255 #, c-format msgid "%s: unsupported ELF version %d" msgstr "" -#: object.cc:1249 +#: object.cc:1262 #, c-format msgid "%s: invalid ELF class 0" msgstr "" -#: object.cc:1255 +#: object.cc:1268 #, c-format msgid "%s: unsupported ELF class %d" msgstr "" -#: object.cc:1262 +#: object.cc:1275 #, c-format msgid "%s: invalid ELF data encoding" msgstr "" -#: object.cc:1268 +#: object.cc:1281 #, c-format msgid "%s: unsupported ELF data encoding %d" msgstr "" -#: object.cc:1288 +#: object.cc:1301 #, c-format msgid "%s: not configured to support 32-bit big-endian object" msgstr "" -#: object.cc:1301 +#: object.cc:1314 #, c-format msgid "%s: not configured to support 32-bit little-endian object" msgstr "" -#: object.cc:1322 +#: object.cc:1335 #, c-format msgid "%s: not configured to support 64-bit big-endian object" msgstr "" -#: object.cc:1335 +#: object.cc:1348 #, c-format msgid "%s: not configured to support 64-bit little-endian object" msgstr "" -#: options.cc:142 +#: options.cc:157 #, c-format msgid "%s: unable to parse script file %s\n" msgstr "" -#: options.cc:170 +#: options.cc:185 #, c-format msgid "" "Usage: %s [options] file...\n" "Options:\n" msgstr "" -#: options.cc:341 +#: options.cc:356 +msgid "Allow unresolved references in shared libraries" +msgstr "" + +#: options.cc:360 +msgid "Do not allow unresolved references in shared libraries" +msgstr "" + +#: options.cc:364 msgid "Only set DT_NEEDED for dynamic libs if used" msgstr "" -#: options.cc:344 +#: options.cc:367 msgid "Always DT_NEEDED for dynamic libs (default)" msgstr "" -#: options.cc:347 +#: options.cc:370 msgid "-l searches for shared libraries" msgstr "" -#: options.cc:351 +#: options.cc:374 msgid "-l does not search for shared libraries" msgstr "" -#: options.cc:354 +#: options.cc:377 msgid "Bind defined symbols locally" msgstr "" -#: options.cc:356 +#: options.cc:379 +msgid "Demangle C++ symbols in log messages" +msgstr "" + +#: options.cc:382 +msgid "Do not demangle C++ symbols in log messages" +msgstr "" + +#: options.cc:385 +msgid "Try to detect violations of the One Definition Rule" +msgstr "" + +#: options.cc:387 msgid "Export all dynamic symbols" msgstr "" -#: options.cc:358 +#: options.cc:389 msgid "Create exception frame header" msgstr "" -#: options.cc:360 +#: options.cc:391 msgid "Set dynamic linker path" msgstr "" -#: options.cc:361 +#: options.cc:392 msgid "-I PROGRAM, --dynamic-linker PROGRAM" msgstr "" -#: options.cc:363 +#: options.cc:394 msgid "Search for library LIBNAME" msgstr "" -#: options.cc:364 +#: options.cc:395 msgid "-lLIBNAME, --library LIBNAME" msgstr "" -#: options.cc:366 +#: options.cc:397 msgid "Add directory to search path" msgstr "" -#: options.cc:367 +#: options.cc:398 msgid "-L DIR, --library-path DIR" msgstr "" -#: options.cc:369 +#: options.cc:400 msgid "Ignored for compatibility" msgstr "" -#: options.cc:371 +#: options.cc:402 msgid "Set output file name" msgstr "" -#: options.cc:372 +#: options.cc:403 msgid "-o FILE, --output FILE" msgstr "" -#: options.cc:374 +#: options.cc:405 msgid "Optimize output file size" msgstr "" -#: options.cc:375 +#: options.cc:406 msgid "-O level" msgstr "" -#: options.cc:377 +#: options.cc:408 msgid "Generate relocatable output" msgstr "" -#: options.cc:379 +#: options.cc:410 msgid "Add DIR to runtime search path" msgstr "" -#: options.cc:380 +#: options.cc:411 msgid "-R DIR, -rpath DIR" msgstr "" -#: options.cc:383 +#: options.cc:414 msgid "Add DIR to link time shared library search path" msgstr "" -#: options.cc:384 +#: options.cc:415 msgid "--rpath-link DIR" msgstr "" -#: options.cc:386 +#: options.cc:417 msgid "Strip all symbols" msgstr "" -#: options.cc:388 +#: options.cc:420 +msgid "Strip debug symbols that are unused by gdb (at least versions <= 6.7)" +msgstr "" + +#. This must come after -Sdebug since it's a prefix of it. +#: options.cc:424 msgid "Strip debugging information" msgstr "" -#: options.cc:390 +#: options.cc:426 msgid "Generate shared library" msgstr "" -#: options.cc:392 +#: options.cc:428 msgid "Do not link against shared libraries" msgstr "" -#: options.cc:394 +#: options.cc:430 msgid "Print resource usage statistics" msgstr "" -#: options.cc:396 +#: options.cc:432 msgid "Set target system root directory" msgstr "" -#: options.cc:397 +#: options.cc:433 msgid "--sysroot DIR" msgstr "" -#: options.cc:398 +#: options.cc:434 msgid "Set the address of the .text section" msgstr "" -#: options.cc:399 +#: options.cc:435 msgid "-Ttext ADDRESS" msgstr "" #. This must come after -Ttext since it's a prefix of it. -#: options.cc:402 +#: options.cc:438 msgid "Read linker script" msgstr "" -#: options.cc:403 +#: options.cc:439 msgid "-T FILE, --script FILE" msgstr "" -#: options.cc:405 +#: options.cc:441 msgid "Run the linker multi-threaded" msgstr "" -#: options.cc:407 +#: options.cc:443 msgid "Do not run the linker multi-threaded" msgstr "" -#: options.cc:409 +#: options.cc:445 msgid "Number of threads to use" msgstr "" -#: options.cc:410 +#: options.cc:446 msgid "--thread-count COUNT" msgstr "" -#: options.cc:413 +#: options.cc:449 msgid "Number of threads to use in initial pass" msgstr "" -#: options.cc:414 +#: options.cc:450 msgid "--thread-count-initial COUNT" msgstr "" -#: options.cc:417 +#: options.cc:453 msgid "Number of threads to use in middle pass" msgstr "" -#: options.cc:418 +#: options.cc:454 msgid "--thread-count-middle COUNT" msgstr "" -#: options.cc:421 +#: options.cc:457 msgid "Number of threads to use in final pass" msgstr "" -#: options.cc:422 +#: options.cc:458 msgid "--thread-count-final COUNT" msgstr "" -#: options.cc:425 +#: options.cc:461 msgid "Include all archive contents" msgstr "" -#: options.cc:429 +#: options.cc:465 msgid "Include only needed archive contents" msgstr "" -#: options.cc:434 +#: options.cc:470 msgid "" "Subcommands as follows:\n" " -z execstack Mark output as requiring executable stack\n" " -z noexecstack Mark output as not requiring executable stack" msgstr "" -#: options.cc:437 +#: options.cc:473 msgid "-z SUBCOMMAND" msgstr "" -#: options.cc:440 +#: options.cc:476 msgid "Start a library search group" msgstr "" -#: options.cc:442 +#: options.cc:478 msgid "End a library search group" msgstr "" -#: options.cc:444 +#: options.cc:480 msgid "Report usage information" msgstr "" -#: options.cc:446 +#: options.cc:482 msgid "Report version information" msgstr "" -#: options.cc:518 +#: options.cc:484 +msgid "Turn on debugging (all,task)" +msgstr "" + +#: options.cc:485 +msgid "--debug=TYPE" +msgstr "" + +#: options.cc:578 #, c-format msgid "%s: unrecognized -z subcommand: %s\n" msgstr "" -#: options.cc:698 +#: options.cc:601 +#, c-format +msgid "%s: unrecognized --debug subcommand: %s\n" +msgstr "" + +#: options.cc:781 msgid "unexpected argument" msgstr "" -#: options.cc:705 options.cc:757 options.cc:838 +#: options.cc:788 options.cc:840 options.cc:921 msgid "missing argument" msgstr "" -#: options.cc:718 options.cc:766 +#: options.cc:801 options.cc:849 msgid "unknown option" msgstr "" -#: options.cc:784 +#: options.cc:867 #, c-format msgid "%s: missing group end\n" msgstr "" -#: options.cc:912 +#: options.cc:995 msgid "may not nest groups" msgstr "" -#: options.cc:922 +#: options.cc:1005 msgid "group end without group start" msgstr "" -#: options.cc:932 +#: options.cc:1015 #, c-format msgid "%s: use the --help option for usage information\n" msgstr "" -#: options.cc:941 +#: options.cc:1024 #, c-format msgid "%s: %s: %s\n" msgstr "" -#: options.cc:950 +#: options.cc:1033 #, c-format msgid "%s: -%c: %s\n" msgstr "" -#: options.h:338 +#: options.h:393 #, c-format msgid "%s: invalid argument to -Ttext: %s\n" msgstr "" -#: options.h:351 +#: options.h:406 #, c-format msgid "%s: invalid thread count: %s\n" msgstr "" -#: output.cc:1061 +#: output.cc:1108 #, c-format msgid "invalid alignment %lu for section \"%s\"" msgstr "" -#: output.cc:1787 +#: output.cc:1870 #, c-format msgid "%s: open: %s" msgstr "" -#: output.cc:1792 +#: output.cc:1875 #, c-format msgid "%s: lseek: %s" msgstr "" -#: output.cc:1795 +#: output.cc:1878 #, c-format msgid "%s: write: %s" msgstr "" -#: output.cc:1801 +#: output.cc:1884 #, c-format msgid "%s: mmap: %s" msgstr "" -#: output.cc:1811 +#: output.cc:1894 #, c-format msgid "%s: munmap: %s" msgstr "" -#: output.cc:1815 +#: output.cc:1898 #, c-format msgid "%s: close: %s" msgstr "" -#: readsyms.cc:147 +#: readsyms.cc:151 #, c-format msgid "%s: file is empty" msgstr "" -#: readsyms.cc:182 +#: readsyms.cc:186 #, c-format msgid "%s: ordinary object found in input group" msgstr "" #. Here we have to handle any other input file types we need. -#: readsyms.cc:230 +#: readsyms.cc:234 #, c-format msgid "%s: not an object or archive" msgstr "" -#: reloc.cc:221 reloc.cc:487 +#: reloc.cc:245 reloc.cc:511 #, c-format msgid "relocation section %u uses unexpected symbol table %u" msgstr "" -#: reloc.cc:236 reloc.cc:505 +#: reloc.cc:260 reloc.cc:529 #, c-format msgid "unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:245 reloc.cc:514 +#: reloc.cc:269 reloc.cc:538 #, c-format msgid "reloc section %u size %lu uneven" msgstr "" -#: reloc.cc:702 +#: reloc.cc:729 #, c-format msgid "reloc section size %zu is not a multiple of reloc size %d\n" msgstr "" -#: resolve.cc:165 -#, c-format -msgid "%s: invalid STB_LOCAL symbol %s in external symbols" +#. We should only see externally visible symbols in the symbol +#. table. +#: resolve.cc:144 +msgid "invalid STB_LOCAL symbol in external symbols" msgstr "" -#: resolve.cc:171 -#, c-format -msgid "%s: unsupported symbol binding %d for symbol %s" +#. Any target which wants to handle STB_LOOS, etc., needs to +#. define a resolve method. +#: resolve.cc:150 +msgid "unsupported symbol binding" msgstr "" #. Two definitions of the same symbol. #. FIXME: Do a better job of reporting locations. -#: resolve.cc:310 +#: resolve.cc:318 #, c-format msgid "%s: multiple definition of %s" msgstr "" -#: resolve.cc:311 resolve.cc:316 +#: resolve.cc:319 resolve.cc:324 msgid "command line" msgstr "" -#: resolve.cc:313 +#: resolve.cc:321 #, c-format msgid "%s: previous definition here" msgstr "" #. There are some options that we could handle here--e.g., #. -lLIBRARY. Should we bother? -#: script.cc:1326 +#: script.cc:1330 #, c-format msgid "" "%s: Ignoring command OPTION; OPTION is only valid for scripts specified via -" "T" msgstr "" -#: symtab.cc:541 +#: symtab.cc:576 #, c-format msgid "bad global symbol name offset %u at %zu" msgstr "" -#: symtab.cc:619 +#: symtab.cc:654 msgid "too few symbol versions" msgstr "" -#: symtab.cc:648 +#: symtab.cc:683 #, c-format msgid "bad symbol name offset %u at %zu" msgstr "" -#: symtab.cc:702 +#: symtab.cc:737 #, c-format msgid "versym for symbol %zu out of range: %u" msgstr "" -#: symtab.cc:710 +#: symtab.cc:745 #, c-format msgid "versym for symbol %zu has no name: %u" msgstr "" -#: symtab.cc:1428 symtab.cc:1631 +#: symtab.cc:1463 symtab.cc:1676 #, c-format msgid "%s: unsupported symbol section 0x%x" msgstr "" +#: symtab.cc:1800 +#, c-format +msgid "%s: undefined reference to '%s'" +msgstr "" + +#: symtab.cc:1941 +#, c-format +msgid "" +"while linking %s: symbol '%s' defined in multiple places (possible ODR " +"violation):" +msgstr "" + #: target-reloc.h:211 #, c-format msgid "reloc has bad offset %zu" @@ -940,12 +1003,21 @@ msgid "" "This program has absolutely no warranty.\n" msgstr "" -#: x86_64.cc:1162 +#: workqueue-threads.cc:106 +#, c-format +msgid "%s failed: %s" +msgstr "" + +#: x86_64.cc:1177 #, c-format msgid "%s: unsupported REL reloc section" msgstr "" -#: x86_64.cc:1561 +#: x86_64.cc:1513 +msgid "TLS reloc but no TLS segment" +msgstr "" + +#: x86_64.cc:1576 #, c-format msgid "unsupported reloc type %u" msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 39d33767033..5625f59f0e6 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -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() diff --git a/gold/readsyms.h b/gold/readsyms.h index d88446309dc..c02a0ee4672 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -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_; diff --git a/gold/reloc.cc b/gold/reloc.cc index ab74498d244..d50674f6d77 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -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. diff --git a/gold/reloc.h b/gold/reloc.h index 2ff49d84bf0..d84dc88828b 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -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; diff --git a/gold/script.cc b/gold/script.cc index 83e490c2fcb..1ebd100b932 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -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_; diff --git a/gold/symtab.cc b/gold/symtab.cc index f0c09f9924d..b2901ab9faa 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -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 @@ -219,6 +230,16 @@ Sized_symbol::init(const char* name, Value_type value, Size_type symsize, this->symsize_ = symsize; } +// Allocate a common symbol. + +template +void +Sized_symbol::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 diff --git a/gold/symtab.h b/gold/symtab.h index b6e550463ee..9c7fb0935c7 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -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&); diff --git a/gold/workqueue-internal.h b/gold/workqueue-internal.h new file mode 100644 index 00000000000..d9fd1608502 --- /dev/null +++ b/gold/workqueue-internal.h @@ -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 . + +// 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 + +#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_queue_entry; + + // A queue of tasks to run. + typedef std::queue 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) diff --git a/gold/workqueue-threads.cc b/gold/workqueue-threads.cc new file mode 100644 index 00000000000..a4f347de5de --- /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 . + +// 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 +#include + +#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(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(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) diff --git a/gold/workqueue.cc b/gold/workqueue.cc index 95c14ce5a85..018c96bdbd2 100644 --- a/gold/workqueue.cc +++ b/gold/workqueue.cc @@ -22,11 +22,9 @@ #include "gold.h" -#ifdef ENABLE_THREADS -#include -#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; - - 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; - } + Hold_lock hl(this->completed_lock_); + --this->queued_; } - // We're going around again, so recompute ALL_BLOCKED. - *all_blocked = true; + return t; } + + if (is_runnable != Task::IS_BLOCKED) + *all_blocked = false; + + 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. diff --git a/gold/workqueue.h b/gold/workqueue.h index 777b3aa0918..e435739c4ad 100644 --- a/gold/workqueue.h +++ b/gold/workqueue.h @@ -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_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 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.