diff options
author | Ian Lance Taylor <iant@google.com> | 2007-12-14 19:00:21 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2007-12-14 19:00:21 +0000 |
commit | 17a1d0a9b26ce8f4f71073c41483baa0c10ed83b (patch) | |
tree | 3cdd95751145e2cf1cbcaedee2df8790c86b935d | |
parent | 7004837e8d2e02ee35c50d236681e9c30a283619 (diff) | |
download | fsf-binutils-gdb-17a1d0a9b26ce8f4f71073c41483baa0c10ed83b.zip fsf-binutils-gdb-17a1d0a9b26ce8f4f71073c41483baa0c10ed83b.tar.gz fsf-binutils-gdb-17a1d0a9b26ce8f4f71073c41483baa0c10ed83b.tar.bz2 |
Rewrite workqueue. This version eliminates the master thread, and
reduces the amount of locking required to find a new thread to run.
-rw-r--r-- | gold/Makefile.am | 1 | ||||
-rw-r--r-- | gold/Makefile.in | 1 | ||||
-rw-r--r-- | gold/archive.cc | 38 | ||||
-rw-r--r-- | gold/archive.h | 31 | ||||
-rw-r--r-- | gold/common.cc | 30 | ||||
-rw-r--r-- | gold/common.h | 10 | ||||
-rw-r--r-- | gold/dirsearch.cc | 22 | ||||
-rw-r--r-- | gold/dirsearch.h | 14 | ||||
-rw-r--r-- | gold/fileread.cc | 87 | ||||
-rw-r--r-- | gold/fileread.h | 54 | ||||
-rw-r--r-- | gold/gold.cc | 84 | ||||
-rw-r--r-- | gold/gold.h | 4 | ||||
-rw-r--r-- | gold/layout.cc | 133 | ||||
-rw-r--r-- | gold/layout.h | 62 | ||||
-rw-r--r-- | gold/main.cc | 2 | ||||
-rw-r--r-- | gold/object.cc | 13 | ||||
-rw-r--r-- | gold/object.h | 25 | ||||
-rw-r--r-- | gold/po/POTFILES.in | 1 | ||||
-rw-r--r-- | gold/po/gold.pot | 225 | ||||
-rw-r--r-- | gold/readsyms.cc | 119 | ||||
-rw-r--r-- | gold/readsyms.h | 30 | ||||
-rw-r--r-- | gold/reloc.cc | 98 | ||||
-rw-r--r-- | gold/reloc.h | 29 | ||||
-rw-r--r-- | gold/script.cc | 39 | ||||
-rw-r--r-- | gold/script.h | 2 | ||||
-rw-r--r-- | gold/symtab.cc | 25 | ||||
-rw-r--r-- | gold/symtab.h | 6 | ||||
-rw-r--r-- | gold/testsuite/object_unittest.cc | 10 | ||||
-rw-r--r-- | gold/token.h | 316 | ||||
-rw-r--r-- | gold/workqueue-internal.h | 86 | ||||
-rw-r--r-- | gold/workqueue-threads.cc | 169 | ||||
-rw-r--r-- | gold/workqueue.cc | 669 | ||||
-rw-r--r-- | gold/workqueue.h | 423 |
33 files changed, 1458 insertions, 1400 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am index 59501c6..ce17e96 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -87,6 +87,7 @@ HFILES = \ target-reloc.h \ target-select.h \ tls.h \ + token.h \ workqueue.h \ workqueue-internal.h diff --git a/gold/Makefile.in b/gold/Makefile.in index 9345701..bfeaf86 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -344,6 +344,7 @@ HFILES = \ target-reloc.h \ target-select.h \ tls.h \ + token.h \ workqueue.h \ workqueue-internal.h diff --git a/gold/archive.cc b/gold/archive.cc index dc12ea9..b80dea5 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -73,12 +73,12 @@ const char Archive::arfmag[2] = { '`', '\n' }; // table. void -Archive::setup() +Archive::setup(Task* task) { // We need to ignore empty archives. if (this->input_file_->file().filesize() == sarmag) { - this->input_file_->file().unlock(); + this->input_file_->file().unlock(task); return; } @@ -109,7 +109,7 @@ Archive::setup() } // Opening the file locked it. Unlock it now. - this->input_file_->file().unlock(); + this->input_file_->file().unlock(task); } // Read the archive symbol map. @@ -434,33 +434,19 @@ Add_archive_symbols::~Add_archive_symbols() // Return whether we can add the archive symbols. We are blocked by // this_blocker_. We block next_blocker_. We also lock the file. -Task::Is_runnable_type -Add_archive_symbols::is_runnable(Workqueue*) +Task_token* +Add_archive_symbols::is_runnable() { if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; + return this->this_blocker_; + return NULL; } -class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker -{ - public: - Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue, - File_read& file) - : blocker_(token, workqueue), filelock_(file) - { } - - private: - Task_locker_block blocker_; - Task_locker_obj<File_read> filelock_; -}; - -Task_locker* -Add_archive_symbols::locks(Workqueue* workqueue) +void +Add_archive_symbols::locks(Task_locker* tl) { - return new Add_archive_symbols_locker(*this->next_blocker_, - workqueue, - this->archive_->file()); + tl->add(this, this->next_blocker_); + tl->add(this, this->archive_->token()); } void @@ -469,6 +455,8 @@ Add_archive_symbols::run(Workqueue*) this->archive_->add_symbols(this->symtab_, this->layout_, this->input_objects_); + this->archive_->release(); + if (this->input_group_ != NULL) this->input_group_->add_archive(this->archive_); else diff --git a/gold/archive.h b/gold/archive.h index 57af167..b135cd6 100644 --- a/gold/archive.h +++ b/gold/archive.h @@ -31,6 +31,7 @@ namespace gold { +class Task; class Input_file; class Input_objects; class Input_group; @@ -64,7 +65,7 @@ class Archive // Set up the archive: read the symbol map. void - setup(); + setup(Task*); // Get a reference to the underlying file. File_read& @@ -73,19 +74,29 @@ class Archive // Lock the underlying file. void - lock() - { this->input_file_->file().lock(); } + lock(const Task* t) + { this->input_file_->file().lock(t); } // Unlock the underlying file. void - unlock() - { this->input_file_->file().unlock(); } + unlock(const Task* t) + { this->input_file_->file().unlock(t); } // Return whether the underlying file is locked. bool is_locked() const { return this->input_file_->file().is_locked(); } + // Return the token, so that the task can be queued. + Task_token* + token() + { return this->input_file_->file().token(); } + + // Release the underlying file. + void + release() + { this->input_file_->file().release(); } + // Select members from the archive as needed and add them to the // link. void @@ -178,11 +189,11 @@ class Add_archive_symbols : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -196,8 +207,6 @@ class Add_archive_symbols : public Task } private: - class Add_archive_symbols_locker; - Symbol_table* symtab_; Layout* layout_; Input_objects* input_objects_; diff --git a/gold/common.cc b/gold/common.cc index 652dfa1..d94f2c3 100644 --- a/gold/common.cc +++ b/gold/common.cc @@ -38,35 +38,21 @@ namespace gold // This task allocates the common symbols. We need a lock on the // symbol table. -Task::Is_runnable_type -Allocate_commons_task::is_runnable(Workqueue*) +Task_token* +Allocate_commons_task::is_runnable() { if (!this->symtab_lock_->is_writable()) - return IS_LOCKED; - return IS_RUNNABLE; + return this->symtab_lock_; + return NULL; } // Return the locks we hold: one on the symbol table, and one blocker. -class Allocate_commons_task::Allocate_commons_locker : public Task_locker -{ - public: - Allocate_commons_locker(Task_token& symtab_lock, Task* task, - Task_token& blocker, Workqueue* workqueue) - : symtab_locker_(symtab_lock, task), - blocker_(blocker, workqueue) - { } - - private: - Task_locker_write symtab_locker_; - Task_locker_block blocker_; -}; - -Task_locker* -Allocate_commons_task::locks(Workqueue* workqueue) +void +Allocate_commons_task::locks(Task_locker* tl) { - return new Allocate_commons_locker(*this->symtab_lock_, this, - *this->blocker_, workqueue); + tl->add(this, this->blocker_); + tl->add(this, this->symtab_lock_); } // Allocate the common symbols. diff --git a/gold/common.h b/gold/common.h index bd24985..b85192e 100644 --- a/gold/common.h +++ b/gold/common.h @@ -45,11 +45,11 @@ class Allocate_commons_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -59,8 +59,6 @@ class Allocate_commons_task : public Task { return "Allocate_commons_task"; } private: - class Allocate_commons_locker; - const General_options& options_; Symbol_table* symtab_; Layout* layout_; diff --git a/gold/dirsearch.cc b/gold/dirsearch.cc index dd1c7e6..960c8eb 100644 --- a/gold/dirsearch.cc +++ b/gold/dirsearch.cc @@ -28,6 +28,8 @@ #include <dirent.h> #include "gold-threads.h" +#include "options.h" +#include "workqueue.h" #include "dirsearch.h" namespace @@ -169,11 +171,11 @@ class Dir_cache_task : public gold::Task : dir_(dir), token_(token) { } - Is_runnable_type - is_runnable(gold::Workqueue*); + gold::Task_token* + is_runnable(); - gold::Task_locker* - locks(gold::Workqueue*); + void + locks(gold::Task_locker*); void run(gold::Workqueue*); @@ -189,19 +191,19 @@ class Dir_cache_task : public gold::Task // We can always run the task to read the directory. -gold::Task::Is_runnable_type -Dir_cache_task::is_runnable(gold::Workqueue*) +gold::Task_token* +Dir_cache_task::is_runnable() { - return IS_RUNNABLE; + return NULL; } // Return the locks to hold. We use a blocker lock to prevent file // lookups from starting until the directory contents have been read. -gold::Task_locker* -Dir_cache_task::locks(gold::Workqueue* workqueue) +void +Dir_cache_task::locks(gold::Task_locker* tl) { - return new gold::Task_locker_block(this->token_, workqueue); + tl->add(this, &this->token_); } // Run the task--read the directory contents. diff --git a/gold/dirsearch.h b/gold/dirsearch.h index c1af843..e590f41 100644 --- a/gold/dirsearch.h +++ b/gold/dirsearch.h @@ -26,12 +26,14 @@ #include <string> #include <list> -#include "workqueue.h" +#include "options.h" +#include "token.h" namespace gold { class General_options; +class Workqueue; // A simple interface to manage directories to be searched for // libraries. @@ -40,7 +42,7 @@ class Dirsearch { public: Dirsearch() - : directories_(NULL), token_() + : directories_(NULL), token_(true) { } // Set the list of directories to search. @@ -55,10 +57,10 @@ class Dirsearch std::string find(const std::string&, const std::string& n2, bool *is_in_sysroot) const; - // Return a reference to the blocker token which controls access. - const Task_token& - token() const - { return this->token_; } + // Return the blocker token which controls access. + Task_token* + token() + { return &this->token_; } private: // We can not copy this class. diff --git a/gold/fileread.cc b/gold/fileread.cc index e5df08b..774179d 100644 --- a/gold/fileread.cc +++ b/gold/fileread.cc @@ -83,7 +83,7 @@ unsigned long long File_read::maximum_mapped_bytes; File_read::~File_read() { - gold_assert(this->lock_count_ == 0); + gold_assert(this->token_.is_writable()); if (this->descriptor_ >= 0) { if (close(this->descriptor_) < 0) @@ -98,9 +98,9 @@ File_read::~File_read() // Open the file. bool -File_read::open(const std::string& name) +File_read::open(const Task* task, const std::string& name) { - gold_assert(this->lock_count_ == 0 + gold_assert(this->token_.is_writable() && this->descriptor_ < 0 && this->name_.empty()); this->name_ = name; @@ -116,7 +116,7 @@ File_read::open(const std::string& name) this->size_ = s.st_size; } - ++this->lock_count_; + this->token_.add_writer(task); return this->descriptor_ >= 0; } @@ -124,46 +124,67 @@ File_read::open(const std::string& name) // Open the file for testing purposes. bool -File_read::open(const std::string& name, const unsigned char* contents, - off_t size) +File_read::open(const Task* task, const std::string& name, + const unsigned char* contents, off_t size) { - gold_assert(this->lock_count_ == 0 + gold_assert(this->token_.is_writable() && this->descriptor_ < 0 && this->name_.empty()); this->name_ = name; this->contents_ = contents; this->size_ = size; - ++this->lock_count_; + this->token_.add_writer(task); return true; } +// Release the file. This is called when we are done with the file in +// a Task. + void -File_read::lock() +File_read::release() { - ++this->lock_count_; + gold_assert(this->is_locked()); + + File_read::total_mapped_bytes += this->mapped_bytes_; + File_read::current_mapped_bytes += this->mapped_bytes_; + this->mapped_bytes_ = 0; + if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes) + File_read::maximum_mapped_bytes = File_read::current_mapped_bytes; + + this->clear_views(false); + + this->released_ = true; } +// Lock the file. + void -File_read::unlock() +File_read::lock(const Task* task) { - gold_assert(this->lock_count_ > 0); - --this->lock_count_; - if (this->lock_count_ == 0) - { - File_read::total_mapped_bytes += this->mapped_bytes_; - File_read::current_mapped_bytes += this->mapped_bytes_; - this->mapped_bytes_ = 0; - if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes) - File_read::maximum_mapped_bytes = File_read::current_mapped_bytes; + gold_assert(this->released_); + this->token_.add_writer(task); + this->released_ = false; +} - this->clear_views(false); - } +// Unlock the file. + +void +File_read::unlock(const Task* task) +{ + this->release(); + this->token_.remove_writer(task); } +// Return whether the file is locked. + bool File_read::is_locked() const { - return this->lock_count_ > 0; + if (!this->token_.is_writable()) + return true; + // The file is not locked, so it should have been released. + gold_assert(this->released_); + return false; } // See if we have a view which covers the file starting at START for @@ -238,7 +259,8 @@ File_read::read(off_t start, off_t size, void* p) const File_read::View* File_read::find_or_make_view(off_t start, off_t size, bool cache) { - gold_assert(this->lock_count_ > 0); + gold_assert(!this->token_.is_writable()); + this->released_ = false; off_t poff = File_read::page_offset(start); @@ -301,14 +323,11 @@ File_read::find_or_make_view(off_t start, off_t size, bool cache) return v; } -// This implementation of get_view just reads into a memory buffer, -// which we store on view_list_. At some point we should support -// mmap. +// Get a view into the file. const unsigned char* File_read::get_view(off_t start, off_t size, bool cache) { - gold_assert(this->lock_count_ > 0); File_read::View* pv = this->find_or_make_view(start, size, cache); return pv->data() + (start - pv->start()); } @@ -316,7 +335,6 @@ File_read::get_view(off_t start, off_t size, bool cache) File_view* File_read::get_lasting_view(off_t start, off_t size, bool cache) { - gold_assert(this->lock_count_ > 0); File_read::View* pv = this->find_or_make_view(start, size, cache); pv->lock(); return new File_view(*this, pv, pv->data() + (start - pv->start())); @@ -388,13 +406,13 @@ File_view::~File_view() // Create a file for testing. -Input_file::Input_file(const char* name, const unsigned char* contents, - off_t size) +Input_file::Input_file(const Task* task, const char* name, + const unsigned char* contents, off_t size) : file_() { this->input_argument_ = new Input_file_argument(name, false, "", Position_dependent_options()); - bool ok = file_.open(name, contents, size); + bool ok = file_.open(task, name, contents, size); gold_assert(ok); } @@ -408,7 +426,8 @@ Input_file::Input_file(const char* name, const unsigned char* contents, // the file location, rather than the current directory. bool -Input_file::open(const General_options& options, const Dirsearch& dirpath) +Input_file::open(const General_options& options, const Dirsearch& dirpath, + const Task* task) { std::string name; @@ -477,7 +496,7 @@ Input_file::open(const General_options& options, const Dirsearch& dirpath) } // Now that we've figured out where the file lives, try to open it. - if (!this->file_.open(name)) + if (!this->file_.open(task, name)) { gold_error(_("cannot open %s: %s"), name.c_str(), strerror(errno)); diff --git a/gold/fileread.h b/gold/fileread.h index eddb887..fd9b1ba 100644 --- a/gold/fileread.h +++ b/gold/fileread.h @@ -30,6 +30,7 @@ #include <string> #include "options.h" +#include "token.h" namespace gold { @@ -45,41 +46,57 @@ class File_read { public: File_read() - : name_(), descriptor_(-1), size_(0), lock_count_(0), views_(), - saved_views_(), contents_(NULL), mapped_bytes_(0) + : name_(), descriptor_(-1), size_(0), token_(false), views_(), + saved_views_(), contents_(NULL), mapped_bytes_(0), released_(true) { } ~File_read(); // Open a file. bool - open(const std::string& name); + open(const Task*, const std::string& name); // Pretend to open the file, but provide the file contents. No // actual file system activity will occur. This is used for // testing. bool - open(const std::string& name, const unsigned char* contents, off_t size); + open(const Task*, const std::string& name, const unsigned char* contents, + off_t size); // Return the file name. const std::string& filename() const { return this->name_; } - // Lock the file for access within a particular Task::run execution. - // This means that the descriptor can not be closed. This routine - // may only be called from the main thread. + // Lock the file for exclusive access within a particular Task::run + // execution. This means that the descriptor can not be closed. + // This routine may only be called when the workqueue lock is held. void - lock(); + lock(const Task* t); // Unlock the descriptor, permitting it to be closed if necessary. void - unlock(); + unlock(const Task* t); // Test whether the object is locked. bool is_locked() const; + // Return the token, so that the task can be queued. + Task_token* + token() + { return &this->token_; } + + // Release the file. This indicates that we aren't going to do + // anything further with it until it is unlocked. This is used + // because a Task which locks the file never calls either lock or + // unlock; it just locks the token. The basic rule is that a Task + // which locks a file via the Task::locks interface must explicitly + // call release() when it is done. This is not necessary for code + // which calls unlock() on the file. + void + release(); + // Return the size of the file. off_t filesize() const @@ -118,16 +135,16 @@ class File_read File_read(const File_read&); File_read& operator=(const File_read&); - // Total bytes mapped into memory during the link. This variable is - // only accessed from the main thread, when unlocking the object. + // Total bytes mapped into memory during the link. This variable + // may not be accurate when running multi-threaded. static unsigned long long total_mapped_bytes; // Current number of bytes mapped into memory during the link. This - // variable is only accessed from the main thread. + // variable may not be accurate when running multi-threaded. static unsigned long long current_mapped_bytes; // High water mark of bytes mapped into memory during the link. - // This variable is only accessed from the main thread. + // This variable may not be accurate when running multi-threaded. static unsigned long long maximum_mapped_bytes; // A view into the file. @@ -227,8 +244,8 @@ class File_read int descriptor_; // File size. off_t size_; - // Number of locks on the file. - int lock_count_; + // A token used to lock the file. + Task_token token_; // Buffered views into the file. Views views_; // List of views which were locked but had to be removed from views_ @@ -240,6 +257,8 @@ class File_read // while the file is locked. When we unlock the file, we transfer // the total to total_mapped_bytes, and reset this to zero. size_t mapped_bytes_; + // Whether the file was released. + bool released_; }; // A view of file data that persists even when the file is unlocked. @@ -288,12 +307,13 @@ class Input_file // Create an input file with the contents already provided. This is // only used for testing. With this path, don't call the open // method. - Input_file(const char* name, const unsigned char* contents, off_t size); + Input_file(const Task*, const char* name, const unsigned char* contents, + off_t size); // Open the file. If the open fails, this will report an error and // return false. bool - open(const General_options&, const Dirsearch&); + open(const General_options&, const Dirsearch&, const Task*); // Return the name given by the user. For -lc this will return "c". const char* diff --git a/gold/gold.cc b/gold/gold.cc index a3ead18..b300871 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -89,7 +89,7 @@ class Middle_runner : public Task_function_runner { } void - run(Workqueue*); + run(Workqueue*, const Task*); private: const General_options& options_; @@ -99,9 +99,9 @@ class Middle_runner : public Task_function_runner }; void -Middle_runner::run(Workqueue* workqueue) +Middle_runner::run(Workqueue* workqueue, const Task* task) { - queue_middle_tasks(this->options_, this->input_objects_, this->symtab_, + queue_middle_tasks(this->options_, task, this->input_objects_, this->symtab_, this->layout_, workqueue); } @@ -109,7 +109,7 @@ Middle_runner::run(Workqueue* workqueue) void queue_initial_tasks(const General_options& options, - const Dirsearch& search_path, + Dirsearch& search_path, const Command_line& cmdline, Workqueue* workqueue, Input_objects* input_objects, Symbol_table* symtab, Layout* layout) @@ -131,10 +131,10 @@ queue_initial_tasks(const General_options& options, p != cmdline.end(); ++p) { - Task_token* next_blocker = new Task_token(); + Task_token* next_blocker = new Task_token(true); next_blocker->add_blocker(); workqueue->queue(new Read_symbols(options, input_objects, symtab, layout, - search_path, &*p, NULL, this_blocker, + &search_path, &*p, NULL, this_blocker, next_blocker)); this_blocker = next_blocker; } @@ -153,6 +153,7 @@ queue_initial_tasks(const General_options& options, void queue_middle_tasks(const General_options& options, + const Task* task, const Input_objects* input_objects, Symbol_table* symtab, Layout* layout, @@ -187,7 +188,7 @@ queue_middle_tasks(const General_options& options, // See if any of the input definitions violate the One Definition Rule. // TODO: if this is too slow, do this as a task, rather than inline. - symtab->detect_odr_violations(options.output_file_name()); + symtab->detect_odr_violations(task, options.output_file_name()); // Define some sections and symbols needed for a dynamic link. This // handles some cases we want to see before we read the relocs. @@ -212,8 +213,8 @@ queue_middle_tasks(const General_options& options, // Doing that is more complex, since we may later decide to discard // some of the sections, and thus change our minds about the types // of references made to the symbols. - Task_token* blocker = new Task_token(); - Task_token* symtab_lock = new Task_token(); + Task_token* blocker = new Task_token(true); + Task_token* symtab_lock = new Task_token(false); for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); p != input_objects->relobj_end(); ++p) @@ -260,30 +261,20 @@ queue_final_tasks(const General_options& options, thread_count = input_objects->number_of_input_objects(); workqueue->set_thread_count(thread_count); + bool any_postprocessing_sections = layout->any_postprocessing_sections(); + // Use a blocker to wait until all the input sections have been // written out. - Task_token* input_sections_blocker = new Task_token(); + Task_token* input_sections_blocker = NULL; + if (!any_postprocessing_sections) + input_sections_blocker = new Task_token(true); // Use a blocker to block any objects which have to wait for the // output sections to complete before they can apply relocations. - Task_token* output_sections_blocker = new Task_token(); + Task_token* output_sections_blocker = new Task_token(true); // Use a blocker to block the final cleanup task. - Task_token* final_blocker = new Task_token(); - - // Queue a task for each input object to relocate the sections and - // write out the local symbols. - for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); - p != input_objects->relobj_end(); - ++p) - { - input_sections_blocker->add_blocker(); - final_blocker->add_blocker(); - workqueue->queue(new Relocate_task(options, symtab, layout, *p, of, - input_sections_blocker, - output_sections_blocker, - final_blocker)); - } + Task_token* final_blocker = new Task_token(true); // Queue a task to write out the symbol table. if (!options.strip_all()) @@ -307,12 +298,43 @@ queue_final_tasks(const General_options& options, final_blocker->add_blocker(); workqueue->queue(new Write_data_task(layout, symtab, of, final_blocker)); + // Queue a task for each input object to relocate the sections and + // write out the local symbols. + for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + p != input_objects->relobj_end(); + ++p) + { + if (input_sections_blocker != NULL) + input_sections_blocker->add_blocker(); + final_blocker->add_blocker(); + workqueue->queue(new Relocate_task(options, symtab, layout, *p, of, + input_sections_blocker, + output_sections_blocker, + final_blocker)); + } + // Queue a task to write out the output sections which depend on - // input sections. - final_blocker->add_blocker(); - workqueue->queue(new Write_after_input_sections_task(layout, of, - input_sections_blocker, - final_blocker)); + // input sections. If there are any sections which require + // postprocessing, then we need to do this last, since it may resize + // the output file. + if (!any_postprocessing_sections) + { + final_blocker->add_blocker(); + Task* t = new Write_after_input_sections_task(layout, of, + input_sections_blocker, + final_blocker); + workqueue->queue(t); + } + else + { + Task_token *new_final_blocker = new Task_token(true); + new_final_blocker->add_blocker(); + Task* t = new Write_after_input_sections_task(layout, of, + final_blocker, + new_final_blocker); + workqueue->queue(t); + final_blocker = new_final_blocker; + } // Queue a task to close the output file. This will be blocked by // FINAL_BLOCKER. diff --git a/gold/gold.h b/gold/gold.h index fcb77ec..32fcc43 100644 --- a/gold/gold.h +++ b/gold/gold.h @@ -174,6 +174,7 @@ class Input_objects; class Symbol; class Symbol_table; class Layout; +class Task; class Workqueue; class Output_file; template<int size, bool big_endian> @@ -252,7 +253,7 @@ get_version_string(); // Queue up the first set of tasks. extern void queue_initial_tasks(const General_options&, - const Dirsearch&, + Dirsearch&, const Command_line&, Workqueue*, Input_objects*, @@ -262,6 +263,7 @@ queue_initial_tasks(const General_options&, // Queue up the middle set of tasks. extern void queue_middle_tasks(const General_options&, + const Task*, const Input_objects*, Symbol_table*, Layout*, diff --git a/gold/layout.cc b/gold/layout.cc index 1cea1a6..71e3ab4 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -44,10 +44,11 @@ namespace gold // have been read. void -Layout_task_runner::run(Workqueue* workqueue) +Layout_task_runner::run(Workqueue* workqueue, const Task* task) { off_t file_size = this->layout_->finalize(this->input_objects_, - this->symtab_); + this->symtab_, + task); // Now we know the final size of the output file and we know where // each piece of information goes. @@ -72,7 +73,8 @@ Layout::Layout(const General_options& options) input_requires_executable_stack_(false), input_with_gnu_stack_note_(false), input_without_gnu_stack_note_(false), - has_static_tls_(false) + has_static_tls_(false), + any_postprocessing_sections_(false) { // Make space for more than enough segments for a typical file. // This is just for efficiency--it's OK if we wind up needing more. @@ -653,13 +655,14 @@ Layout::find_first_load_seg() // This function returns the size of the output file. off_t -Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) +Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, + const Task* task) { Target* const target = input_objects->target(); target->finalize_sections(this); - this->count_local_symbols(input_objects); + this->count_local_symbols(task, input_objects); this->create_gold_note(); this->create_executable_stack_info(target); @@ -730,7 +733,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) off_t off = this->set_segment_offsets(target, load_seg, &shndx); // Create the symbol table sections. - this->create_symtab_sections(input_objects, symtab, &off); + this->create_symtab_sections(input_objects, symtab, task, &off); if (!parameters->doing_static_link()) this->assign_local_dynsym_offsets(input_objects); @@ -747,6 +750,12 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) // Create the section table header. this->create_shdrs(&off); + // If there are no sections which require postprocessing, we can + // handle the section names now, and avoid a resize later. + if (!this->any_postprocessing_sections_) + off = this->set_section_offsets(off, + STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS); + file_header->set_section_info(this->section_headers_, shstrtab_section); // Now we know exactly where everything goes in the output file @@ -1106,16 +1115,19 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass) if (pass == BEFORE_INPUT_SECTIONS_PASS && (*p)->requires_postprocessing()) - (*p)->create_postprocessing_buffer(); + { + (*p)->create_postprocessing_buffer(); + this->any_postprocessing_sections_ = true; + } if (pass == BEFORE_INPUT_SECTIONS_PASS && (*p)->after_input_sections()) continue; - else if (pass == AFTER_INPUT_SECTIONS_PASS + else if (pass == POSTPROCESSING_SECTIONS_PASS && (!(*p)->after_input_sections() || (*p)->type() == elfcpp::SHT_STRTAB)) continue; - else if (pass == STRTAB_AFTER_INPUT_SECTIONS_PASS + else if (pass == STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS && (!(*p)->after_input_sections() || (*p)->type() != elfcpp::SHT_STRTAB)) continue; @@ -1126,7 +1138,7 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass) off += (*p)->data_size(); // At this point the name must be set. - if (pass != STRTAB_AFTER_INPUT_SECTIONS_PASS) + if (pass != STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS) this->namepool_.add((*p)->name(), false, NULL); } return off; @@ -1152,7 +1164,8 @@ Layout::set_section_indexes(unsigned int shndx) // symbol table, and build the respective string pools. void -Layout::count_local_symbols(const Input_objects* input_objects) +Layout::count_local_symbols(const Task* task, + const Input_objects* input_objects) { // First, figure out an upper bound on the number of symbols we'll // be inserting into each pool. This helps us create the pools with @@ -1177,7 +1190,7 @@ Layout::count_local_symbols(const Input_objects* input_objects) p != input_objects->relobj_end(); ++p) { - Task_lock_obj<Object> tlo(**p); + Task_lock_obj<Object> tlo(task, *p); (*p)->count_local_symbols(&this->sympool_, &this->dynpool_); } } @@ -1189,6 +1202,7 @@ Layout::count_local_symbols(const Input_objects* input_objects) void Layout::create_symtab_sections(const Input_objects* input_objects, Symbol_table* symtab, + const Task* task, off_t* poff) { int symsize; @@ -1262,7 +1276,7 @@ Layout::create_symtab_sections(const Input_objects* input_objects, == this->dynsym_section_->data_size() - locsize); } - off = symtab->finalize(local_symcount, off, dynoff, dyn_global_index, + off = symtab->finalize(task, local_symcount, off, dynoff, dyn_global_index, dyncount, &this->sympool_); if (!parameters->strip_all()) @@ -2004,16 +2018,21 @@ Layout::write_sections_after_input_sections(Output_file* of) // file size. Note we finalize the .shstrab last, to allow the // after_input_section sections to modify their section-names before // writing. - off_t off = this->output_file_size_; - off = this->set_section_offsets(off, AFTER_INPUT_SECTIONS_PASS); - - // Now that we've finalized the names, we can finalize the shstrab. - off = this->set_section_offsets(off, STRTAB_AFTER_INPUT_SECTIONS_PASS); - - if (off > this->output_file_size_) + if (this->any_postprocessing_sections_) { - of->resize(off); - this->output_file_size_ = off; + off_t off = this->output_file_size_; + off = this->set_section_offsets(off, POSTPROCESSING_SECTIONS_PASS); + + // Now that we've finalized the names, we can finalize the shstrab. + off = + this->set_section_offsets(off, + STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS); + + if (off > this->output_file_size_) + { + of->resize(off); + this->output_file_size_ = off; + } } for (Section_list::const_iterator p = this->section_list_.begin(); @@ -2049,36 +2068,20 @@ Layout::print_stats() const // We can always run this task. -Task::Is_runnable_type -Write_sections_task::is_runnable(Workqueue*) +Task_token* +Write_sections_task::is_runnable() { - return IS_RUNNABLE; + return NULL; } // We need to unlock both OUTPUT_SECTIONS_BLOCKER and FINAL_BLOCKER // when finished. -class Write_sections_task::Write_sections_locker : public Task_locker -{ - public: - Write_sections_locker(Task_token& output_sections_blocker, - Task_token& final_blocker, - Workqueue* workqueue) - : output_sections_block_(output_sections_blocker, workqueue), - final_block_(final_blocker, workqueue) - { } - - private: - Task_block_token output_sections_block_; - Task_block_token final_block_; -}; - -Task_locker* -Write_sections_task::locks(Workqueue* workqueue) +void +Write_sections_task::locks(Task_locker* tl) { - return new Write_sections_locker(*this->output_sections_blocker_, - *this->final_blocker_, - workqueue); + tl->add(this, this->output_sections_blocker_); + tl->add(this, this->final_blocker_); } // Run the task--write out the data. @@ -2093,18 +2096,18 @@ Write_sections_task::run(Workqueue*) // We can always run this task. -Task::Is_runnable_type -Write_data_task::is_runnable(Workqueue*) +Task_token* +Write_data_task::is_runnable() { - return IS_RUNNABLE; + return NULL; } // We need to unlock FINAL_BLOCKER when finished. -Task_locker* -Write_data_task::locks(Workqueue* workqueue) +void +Write_data_task::locks(Task_locker* tl) { - return new Task_locker_block(*this->final_blocker_, workqueue); + tl->add(this, this->final_blocker_); } // Run the task--write out the data. @@ -2119,18 +2122,18 @@ Write_data_task::run(Workqueue*) // We can always run this task. -Task::Is_runnable_type -Write_symbols_task::is_runnable(Workqueue*) +Task_token* +Write_symbols_task::is_runnable() { - return IS_RUNNABLE; + return NULL; } // We need to unlock FINAL_BLOCKER when finished. -Task_locker* -Write_symbols_task::locks(Workqueue* workqueue) +void +Write_symbols_task::locks(Task_locker* tl) { - return new Task_locker_block(*this->final_blocker_, workqueue); + tl->add(this, this->final_blocker_); } // Run the task--write out the symbols. @@ -2146,20 +2149,20 @@ Write_symbols_task::run(Workqueue*) // We can only run this task after the input sections have completed. -Task::Is_runnable_type -Write_after_input_sections_task::is_runnable(Workqueue*) +Task_token* +Write_after_input_sections_task::is_runnable() { if (this->input_sections_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; + return this->input_sections_blocker_; + return NULL; } // We need to unlock FINAL_BLOCKER when finished. -Task_locker* -Write_after_input_sections_task::locks(Workqueue* workqueue) +void +Write_after_input_sections_task::locks(Task_locker* tl) { - return new Task_locker_block(*this->final_blocker_, workqueue); + tl->add(this, this->final_blocker_); } // Run the task. @@ -2175,7 +2178,7 @@ Write_after_input_sections_task::run(Workqueue*) // Run the task--close the file. void -Close_task_runner::run(Workqueue*) +Close_task_runner::run(Workqueue*, const Task*) { this->of_->close(); } diff --git a/gold/layout.h b/gold/layout.h index 37a70db..3084d60 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -67,7 +67,7 @@ class Layout_task_runner : public Task_function_runner // Run the operation. void - run(Workqueue*); + run(Workqueue*, const Task*); private: Layout_task_runner(const Layout_task_runner&); @@ -168,7 +168,12 @@ class Layout // Finalize the layout after all the input sections have been added. off_t - finalize(const Input_objects*, Symbol_table*); + finalize(const Input_objects*, Symbol_table*, const Task*); + + // Return whether any sections require postprocessing. + bool + any_postprocessing_sections() const + { return this->any_postprocessing_sections_; } // Return the size of the output file. off_t @@ -283,11 +288,12 @@ class Layout // Count the local symbols in the regular symbol table and the dynamic // symbol table, and build the respective string pools. void - count_local_symbols(const Input_objects*); + count_local_symbols(const Task*, const Input_objects*); // Create the output sections for the symbol table. void - create_symtab_sections(const Input_objects*, Symbol_table*, off_t*); + create_symtab_sections(const Input_objects*, Symbol_table*, const Task*, + off_t*); // Create the .shstrtab section. Output_section* @@ -368,14 +374,14 @@ class Layout // Set the final file offsets of all the sections not associated // with a segment. We set section offsets in three passes: the // first handles all allocated sections, the second sections that - // can be handled after input-sections are processed, and the last - // the late-bound STRTAB sections (probably only shstrtab, which is - // the one we care about because it holds section names). + // require postprocessing, and the last the late-bound STRTAB + // sections (probably only shstrtab, which is the one we care about + // because it holds section names). enum Section_offset_pass { BEFORE_INPUT_SECTIONS_PASS, - AFTER_INPUT_SECTIONS_PASS, - STRTAB_AFTER_INPUT_SECTIONS_PASS + POSTPROCESSING_SECTIONS_PASS, + STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS }; off_t set_section_offsets(off_t, Section_offset_pass pass); @@ -472,6 +478,8 @@ class Layout bool input_without_gnu_stack_note_; // Whether we have seen an object file that uses the static TLS model. bool has_static_tls_; + // Whether any sections require postprocessing. + bool any_postprocessing_sections_; }; // This task handles writing out data in output sections which is not @@ -492,11 +500,11 @@ class Write_sections_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -527,11 +535,11 @@ class Write_data_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -562,11 +570,11 @@ class Write_symbols_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -602,11 +610,11 @@ class Write_after_input_sections_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -616,8 +624,6 @@ class Write_after_input_sections_task : public Task { return "Write_after_input_sections_task"; } private: - class Write_sections_locker; - Layout* layout_; Output_file* of_; Task_token* input_sections_blocker_; @@ -635,7 +641,7 @@ class Close_task_runner : public Task_function_runner // Run the operation. void - run(Workqueue*); + run(Workqueue*, const Task*); private: Output_file* of_; diff --git a/gold/main.cc b/gold/main.cc index c7c2d95..2874a2d 100644 --- a/gold/main.cc +++ b/gold/main.cc @@ -94,7 +94,7 @@ main(int argc, char** argv) &symtab, &layout); // Run the main task processing loop. - workqueue.process(); + workqueue.process(0); if (command_line.options().print_stats()) { diff --git a/gold/object.cc b/gold/object.cc index 3bf274a..9d62881 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -726,8 +726,8 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab, } // Finalize the local symbols. Here we add their names to *POOL and -// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_. -// This function is always called from the main thread. The actual +// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_. This +// function is always called from a singleton thread. The actual // output of the local symbols will occur in a separate task. template<int size, bool big_endian> @@ -831,7 +831,7 @@ Sized_relobj<size, big_endian>::do_count_local_symbols(Stringpool* pool, // Finalize the local symbols. Here we add their values to // THIS->LOCAL_VALUES_ and set their output symbol table indexes. -// This function is always called from the main thread. The actual +// This function is always called from a singleton thread. The actual // output of the local symbols will occur in a separate task. template<int size, bool big_endian> @@ -987,9 +987,10 @@ Sized_relobj<size, big_endian>::local_value(unsigned int shndx, template<int size, bool big_endian> void -Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of, - const Stringpool* sympool, - const Stringpool* dynpool) +Sized_relobj<size, big_endian>::write_local_symbols( + Output_file* of, + const Stringpool* sympool, + const Stringpool* dynpool) { if (parameters->strip_all() && this->output_local_dynsym_count_ == 0) return; diff --git a/gold/object.h b/gold/object.h index 1fcdd10..a43ef0c 100644 --- a/gold/object.h +++ b/gold/object.h @@ -35,6 +35,7 @@ namespace gold { class General_options; +class Task; class Layout; class Output_section; class Output_file; @@ -165,19 +166,29 @@ class Object // Lock the underlying file. void - lock() - { this->input_file()->file().lock(); } + lock(const Task* t) + { this->input_file()->file().lock(t); } // Unlock the underlying file. void - unlock() - { this->input_file()->file().unlock(); } + unlock(const Task* t) + { this->input_file()->file().unlock(t); } // Return whether the underlying file is locked. bool is_locked() const { return this->input_file()->file().is_locked(); } + // Return the token, so that the task can be queued. + Task_token* + token() + { return this->input_file()->file().token(); } + + // Release the underlying file. + void + release() + { this->input_file_->file().release(); } + // Return the sized target structure associated with this object. // This is like the target method but it returns a pointer of // appropriate checked type. @@ -322,7 +333,7 @@ class Object virtual unsigned int do_section_info(unsigned int shndx) = 0; - // Get the file. + // Get the file. We pass on const-ness. Input_file* input_file() { return this->input_file_; } @@ -508,7 +519,7 @@ class Relobj : public Object // any relocations for sections which require special handling, such // as the exception frame section. bool - relocs_must_follow_section_writes() + relocs_must_follow_section_writes() const { return this->relocs_must_follow_section_writes_; } // Return the object merge map. @@ -1101,7 +1112,7 @@ class Sized_relobj : public Relobj // Write section data to the output file. Record the views and // sizes in VIEWS for use when relocating. void - write_sections(const unsigned char* pshdrs, Output_file*, Views*); + write_sections(const unsigned char* pshdrs, Output_file*, Views*) const; // Relocate the sections in the output file. void diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index e8a5ba7..6335c9e 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -53,6 +53,7 @@ target-reloc.h target-select.cc target-select.h tls.h +token.h version.cc workqueue.cc workqueue.h diff --git a/gold/po/gold.pot b/gold/po/gold.pot index 8d97ea1..4b786be 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-29 16:33-0800\n" +"POT-Creation-Date: 2007-12-14 09:45-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -61,11 +61,11 @@ msgstr "" msgid "%s: member at %zu is not an ELF object" msgstr "" -#: compressed_output.cc:140 -msgid "Not compressing section data: zlib error" +#: compressed_output.cc:126 +msgid "not compressing section data: zlib error" msgstr "" -#: dirsearch.cc:68 +#: dirsearch.cc:70 #, c-format msgid "%s: can not read directory: %s" msgstr "" @@ -114,7 +114,7 @@ msgstr "" msgid "dynamic symbol table name section has wrong type: %u" msgstr "" -#: dynobj.cc:402 object.cc:236 object.cc:571 +#: dynobj.cc:402 object.cc:238 object.cc:574 #, c-format msgid "bad section name offset for section %u: %lu" msgstr "" @@ -218,42 +218,42 @@ msgstr "" msgid "%s: fstat failed: %s" msgstr "" -#: fileread.cc:210 +#: fileread.cc:231 #, c-format msgid "%s: pread failed: %s" msgstr "" -#: fileread.cc:216 +#: fileread.cc:237 #, c-format msgid "%s: file too short: read only %lld of %lld bytes at %lld" msgstr "" -#: fileread.cc:292 +#: fileread.cc:312 #, c-format msgid "%s: mmap offset %lld size %lld failed: %s" msgstr "" -#: fileread.cc:371 +#: fileread.cc:393 #, c-format msgid "%s: total bytes mapped for read: %llu\n" msgstr "" -#: fileread.cc:373 +#: fileread.cc:395 #, c-format msgid "%s: maximum bytes mapped for read at one time: %llu\n" msgstr "" -#: fileread.cc:442 +#: fileread.cc:465 #, c-format msgid "cannot find -l%s" msgstr "" -#: fileread.cc:469 +#: fileread.cc:492 #, c-format msgid "cannot find %s" msgstr "" -#: fileread.cc:480 +#: fileread.cc:503 #, c-format msgid "cannot open %s: %s" msgstr "" @@ -265,12 +265,12 @@ msgstr "" #. We had some input files, but we weren't able to open any of #. them. -#: gold.cc:118 gold.cc:165 +#: gold.cc:118 gold.cc:166 msgid "no input files" msgstr "" #. We print out just the first .so we see; there may be others. -#: gold.cc:180 +#: gold.cc:181 #, c-format msgid "cannot mix -static with dynamic object %s" msgstr "" @@ -336,50 +336,50 @@ msgid "pthread_cond_broadcast failed: %s" msgstr "" #. FIXME: This needs to specify the location somehow. -#: i386.cc:160 i386.cc:1439 x86_64.cc:172 x86_64.cc:1269 +#: i386.cc:160 i386.cc:1480 x86_64.cc:172 x86_64.cc:1370 msgid "missing expected TLS relocation" msgstr "" -#: i386.cc:779 x86_64.cc:732 x86_64.cc:910 +#: i386.cc:806 x86_64.cc:761 x86_64.cc:975 #, c-format msgid "%s: unsupported reloc %u against local symbol" msgstr "" -#: i386.cc:882 i386.cc:1169 x86_64.cc:851 x86_64.cc:1093 +#: i386.cc:913 i386.cc:1209 x86_64.cc:886 x86_64.cc:1157 #, c-format msgid "%s: unexpected reloc %u in object file" msgstr "" -#: i386.cc:1018 x86_64.cc:924 x86_64.cc:1152 +#: i386.cc:1052 x86_64.cc:989 x86_64.cc:1253 #, c-format msgid "%s: unsupported reloc %u against global symbol %s" msgstr "" -#: i386.cc:1322 +#: i386.cc:1363 #, c-format msgid "%s: unsupported RELA reloc section" msgstr "" -#: i386.cc:1579 x86_64.cc:1467 +#: i386.cc:1620 x86_64.cc:1569 #, c-format msgid "unexpected reloc %u in object file" msgstr "" -#: 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 +#: i386.cc:1652 i386.cc:1727 i386.cc:1734 i386.cc:1765 i386.cc:1818 +#: x86_64.cc:1590 x86_64.cc:1670 x86_64.cc:1694 #, c-format msgid "unsupported reloc %u" msgstr "" -#: i386.cc:1702 +#: i386.cc:1742 msgid "both SUN and GNU model TLS relocations" msgstr "" -#: merge.cc:464 +#: merge.cc:472 msgid "mergeable string section length not multiple of character size" msgstr "" -#: merge.cc:480 +#: merge.cc:488 msgid "entry in mergeable string section not null terminated" msgstr "" @@ -388,7 +388,7 @@ msgstr "" msgid "%s: unsupported ELF machine number %d" msgstr "" -#: object.cc:71 script.cc:1226 +#: object.cc:71 script.cc:1229 #, c-format msgid "%s: %s" msgstr "" @@ -398,117 +398,117 @@ msgstr "" msgid "section name section has wrong type: %u" msgstr "" -#: object.cc:308 +#: object.cc:311 #, c-format msgid "invalid symbol table name index: %u" msgstr "" -#: object.cc:314 +#: object.cc:317 #, c-format msgid "symbol table name section has wrong type: %u" msgstr "" -#: object.cc:394 +#: object.cc:397 #, c-format msgid "section group %u info %u out of range" msgstr "" -#: object.cc:412 +#: object.cc:415 #, c-format msgid "symbol %u name offset %u out of range" msgstr "" -#: object.cc:444 +#: object.cc:447 #, c-format msgid "section %u in section group %u out of range" msgstr "" -#: object.cc:534 reloc.cc:229 reloc.cc:496 +#: object.cc:537 reloc.cc:205 reloc.cc:520 #, c-format msgid "relocation section %u has bad info %u" msgstr "" -#: object.cc:706 +#: object.cc:709 msgid "size of symbols is not multiple of symbol size" msgstr "" -#. FIXME: Handle SHN_XINDEX. -#: object.cc:798 +#: object.cc:808 #, c-format -msgid "unknown section index %u for local symbol %u" +msgid "local symbol %u section name out of range: %u >= %u" msgstr "" -#: object.cc:807 +#. FIXME: Handle SHN_XINDEX. +#: object.cc:865 #, c-format -msgid "local symbol %u section index %u out of range" +msgid "unknown section index %u for local symbol %u" msgstr "" -#: object.cc:839 +#: object.cc:874 #, c-format -msgid "local symbol %u section name out of range: %u >= %u" +msgid "local symbol %u section index %u out of range" msgstr "" -#: object.cc:1070 +#: object.cc:1194 #, c-format msgid "%s: incompatible target" msgstr "" -#: object.cc:1226 +#: object.cc:1349 #, c-format msgid "%s: unsupported ELF file type %d" msgstr "" -#: object.cc:1245 object.cc:1291 object.cc:1325 +#: object.cc:1368 object.cc:1414 object.cc:1448 #, c-format msgid "%s: ELF file too short" msgstr "" -#: object.cc:1253 +#: object.cc:1376 #, c-format msgid "%s: invalid ELF version 0" msgstr "" -#: object.cc:1255 +#: object.cc:1378 #, c-format msgid "%s: unsupported ELF version %d" msgstr "" -#: object.cc:1262 +#: object.cc:1385 #, c-format msgid "%s: invalid ELF class 0" msgstr "" -#: object.cc:1268 +#: object.cc:1391 #, c-format msgid "%s: unsupported ELF class %d" msgstr "" -#: object.cc:1275 +#: object.cc:1398 #, c-format msgid "%s: invalid ELF data encoding" msgstr "" -#: object.cc:1281 +#: object.cc:1404 #, c-format msgid "%s: unsupported ELF data encoding %d" msgstr "" -#: object.cc:1301 +#: object.cc:1424 #, c-format msgid "%s: not configured to support 32-bit big-endian object" msgstr "" -#: object.cc:1314 +#: object.cc:1437 #, c-format msgid "%s: not configured to support 32-bit little-endian object" msgstr "" -#: object.cc:1335 +#: object.cc:1458 #, c-format msgid "%s: not configured to support 64-bit big-endian object" msgstr "" -#: object.cc:1348 +#: object.cc:1471 #, c-format msgid "%s: not configured to support 64-bit little-endian object" msgstr "" @@ -832,88 +832,107 @@ msgstr "" msgid "%s: -%c: %s\n" msgstr "" -#: options.h:372 +#: options.h:331 +#, c-format +msgid "invalid optimization level: %s" +msgstr "" + +#: options.h:377 #, c-format -msgid "Unsupported argument to --compress-debug-symbols: %s" +msgid "unsupported argument to --compress-debug-sections: %s" msgstr "" -#: options.h:424 +#: options.h:428 #, c-format -msgid "%s: invalid argument to -Ttext: %s\n" +msgid "invalid argument to -Ttext: %s" msgstr "" #: options.h:437 #, c-format -msgid "%s: invalid thread count: %s\n" +msgid "invalid thread count: %s" msgstr "" -#: output.cc:1122 +#: options.h:445 +msgid "--threads not supported" +msgstr "" + +#: output.cc:1467 #, c-format msgid "invalid alignment %lu for section \"%s\"" msgstr "" -#: output.cc:1941 +#: output.cc:2334 #, c-format msgid "%s: open: %s" msgstr "" -#: output.cc:1953 output.cc:1986 +#: output.cc:2354 #, c-format -msgid "%s: munmap: %s" +msgid "%s: mremap: %s" msgstr "" -#: output.cc:1967 +#: output.cc:2390 #, c-format msgid "%s: lseek: %s" msgstr "" -#: output.cc:1970 +#: output.cc:2393 output.cc:2430 #, c-format msgid "%s: write: %s" msgstr "" -#: output.cc:1976 +#: output.cc:2401 #, c-format msgid "%s: mmap: %s" msgstr "" -#: output.cc:1990 +#: output.cc:2411 +#, c-format +msgid "%s: munmap: %s" +msgstr "" + +#: output.cc:2428 +#, c-format +msgid "%s: write: unexpected 0 return-value" +msgstr "" + +#: output.cc:2440 #, c-format msgid "%s: close: %s" msgstr "" -#: readsyms.cc:151 +#: readsyms.cc:150 #, c-format msgid "%s: file is empty" msgstr "" -#: readsyms.cc:186 +#: readsyms.cc:185 #, 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:234 +#: readsyms.cc:244 #, c-format msgid "%s: not an object or archive" msgstr "" -#: reloc.cc:248 reloc.cc:514 +#: reloc.cc:224 reloc.cc:538 #, c-format msgid "relocation section %u uses unexpected symbol table %u" msgstr "" -#: reloc.cc:263 reloc.cc:532 +#: reloc.cc:239 reloc.cc:556 #, c-format msgid "unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:272 reloc.cc:541 +#: reloc.cc:248 reloc.cc:565 #, c-format msgid "reloc section %u size %lu uneven" msgstr "" -#: reloc.cc:732 +#: reloc.cc:759 #, c-format msgid "reloc section size %zu is not a multiple of reloc size %d\n" msgstr "" @@ -948,48 +967,73 @@ msgstr "" #. There are some options that we could handle here--e.g., #. -lLIBRARY. Should we bother? -#: script.cc:1330 +#: script.cc:1333 #, c-format msgid "" "%s: Ignoring command OPTION; OPTION is only valid for scripts specified via -" "T" msgstr "" -#: symtab.cc:597 +#: stringpool.cc:535 +#, c-format +msgid "%s: %s entries: %zu; buckets: %zu\n" +msgstr "" + +#: stringpool.cc:539 +#, c-format +msgid "%s: %s entries: %zu\n" +msgstr "" + +#: stringpool.cc:542 +#, c-format +msgid "%s: %s Stringdata structures: %zu\n" +msgstr "" + +#: symtab.cc:595 #, c-format msgid "bad global symbol name offset %u at %zu" msgstr "" -#: symtab.cc:675 +#: symtab.cc:673 msgid "too few symbol versions" msgstr "" -#: symtab.cc:704 +#: symtab.cc:702 #, c-format msgid "bad symbol name offset %u at %zu" msgstr "" -#: symtab.cc:758 +#: symtab.cc:756 #, c-format msgid "versym for symbol %zu out of range: %u" msgstr "" -#: symtab.cc:766 +#: symtab.cc:764 #, c-format msgid "versym for symbol %zu has no name: %u" msgstr "" -#: symtab.cc:1484 symtab.cc:1697 +#: symtab.cc:1482 symtab.cc:1698 #, c-format msgid "%s: unsupported symbol section 0x%x" msgstr "" -#: symtab.cc:1821 +#: symtab.cc:1822 #, c-format msgid "%s: undefined reference to '%s'" msgstr "" -#: symtab.cc:1962 +#: symtab.cc:1907 +#, c-format +msgid "%s: symbol table entries: %zu; buckets: %zu\n" +msgstr "" + +#: symtab.cc:1910 +#, c-format +msgid "%s: symbol table entries: %zu\n" +msgstr "" + +#: symtab.cc:1979 #, c-format msgid "" "while linking %s: symbol '%s' defined in multiple places (possible ODR " @@ -1024,26 +1068,17 @@ msgid "" "This program has absolutely no warranty.\n" msgstr "" -#: workqueue.cc:484 -#, c-format -msgid "gold task queue:\n" -msgstr "" - -#: workqueue-threads.cc:107 +#: workqueue-threads.cc:106 #, c-format msgid "%s failed: %s" msgstr "" -#: x86_64.cc:1177 +#: x86_64.cc:1278 #, c-format msgid "%s: unsupported REL reloc section" msgstr "" -#: x86_64.cc:1513 -msgid "TLS reloc but no TLS segment" -msgstr "" - -#: x86_64.cc:1576 +#: x86_64.cc:1742 #, c-format msgid "unsupported reloc type %u" msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 5625f59..7966c98 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -39,9 +39,9 @@ namespace gold // If we fail to open the object, then we won't create an Add_symbols // task. However, we still need to unblock the token, or else the // link won't proceed to generate more error messages. We can only -// unblock tokens in the main thread, so we need a dummy task to do -// that. The dummy task has to maintain the right sequence of blocks, -// so we need both this_blocker and next_blocker. +// unblock tokens when the workqueue lock is held, so we need a dummy +// task to do that. The dummy task has to maintain the right sequence +// of blocks, so we need both this_blocker and next_blocker. class Unblock_token : public Task { @@ -56,17 +56,17 @@ class Unblock_token : public Task delete this->this_blocker_; } - Is_runnable_type - is_runnable(Workqueue*) + Task_token* + is_runnable() { if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; + return this->this_blocker_; + return NULL; } - Task_locker* - locks(Workqueue* workqueue) - { return new Task_locker_block(*this->next_blocker_, workqueue); } + void + locks(Task_locker* tl) + { tl->add(this, this->next_blocker_); } void run(Workqueue*) @@ -93,24 +93,23 @@ Read_symbols::~Read_symbols() // ordinary input file immediately. For an archive specified using // -l, we have to wait until the search path is complete. -Task::Is_runnable_type -Read_symbols::is_runnable(Workqueue*) +Task_token* +Read_symbols::is_runnable() { if (this->input_argument_->is_file() && this->input_argument_->file().may_need_search() - && this->dirpath_.token().is_blocked()) - return IS_BLOCKED; + && this->dirpath_->token()->is_blocked()) + return this->dirpath_->token(); - return IS_RUNNABLE; + return NULL; } // Return a Task_locker for a Read_symbols task. We don't need any // locks here. -Task_locker* -Read_symbols::locks(Workqueue*) +void +Read_symbols::locks(Task_locker*) { - return NULL; } // Run a Read_symbols task. @@ -139,7 +138,7 @@ Read_symbols::do_read_symbols(Workqueue* workqueue) } Input_file* input_file = new Input_file(&this->input_argument_->file()); - if (!input_file->open(this->options_, this->dirpath_)) + if (!input_file->open(this->options_, *this->dirpath_, this)) return false; // Read enough of the file to pick up the entire ELF header. @@ -190,15 +189,23 @@ Read_symbols::do_read_symbols(Workqueue* workqueue) Read_symbols_data* sd = new Read_symbols_data; obj->read_symbols(sd); + + // Opening the file locked it, so now we need to unlock it. + // We need to unlock it before queuing the Add_symbols task, + // because the workqueue doesn't know about our lock on the + // file. If we queue the Add_symbols task first, it will be + // stuck on the end of the file lock, but since the + // workqueue doesn't know about that lock, it will never + // release the Add_symbols task. + + input_file->file().unlock(this); + workqueue->queue_front(new Add_symbols(this->input_objects_, this->symtab_, this->layout_, obj, sd, this->this_blocker_, this->next_blocker_)); - // Opening the file locked it, so now we need to unlock it. - input_file->file().unlock(); - return true; } } @@ -210,14 +217,15 @@ Read_symbols::do_read_symbols(Workqueue* workqueue) // This is an archive. Archive* arch = new Archive(this->input_argument_->file().name(), input_file); - arch->setup(); - workqueue->queue(new Add_archive_symbols(this->symtab_, - this->layout_, - this->input_objects_, - arch, - this->input_group_, - this->this_blocker_, - this->next_blocker_)); + arch->setup(this); + + workqueue->queue_front(new Add_archive_symbols(this->symtab_, + this->layout_, + this->input_objects_, + arch, + this->input_group_, + this->this_blocker_, + this->next_blocker_)); return true; } } @@ -251,6 +259,7 @@ Read_symbols::do_group(Workqueue* workqueue) const Input_file_group* group = this->input_argument_->group(); Task_token* this_blocker = this->this_blocker_; + for (Input_file_group::const_iterator p = group->begin(); p != group->end(); ++p) @@ -258,7 +267,7 @@ Read_symbols::do_group(Workqueue* workqueue) const Input_argument* arg = &*p; gold_assert(arg->is_file()); - Task_token* next_blocker = new Task_token(); + Task_token* next_blocker = new Task_token(true); next_blocker->add_blocker(); workqueue->queue(new Read_symbols(this->options_, this->input_objects_, this->symtab_, this->layout_, @@ -319,34 +328,21 @@ Add_symbols::~Add_symbols() // We are blocked by this_blocker_. We block next_blocker_. We also // lock the file. -Task::Is_runnable_type -Add_symbols::is_runnable(Workqueue*) +Task_token* +Add_symbols::is_runnable() { if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) - return IS_BLOCKED; + return this->this_blocker_; if (this->object_->is_locked()) - return IS_LOCKED; - return IS_RUNNABLE; + return this->object_->token(); + return NULL; } -class Add_symbols::Add_symbols_locker : public Task_locker -{ - public: - Add_symbols_locker(Task_token& token, Workqueue* workqueue, - Object* object) - : blocker_(token, workqueue), objlock_(*object) - { } - - private: - Task_locker_block blocker_; - Task_locker_obj<Object> objlock_; -}; - -Task_locker* -Add_symbols::locks(Workqueue* workqueue) +void +Add_symbols::locks(Task_locker* tl) { - return new Add_symbols_locker(*this->next_blocker_, workqueue, - this->object_); + tl->add(this, this->next_blocker_); + tl->add(this, this->object_->token()); } // Add the symbols in the object to the symbol table. @@ -363,6 +359,7 @@ Add_symbols::run(Workqueue*) { this->object_->layout(this->symtab_, this->layout_, this->sd_); this->object_->add_symbols(this->symtab_, this->sd_); + this->object_->release(); } delete this->sd_; this->sd_ = NULL; @@ -380,18 +377,18 @@ Finish_group::~Finish_group() // We need to wait for THIS_BLOCKER_ and unblock NEXT_BLOCKER_. -Task::Is_runnable_type -Finish_group::is_runnable(Workqueue*) +Task_token* +Finish_group::is_runnable() { if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; + return this->this_blocker_; + return NULL; } -Task_locker* -Finish_group::locks(Workqueue* workqueue) +void +Finish_group::locks(Task_locker* tl) { - return new Task_locker_block(*this->next_blocker_, workqueue); + tl->add(this, this->next_blocker_); } // Loop over the archives until there are no new undefined symbols. @@ -408,7 +405,7 @@ Finish_group::run(Workqueue*) p != this->input_group_->end(); ++p) { - Task_lock_obj<Archive> tl(**p); + Task_lock_obj<Archive> tl(this, *p); (*p)->add_symbols(this->symtab_, this->layout_, this->input_objects_); diff --git a/gold/readsyms.h b/gold/readsyms.h index c02a0ee4..7a4db41 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -55,7 +55,7 @@ class Read_symbols : public Task // NEXT_BLOCKER is used to block the next input file from adding // symbols. Read_symbols(const General_options& options, Input_objects* input_objects, - Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath, + Symbol_table* symtab, Layout* layout, Dirsearch* dirpath, const Input_argument* input_argument, Input_group* input_group, Task_token* this_blocker, Task_token* next_blocker) : options_(options), input_objects_(input_objects), symtab_(symtab), @@ -68,11 +68,11 @@ class Read_symbols : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -93,7 +93,7 @@ class Read_symbols : public Task Input_objects* input_objects_; Symbol_table* symtab_; Layout* layout_; - const Dirsearch& dirpath_; + Dirsearch* dirpath_; const Input_argument* input_argument_; Input_group* input_group_; Task_token* this_blocker_; @@ -123,11 +123,11 @@ class Add_symbols : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -137,8 +137,6 @@ class Add_symbols : public Task { return "Add_symbols " + this->object_->name(); } private: - class Add_symbols_locker; - Input_objects* input_objects_; Symbol_table* symtab_; Layout* layout_; @@ -199,11 +197,11 @@ class Finish_group : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); diff --git a/gold/reloc.cc b/gold/reloc.cc index e34cd04..a91c354 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -37,18 +37,18 @@ namespace gold // After reading it, the start another task to process the // information. These tasks requires access to the file. -Task::Is_runnable_type -Read_relocs::is_runnable(Workqueue*) +Task_token* +Read_relocs::is_runnable() { - return this->object_->is_locked() ? IS_LOCKED : IS_RUNNABLE; + return this->object_->is_locked() ? this->object_->token() : NULL; } // Lock the file. -Task_locker* -Read_relocs::locks(Workqueue*) +void +Read_relocs::locks(Task_locker* tl) { - return new Task_locker_obj<Object>(*this->object_); + tl->add(this, this->object_->token()); } // Read the relocations and then start a Scan_relocs_task. @@ -58,6 +58,8 @@ Read_relocs::run(Workqueue* workqueue) { Read_relocs_data *rd = new Read_relocs_data; this->object_->read_relocs(rd); + this->object_->release(); + workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, this->layout_, this->object_, rd, this->symtab_lock_, this->blocker_)); @@ -78,37 +80,25 @@ Read_relocs::get_name() const // use a lock on the symbol table to keep them from interfering with // each other. -Task::Is_runnable_type -Scan_relocs::is_runnable(Workqueue*) +Task_token* +Scan_relocs::is_runnable() { - if (!this->symtab_lock_->is_writable() || this->object_->is_locked()) - return IS_LOCKED; - return IS_RUNNABLE; + if (!this->symtab_lock_->is_writable()) + return this->symtab_lock_; + if (this->object_->is_locked()) + return this->object_->token(); + return NULL; } // Return the locks we hold: one on the file, one on the symbol table // and one blocker. -class Scan_relocs::Scan_relocs_locker : public Task_locker -{ - public: - Scan_relocs_locker(Object* object, Task_token& symtab_lock, Task* task, - Task_token& blocker, Workqueue* workqueue) - : objlock_(*object), symtab_locker_(symtab_lock, task), - blocker_(blocker, workqueue) - { } - - private: - Task_locker_obj<Object> objlock_; - Task_locker_write symtab_locker_; - Task_locker_block blocker_; -}; - -Task_locker* -Scan_relocs::locks(Workqueue* workqueue) +void +Scan_relocs::locks(Task_locker* tl) { - return new Scan_relocs_locker(this->object_, *this->symtab_lock_, this, - *this->blocker_, workqueue); + tl->add(this, this->object_->token()); + tl->add(this, this->symtab_lock_); + tl->add(this, this->blocker_); } // Scan the relocs. @@ -118,6 +108,7 @@ Scan_relocs::run(Workqueue*) { this->object_->scan_relocs(this->options_, this->symtab_, this->layout_, this->rd_); + this->object_->release(); delete this->rd_; this->rd_ = NULL; } @@ -134,46 +125,30 @@ Scan_relocs::get_name() const // We may have to wait for the output sections to be written. -Task::Is_runnable_type -Relocate_task::is_runnable(Workqueue*) +Task_token* +Relocate_task::is_runnable() { if (this->object_->relocs_must_follow_section_writes() && this->output_sections_blocker_->is_blocked()) - return IS_BLOCKED; + return this->output_sections_blocker_; if (this->object_->is_locked()) - return IS_LOCKED; + return this->object_->token(); - return IS_RUNNABLE; + return NULL; } // We want to lock the file while we run. We want to unblock // INPUT_SECTIONS_BLOCKER and FINAL_BLOCKER when we are done. +// INPUT_SECTIONS_BLOCKER may be NULL. -class Relocate_task::Relocate_locker : public Task_locker -{ - public: - Relocate_locker(Task_token& input_sections_blocker, - Task_token& final_blocker, Workqueue* workqueue, - Object* object) - : input_sections_blocker_(input_sections_blocker, workqueue), - final_blocker_(final_blocker, workqueue), - objlock_(*object) - { } - - private: - Task_block_token input_sections_blocker_; - Task_block_token final_blocker_; - Task_locker_obj<Object> objlock_; -}; - -Task_locker* -Relocate_task::locks(Workqueue* workqueue) +void +Relocate_task::locks(Task_locker* tl) { - return new Relocate_locker(*this->input_sections_blocker_, - *this->final_blocker_, - workqueue, - this->object_); + if (this->input_sections_blocker_ != NULL) + tl->add(this, this->input_sections_blocker_); + tl->add(this, this->final_blocker_); + tl->add(this, this->object_->token()); } // Run the task. @@ -183,6 +158,7 @@ Relocate_task::run(Workqueue*) { this->object_->relocate(this->options_, this->symtab_, this->layout_, this->of_); + this->object_->release(); } // Return a debugging name for the task. @@ -401,10 +377,10 @@ template<int size, bool big_endian> void Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs, Output_file* of, - Views* pviews) + Views* pviews) const { unsigned int shnum = this->shnum(); - std::vector<Map_to_output>& map_sections(this->map_to_output()); + const std::vector<Map_to_output>& map_sections(this->map_to_output()); const unsigned char* p = pshdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) @@ -521,7 +497,7 @@ Sized_relobj<size, big_endian>::relocate_sections( unsigned int shnum = this->shnum(); Sized_target<size, big_endian>* target = this->sized_target(); - std::vector<Map_to_output>& map_sections(this->map_to_output()); + const std::vector<Map_to_output>& map_sections(this->map_to_output()); Relocate_info<size, big_endian> relinfo; relinfo.options = &options; diff --git a/gold/reloc.h b/gold/reloc.h index d84dc88..c997030 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -23,6 +23,7 @@ #ifndef GOLD_RELOC_H #define GOLD_RELOC_H +#include <vector> #include <byteswap.h> #include "elfcpp.h" @@ -69,11 +70,11 @@ class Read_relocs : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -107,11 +108,11 @@ class Scan_relocs : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -120,8 +121,6 @@ class Scan_relocs : public Task get_name() const; private: - class Scan_relocs_locker; - const General_options& options_; Symbol_table* symtab_; Layout* layout_; @@ -148,11 +147,11 @@ class Relocate_task : public Task // The standard Task methods. - Is_runnable_type - is_runnable(Workqueue*); + Task_token* + is_runnable(); - Task_locker* - locks(Workqueue*); + void + locks(Task_locker*); void run(Workqueue*); @@ -161,8 +160,6 @@ class Relocate_task : public Task get_name() const; private: - class Relocate_locker; - const General_options& options_; const Symbol_table* symtab_; const Layout* layout_; diff --git a/gold/script.cc b/gold/script.cc index 1ebd100..1373064 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -797,19 +797,17 @@ class Script_unblock : public Task delete this->this_blocker_; } - Is_runnable_type - is_runnable(Workqueue*) + Task_token* + is_runnable() { if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; + return this->this_blocker_; + return NULL; } - Task_locker* - locks(Workqueue* workqueue) - { - return new Task_locker_block(*this->next_blocker_, workqueue); - } + void + locks(Task_locker* tl) + { tl->add(this, this->next_blocker_); } void run(Workqueue*) @@ -826,8 +824,8 @@ class Script_unblock : public Task // This class holds data passed through the parser to the lexer and to // the parser support functions. This avoids global variables. We -// can't use global variables because we need not be called in the -// main thread. +// can't use global variables because we need not be called by a +// singleton thread. class Parser_closure { @@ -927,7 +925,7 @@ class Parser_closure bool read_input_script(Workqueue* workqueue, const General_options& options, Symbol_table* symtab, Layout* layout, - const Dirsearch& dirsearch, Input_objects* input_objects, + Dirsearch* dirsearch, Input_objects* input_objects, Input_group* input_group, const Input_argument* input_argument, Input_file* input_file, const unsigned char*, off_t, @@ -956,7 +954,7 @@ read_input_script(Workqueue* workqueue, const General_options& options, { // The script did not add any files to read. Note that we are // not permitted to call NEXT_BLOCKER->unblock() here even if - // THIS_BLOCKER is NULL, as we are not in the main thread. + // THIS_BLOCKER is NULL, as we do not hold the workqueue lock. workqueue->queue(new Script_unblock(this_blocker, next_blocker)); return true; } @@ -970,7 +968,7 @@ read_input_script(Workqueue* workqueue, const General_options& options, nb = next_blocker; else { - nb = new Task_token(); + nb = new Task_token(true); nb->add_blocker(); } workqueue->queue(new Read_symbols(options, input_objects, symtab, @@ -992,17 +990,22 @@ read_commandline_script(const char* filename, Command_line* cmdline) // using "." + cmdline->options()->search_path() -- not dirsearch. Dirsearch dirsearch; + // The file locking code wants to record a Task, but we haven't + // started the workqueue yet. This is only for debugging purposes, + // so we invent a fake value. + const Task* task = reinterpret_cast<const Task*>(-1); + Input_file_argument input_argument(filename, false, "", cmdline->position_dependent_options()); Input_file input_file(&input_argument); - if (!input_file.open(cmdline->options(), dirsearch)) + if (!input_file.open(cmdline->options(), dirsearch, task)) return false; Lex lex(&input_file); if (lex.tokenize().is_invalid()) { // Opening the file locked it, so now we need to unlock it. - input_file.file().unlock(); + input_file.file().unlock(task); return false; } @@ -1014,11 +1017,11 @@ read_commandline_script(const char* filename, Command_line* cmdline) &lex.tokens()); if (yyparse(&closure) != 0) { - input_file.file().unlock(); + input_file.file().unlock(task); return false; } - input_file.file().unlock(); + input_file.file().unlock(task); return true; } diff --git a/gold/script.h b/gold/script.h index 16483a0..16caf03 100644 --- a/gold/script.h +++ b/gold/script.h @@ -52,7 +52,7 @@ class Workqueue; bool read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*, - const Dirsearch&, Input_objects*, Input_group*, + Dirsearch*, Input_objects*, Input_group*, const Input_argument*, Input_file*, const unsigned char* p, off_t bytes, Task_token* this_blocker, Task_token* next_blocker); diff --git a/gold/symtab.cc b/gold/symtab.cc index 770628c..7868c39 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -1392,8 +1392,8 @@ Symbol_table::set_dynsym_indexes(const Target* target, // OFF. Add their names to POOL. Return the new file offset. off_t -Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff, - size_t dyn_global_index, size_t dyncount, +Symbol_table::finalize(const Task* task, unsigned int index, off_t off, + off_t dynoff, size_t dyn_global_index, size_t dyncount, Stringpool* pool) { off_t ret; @@ -1426,7 +1426,7 @@ Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff, // Now that we have the final symbol table, we can reliably note // which symbols should get warnings. - this->warnings_.note_warnings(this); + this->warnings_.note_warnings(this, task); return ret; } @@ -1945,7 +1945,8 @@ struct Odr_violation_compare // but apparently different definitions (different source-file/line-no). void -Symbol_table::detect_odr_violations(const char* output_file_name) const +Symbol_table::detect_odr_violations(const Task* task, + const char* output_file_name) const { for (Odr_map::const_iterator it = candidate_odr_violations_.begin(); it != candidate_odr_violations_.end(); @@ -1961,14 +1962,14 @@ Symbol_table::detect_odr_violations(const char* output_file_name) const ++locs) { // We need to lock the object in order to read it. This - // means that we can not run inside a Task. If we want to - // run this in a Task for better performance, we will need - // one Task for object, plus appropriate locking to ensure - // that we don't conflict with other uses of the object. - locs->object->lock(); + // means that we have to run in a singleton Task. If we + // want to run this in a general Task for better + // performance, we will need one Task for object, plus + // appropriate locking to ensure that we don't conflict with + // other uses of the object. + Task_lock_obj<Object> tl(task, locs->object); std::string lineno = Dwarf_line_info::one_addr2line( locs->object, locs->shndx, locs->offset); - locs->object->unlock(); if (!lineno.empty()) line_nums.insert(lineno); } @@ -2003,7 +2004,7 @@ Warnings::add_warning(Symbol_table* symtab, const char* name, Object* obj, // sources for all the symbols. void -Warnings::note_warnings(Symbol_table* symtab) +Warnings::note_warnings(Symbol_table* symtab, const Task* task) { for (Warning_table::iterator p = this->warnings_.begin(); p != this->warnings_.end(); @@ -2023,7 +2024,7 @@ Warnings::note_warnings(Symbol_table* symtab) // the object then, as we might try to issue the same // warning multiple times simultaneously. { - Task_locker_obj<Object> tl(*p->second.object); + Task_lock_obj<Object> tl(task, p->second.object); const unsigned char* c; off_t len; c = p->second.object->section_contents(p->second.shndx, &len, diff --git a/gold/symtab.h b/gold/symtab.h index cb3be9b..244783e 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -905,7 +905,7 @@ class Warnings // For each symbol for which we should give a warning, make a note // on the symbol. void - note_warnings(Symbol_table* symtab); + note_warnings(Symbol_table* symtab, const Task*); // Issue a warning for a reference to SYM at RELINFO's location. template<int size, bool big_endian> @@ -1078,7 +1078,7 @@ class Symbol_table // Check candidate_odr_violations_ to find symbols with the same name // but apparently different definitions (different source-file/line-no). void - detect_odr_violations(const char* output_file_name) const; + detect_odr_violations(const Task*, const char* output_file_name) const; // SYM is defined using a COPY reloc. Return the dynamic object // where the original definition was found. @@ -1102,7 +1102,7 @@ class Symbol_table // symbol, and DYNCOUNT is the number of global dynamic symbols. // This records the parameters, and returns the new file offset. off_t - finalize(unsigned int index, off_t off, off_t dynoff, + finalize(const Task*, unsigned int index, off_t off, off_t dynoff, size_t dyn_global_index, size_t dyncount, Stringpool* pool); // Write out the global symbols. diff --git a/gold/testsuite/object_unittest.cc b/gold/testsuite/object_unittest.cc index 9b03425..4510e0c 100644 --- a/gold/testsuite/object_unittest.cc +++ b/gold/testsuite/object_unittest.cc @@ -39,22 +39,24 @@ bool Sized_object_test(const unsigned char* test_file, unsigned int test_file_size, Target* target_test_pointer) { - Input_file input_file("test.o", test_file, test_file_size); + // We need a pretend Task. + const Task* task = reinterpret_cast<const Task*>(-1); + Input_file input_file(task, "test.o", test_file, test_file_size); Object* object = make_elf_object("test.o", &input_file, 0, test_file, test_file_size); CHECK(object->name() == "test.o"); CHECK(!object->is_dynamic()); CHECK(object->target() == target_test_pointer); CHECK(object->is_locked()); - object->unlock(); + object->unlock(task); CHECK(!object->is_locked()); - object->lock(); + object->lock(task); CHECK(object->shnum() == 5); CHECK(object->section_name(0).empty()); CHECK(object->section_name(1) == ".test"); CHECK(object->section_flags(0) == 0); CHECK(object->section_flags(1) == elfcpp::SHF_ALLOC); - object->unlock(); + object->unlock(task); return true; } diff --git a/gold/token.h b/gold/token.h new file mode 100644 index 0000000..49a7d51 --- /dev/null +++ b/gold/token.h @@ -0,0 +1,316 @@ +// token.h -- lock tokens 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_TOKEN_H +#define GOLD_TOKEN_H + +namespace gold +{ + +class Condvar; +class Task; + +// A list of Tasks, managed through the next_locked_ field in the +// class Task. We define this class here because we need it in +// Task_token. + +class Task_list +{ + public: + Task_list() + : head_(NULL), tail_(NULL) + { } + + ~Task_list() + { gold_assert(this->head_ == NULL && this->tail_ == NULL); } + + // Return whether the list is empty. + bool + empty() const + { return this->head_ == NULL; } + + // Add T to the end of the list. + void + push_back(Task* t); + + // Remove the first Task on the list and return it. Return NULL if + // the list is empty. + Task* + pop_front(); + + private: + // The start of the list. NULL if the list is empty. + Task* head_; + // The end of the list. NULL if the list is empty. + Task* tail_; +}; + +// We support two basic types of locks, which are both implemented +// using the single class Task_token. + +// A write lock may be held by a single Task at a time. This is used +// to control access to a single shared resource such as an Object. + +// A blocker is used to indicate that a Task A must be run after some +// set of Tasks B. For each of the Tasks B, we increment the blocker +// when the Task is created, and decrement it when the Task is +// completed. When the count goes to 0, the task A is ready to run. + +// There are no shared read locks. We always read and write objects +// in predictable patterns. The purpose of the locks is to permit +// some flexibility for the threading system, for cases where the +// execution order does not matter. + +// These tokens are only manipulated when the workqueue lock is held +// or when they are first created. They do not require any locking +// themselves. + +class Task_token +{ + public: + Task_token(bool is_blocker) + : is_blocker_(is_blocker), blockers_(0), writer_(NULL), waiting_() + { } + + ~Task_token() + { + gold_assert(this->blockers_ == 0); + gold_assert(this->writer_ == NULL); + } + + // Return whether this is a blocker. + bool + is_blocker() const + { return this->is_blocker_; } + + // A write lock token uses these methods. + + // Is the token writable? + bool + is_writable() const + { + gold_assert(!this->is_blocker_); + return this->writer_ == NULL; + } + + // Add the task as the token's writer (there may only be one + // writer). + void + add_writer(const Task* t) + { + gold_assert(!this->is_blocker_ && this->writer_ == NULL); + this->writer_ = t; + } + + // Remove the task as the token's writer. + void + remove_writer(const Task* t) + { + gold_assert(!this->is_blocker_ && this->writer_ == t); + this->writer_ = NULL; + } + + // A blocker token uses these methods. + + // Add a blocker to the token. + void + add_blocker() + { + gold_assert(this->is_blocker_); + ++this->blockers_; + this->writer_ = NULL; + } + + // Remove a blocker from the token. Returns true if block count + // drops to zero. + bool + remove_blocker() + { + gold_assert(this->is_blocker_ && this->blockers_ > 0); + --this->blockers_; + this->writer_ = NULL; + return this->blockers_ == 0; + } + + // Is the token currently blocked? + bool + is_blocked() const + { + gold_assert(this->is_blocker_); + return this->blockers_ > 0; + } + + // Both blocker and write lock tokens use these methods. + + // Add T to the list of tasks waiting for this token to be released. + void + add_waiting(Task* t) + { this->waiting_.push_back(t); } + + // Remove the first Task waiting for this token to be released, and + // return it. Return NULL if no Tasks are waiting. + Task* + remove_first_waiting() + { return this->waiting_.pop_front(); } + + private: + // It makes no sense to copy these. + Task_token(const Task_token&); + Task_token& operator=(const Task_token&); + + // Whether this is a blocker token. + bool is_blocker_; + // The number of blockers. + int blockers_; + // The single writer. + const Task* writer_; + // The list of Tasks waiting for this token to be released. + Task_list waiting_; +}; + +// In order to support tokens more reliably, we provide objects which +// handle them using RAII. + +// RAII class to get a write lock on a token. This requires +// specifying the task which is doing the lock. + +class Task_write_token +{ + public: + Task_write_token(Task_token* token, const Task* task) + : token_(token), task_(task) + { this->token_->add_writer(this->task_); } + + ~Task_write_token() + { this->token_->remove_writer(this->task_); } + + private: + Task_write_token(const Task_write_token&); + Task_write_token& operator=(const Task_write_token&); + + Task_token* token_; + const Task* task_; +}; + +// RAII class for a blocker. + +class Task_block_token +{ + public: + // The blocker count must be incremented when the task is created. + // This object is created when the task is run, so we don't do + // anything in the constructor. + Task_block_token(Task_token* token) + : token_(token) + { gold_assert(this->token_->is_blocked()); } + + ~Task_block_token() + { this->token_->remove_blocker(); } + + private: + Task_block_token(const Task_block_token&); + Task_block_token& operator=(const Task_block_token&); + + Task_token* token_; +}; + +// An object which implements an RAII lock for any object which +// supports lock and unlock methods. + +template<typename Obj> +class Task_lock_obj +{ + public: + Task_lock_obj(const Task* task, Obj* obj) + : task_(task), obj_(obj) + { this->obj_->lock(task); } + + ~Task_lock_obj() + { this->obj_->unlock(this->task_); } + + private: + Task_lock_obj(const Task_lock_obj&); + Task_lock_obj& operator=(const Task_lock_obj&); + + const Task* task_; + Obj* obj_; +}; + +// A class which holds the set of Task_tokens which must be locked for +// a Task. No Task requires more than four Task_tokens, so we set +// that as a limit. + +class Task_locker +{ + public: + static const int max_task_count = 4; + + Task_locker() + : count_(0) + { } + + ~Task_locker() + { } + + // Clear the locker. + void + clear() + { this->count_ = 0; } + + // Add a token to the locker. + void + add(Task* t, Task_token* token) + { + gold_assert(this->count_ < max_task_count); + this->tokens_[this->count_] = token; + ++this->count_; + // A blocker will have been incremented when the task is created. + // A writer we need to lock now. + if (!token->is_blocker()) + token->add_writer(t); + } + + // Iterate over the tokens. + + typedef Task_token** iterator; + + iterator + begin() + { return &this->tokens_[0]; } + + iterator + end() + { return &this->tokens_[this->count_]; } + + private: + Task_locker(const Task_locker&); + Task_locker& operator=(const Task_locker&); + + // The number of tokens. + int count_; + // The tokens. + Task_token* tokens_[max_task_count]; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_TOKEN_H) diff --git a/gold/workqueue-internal.h b/gold/workqueue-internal.h index d9fd160..201caa4 100644 --- a/gold/workqueue-internal.h +++ b/gold/workqueue-internal.h @@ -24,6 +24,7 @@ #define GOLD_WORKQUEUE_INTERNAL_H #include <queue> +#include <csignal> #include "gold-threads.h" #include "workqueue.h" @@ -36,92 +37,71 @@ 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. +// The Workqueue_threader abstract class. This is the interface used +// by the general workqueue code to manage threads. -class Workqueue_runner +class Workqueue_threader { public: - Workqueue_runner(Workqueue* workqueue) + Workqueue_threader(Workqueue* workqueue) : workqueue_(workqueue) { } - virtual ~Workqueue_runner() + virtual ~Workqueue_threader() { } - // 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); } + // Return whether to cancel the current thread. + virtual bool + should_cancel_thread() = 0; - Workqueue* get_workqueue() const + protected: + // Get the Workqueue. + Workqueue* + get_workqueue() { return this->workqueue_; } private: + // The Workqueue. Workqueue* workqueue_; }; -// The threaded instantiation of Workqueue_runner. +// The threaded instantiation of Workqueue_threader. -class Workqueue_runner_threadpool : public Workqueue_runner +class Workqueue_threader_threadpool : public Workqueue_threader { public: - Workqueue_runner_threadpool(Workqueue* workqueue); + Workqueue_threader_threadpool(Workqueue*); - ~Workqueue_runner_threadpool(); - - void - run(Task*, Task_locker*); + ~Workqueue_threader_threadpool(); + // Set the thread count. 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. + // Return whether to cancel a thread. bool - get_next(Task**, Task_locker**); + should_cancel_thread(); - // This is called when the thread completes a task. + // Process all tasks. This keeps running until told to cancel. 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; + process(int thread_number) + { this->get_workqueue()->process(thread_number); } - // A queue of tasks to run. - typedef std::queue<Task_queue_entry> Task_queue; + private: + // This is set if we need to check the thread count. + volatile sig_atomic_t check_thread_count_; - // 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 for the remaining members. 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_; + // The number of threads we want to create. This is set to zero + // when all threads should exit. + int desired_thread_count_; + // The number of threads currently running. + int threads_; }; } // End namespace gold. diff --git a/gold/workqueue-threads.cc b/gold/workqueue-threads.cc index 3c30b32..ae09e29 100644 --- a/gold/workqueue-threads.cc +++ b/gold/workqueue-threads.cc @@ -44,7 +44,7 @@ namespace gold class Workqueue_thread { public: - Workqueue_thread(Workqueue_runner_threadpool*); + Workqueue_thread(Workqueue_threader_threadpool*, int thread_number); ~Workqueue_thread(); @@ -62,20 +62,19 @@ class Workqueue_thread 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_; + Workqueue_threader_threadpool* threadpool_; + // The thread number. + int thread_number_; // The thread ID. pthread_t tid_; }; // Create the thread in the constructor. -Workqueue_thread::Workqueue_thread(Workqueue_runner_threadpool* threadpool) - : threadpool_(threadpool) +Workqueue_thread::Workqueue_thread(Workqueue_threader_threadpool* threadpool, + int thread_number) + : threadpool_(threadpool), thread_number_(thread_number) { pthread_attr_t attr; int err = pthread_attr_init(&attr); @@ -114,7 +113,8 @@ void* Workqueue_thread::thread_body(void* arg) { Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg); - pwt->run(); + + pwt->threadpool_->process(pwt->thread_number_); // Delete the thread object as we exit. delete pwt; @@ -122,144 +122,75 @@ Workqueue_thread::thread_body(void* arg) 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. +// Class Workqueue_threader_threadpool. // Constructor. -Workqueue_runner_threadpool::Workqueue_runner_threadpool(Workqueue* workqueue) - : Workqueue_runner(workqueue), - desired_thread_count_(0), +Workqueue_threader_threadpool::Workqueue_threader_threadpool( + Workqueue* workqueue) + : Workqueue_threader(workqueue), + check_thread_count_(0), lock_(), - actual_thread_count_(0), - running_thread_count_(0), - task_queue_(), - task_queue_condvar_(this->lock_) + desired_thread_count_(1), + threads_(1) { } // Destructor. -Workqueue_runner_threadpool::~Workqueue_runner_threadpool() +Workqueue_threader_threadpool::~Workqueue_threader_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 it 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(); + this->get_workqueue()->set_thread_count(0); } -// Set the thread count. This is only called in the main thread, and -// is only called when there are no threads running. +// Set the thread count. 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) +Workqueue_threader_threadpool::set_thread_count(int thread_count) { - 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; - } + int create; + { + Hold_lock hl(this->lock_); - while (this->task_queue_.empty() && this->desired_thread_count_ > 0) - { - // Wait for a new task to become available. - this->task_queue_condvar_.wait(); - } + this->desired_thread_count_ = thread_count; + create = this->desired_thread_count_ - this->threads_; + if (create < 0) + this->check_thread_count_ = 1; + } - // Check whether we are exiting. - if (this->desired_thread_count_ == 0) + if (create > 0) { - gold_assert(this->actual_thread_count_ > 0); - --this->actual_thread_count_; - return false; + for (int i = 0; i < create; ++i) + { + // Note that threads delete themselves when they exit, so we + // don't keep pointers to them. + new Workqueue_thread(this, this->threads_); + ++this->threads_; + } } - - *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. +// Return whether the current thread should be cancelled. -void -Workqueue_runner_threadpool::thread_completed(Task* t, Task_locker* tl) +bool +Workqueue_threader_threadpool::should_cancel_thread() { + // Fast exit without taking a lock. + if (!this->check_thread_count_) + return false; + { Hold_lock hl(this->lock_); - gold_assert(this->actual_thread_count_ > 0); - gold_assert(this->running_thread_count_ > 0); - --this->running_thread_count_; + if (this->threads_ > this->desired_thread_count_) + { + --this->threads_; + return true; + } + this->check_thread_count_ = 0; } - this->completed(t, tl); + return false; } } // End namespace gold. diff --git a/gold/workqueue.cc b/gold/workqueue.cc index 018c96b..647daf2 100644 --- a/gold/workqueue.cc +++ b/gold/workqueue.cc @@ -23,181 +23,99 @@ #include "gold.h" #include "debug.h" +#include "options.h" #include "workqueue.h" #include "workqueue-internal.h" namespace gold { -// Task_token methods. +// Class Task_list. -Task_token::Task_token() - : is_blocker_(false), readers_(0), writer_(NULL) -{ -} - -Task_token::~Task_token() -{ - gold_assert(this->readers_ == 0 && this->writer_ == NULL); -} - -bool -Task_token::is_readable() const -{ - gold_assert(!this->is_blocker_); - return this->writer_ == NULL; -} - -void -Task_token::add_reader() -{ - gold_assert(!this->is_blocker_); - gold_assert(this->is_readable()); - ++this->readers_; -} - -void -Task_token::remove_reader() -{ - gold_assert(!this->is_blocker_); - gold_assert(this->readers_ > 0); - --this->readers_; -} - -bool -Task_token::is_writable() const -{ - gold_assert(!this->is_blocker_); - return this->writer_ == NULL && this->readers_ == 0; -} - -void -Task_token::add_writer(const Task* t) -{ - gold_assert(!this->is_blocker_); - gold_assert(this->is_writable()); - this->writer_ = t; -} - -void -Task_token::remove_writer(const Task* t) -{ - gold_assert(!this->is_blocker_); - gold_assert(this->writer_ == t); - this->writer_ = NULL; -} - -bool -Task_token::has_write_lock(const Task* t) -{ - gold_assert(!this->is_blocker_); - return this->writer_ == t; -} +// Add T to the end of the list. -// For blockers, we just use the readers_ field. - -void -Task_token::add_blocker() +inline void +Task_list::push_back(Task* t) { - if (this->readers_ == 0 && this->writer_ == NULL) - this->is_blocker_ = true; + gold_assert(t->list_next() == NULL); + if (this->head_ == NULL) + { + this->head_ = t; + this->tail_ = t; + } else - gold_assert(this->is_blocker_); - ++this->readers_; -} - -bool -Task_token::remove_blocker() -{ - gold_assert(this->is_blocker_ && this->readers_ > 0); - --this->readers_; - return this->readers_ == 0; -} - -bool -Task_token::is_blocked() const -{ - gold_assert(this->is_blocker_ - || (this->readers_ == 0 && this->writer_ == NULL)); - return this->readers_ > 0; + { + this->tail_->set_list_next(t); + this->tail_ = t; + } } -// The Task_block_token class. +// Remove and return the first Task waiting for this lock to be +// released. -Task_block_token::Task_block_token(Task_token& token, Workqueue* workqueue) - : token_(token), workqueue_(workqueue) +inline Task* +Task_list::pop_front() { - // We must increment the block count when the task is created and - // put on the queue. This object is created when the task is run, - // so we don't increment the block count here. - gold_assert(this->token_.is_blocked()); -} - -Task_block_token::~Task_block_token() -{ - if (this->token_.remove_blocker()) + Task* ret = this->head_; + if (ret != NULL) { - // Tell the workqueue that a blocker was cleared. This is - // always called in the main thread, so no locking is required. - this->workqueue_->cleared_blocker(); + if (ret == this->tail_) + { + gold_assert(ret->list_next() == NULL); + this->head_ = NULL; + this->tail_ = NULL; + } + else + { + this->head_ = ret->list_next(); + gold_assert(this->head_ != NULL); + ret->clear_list_next(); + } } + return ret; } -// The simple single-threaded implementation of Workqueue_runner. +// The simple single-threaded implementation of Workqueue_threader. -class Workqueue_runner_single : public Workqueue_runner +class Workqueue_threader_single : public Workqueue_threader { public: - Workqueue_runner_single(Workqueue* workqueue) - : Workqueue_runner(workqueue) + Workqueue_threader_single(Workqueue* workqueue) + : Workqueue_threader(workqueue) { } - ~Workqueue_runner_single() + ~Workqueue_threader_single() { } void - run(Task*, Task_locker*); + set_thread_count(int thread_count) + { gold_assert(thread_count > 0); } - void - set_thread_count(int); + bool + should_cancel_thread() + { return false; } }; -void -Workqueue_runner_single::run(Task* t, Task_locker* tl) -{ - t->run(this->get_workqueue()); - this->completed(t, tl); -} - -void -Workqueue_runner_single::set_thread_count(int thread_count) -{ - gold_assert(thread_count > 0); -} - // Workqueue methods. Workqueue::Workqueue(const General_options& options) - : tasks_lock_(), + : lock_(), first_tasks_(), tasks_(), - completed_lock_(), - completed_(), running_(0), - queued_(0), - completed_condvar_(this->completed_lock_), - cleared_blockers_(0), - desired_thread_count_(1) + waiting_(0), + condvar_(this->lock_), + threader_(NULL) { bool threads = options.threads(); #ifndef ENABLE_THREADS threads = false; #endif if (!threads) - this->runner_ = new Workqueue_runner_single(this); + this->threader_ = new Workqueue_threader_single(this); else { #ifdef ENABLE_THREADS - this->runner_ = new Workqueue_runner_threadpool(this); + this->threader_ = new Workqueue_threader_threadpool(this); #else gold_unreachable(); #endif @@ -206,10 +124,28 @@ Workqueue::Workqueue(const General_options& options) Workqueue::~Workqueue() { - gold_assert(this->first_tasks_.empty()); - gold_assert(this->tasks_.empty()); - gold_assert(this->completed_.empty()); - gold_assert(this->running_ == 0); +} + +// Add a task to the end of a specific queue, or put it on the list +// waiting for a Token. + +void +Workqueue::add_to_queue(Task_list* queue, Task* t) +{ + Hold_lock hl(this->lock_); + + Task_token* token = t->is_runnable(); + if (token != NULL) + { + token->add_waiting(t); + ++this->waiting_; + } + else + { + queue->push_back(t); + // Tell any waiting thread that there is work to do. + this->condvar_.signal(); + } } // Add a task to the queue. @@ -217,14 +153,7 @@ Workqueue::~Workqueue() void Workqueue::queue(Task* t) { - { - Hold_lock hl(this->tasks_lock_); - this->tasks_.push_back(t); - } - { - Hold_lock hl(this->completed_lock_); - ++this->queued_; - } + this->add_to_queue(&this->tasks_, t); } // Add a task to the front of the queue. @@ -232,278 +161,304 @@ Workqueue::queue(Task* t) void Workqueue::queue_front(Task* t) { - { - Hold_lock hl(this->tasks_lock_); - this->first_tasks_.push_front(t); - } - { - Hold_lock hl(this->completed_lock_); - ++this->queued_; - } + t->set_should_run_soon(); + this->add_to_queue(&this->first_tasks_, t); } -// Clear the list of completed tasks. Return whether we cleared -// anything. The completed_lock_ must be held when this is called. +// Return whether to cancel the current thread. -bool -Workqueue::clear_completed() +inline bool +Workqueue::should_cancel_thread() { - if (this->completed_.empty()) - return false; - do - { - delete this->completed_.front(); - this->completed_.pop_front(); - } - while (!this->completed_.empty()); - return true; + return this->threader_->should_cancel_thread(); } -// Find a runnable task in TASKS, which is non-empty. Return NULL if -// none could be found. The tasks_lock_ must be held when this is -// called. Sets ALL_BLOCKED if all non-runnable tasks are waiting on -// a blocker. +// Find a runnable task in TASKS. Return NULL if none could be found. +// If we find a Task waiting for a Token, add it to the list for that +// Token. The workqueue lock must be held when this is called. Task* -Workqueue::find_runnable(Task_list* tasks, bool* all_blocked) +Workqueue::find_runnable_in_list(Task_list* tasks) { - Task* tlast = tasks->back(); - *all_blocked = true; Task* t; - do + while ((t = tasks->pop_front()) != NULL) { - t = tasks->front(); - tasks->pop_front(); + Task_token* token = t->is_runnable(); + + if (token == NULL) + return t; + + token->add_waiting(t); + ++this->waiting_; + } + + // We couldn't find any runnable task. + return NULL; +} + +// Find a runnable task. Return NULL if none could be found. The +// workqueue lock must be held when this is called. - Task::Is_runnable_type is_runnable = t->is_runnable(this); - if (is_runnable == Task::IS_RUNNABLE) +Task* +Workqueue::find_runnable() +{ + Task* t = this->find_runnable_in_list(&this->first_tasks_); + if (t == NULL) + t = this->find_runnable_in_list(&this->tasks_); + return t; +} + +// Find a runnable a task, and wait until we find one. Return NULL if +// we should exit. The workqueue lock must be held when this is +// called. + +Task* +Workqueue::find_runnable_or_wait(int thread_number) +{ + Task* t = this->find_runnable(); + + while (t == NULL) + { + if (this->running_ == 0 + && this->first_tasks_.empty() + && this->tasks_.empty()) { - { - Hold_lock hl(this->completed_lock_); - --this->queued_; - } + // Kick all the threads to make them exit. + this->condvar_.broadcast(); - return t; + gold_assert(this->waiting_ == 0); + return NULL; } - if (is_runnable != Task::IS_BLOCKED) - *all_blocked = false; + if (this->should_cancel_thread()) + return NULL; + + gold_debug(DEBUG_TASK, "%3d sleeping", thread_number); - tasks->push_back(t); + this->condvar_.wait(); + + gold_debug(DEBUG_TASK, "%3d awake", thread_number); + + t = this->find_runnable(); } - while (t != tlast); - // We couldn't find any runnable task. - return NULL; + return t; } -// Process all the tasks on the workqueue. This is the main loop in -// the linker. Note that as we process tasks, new tasks will be -// added. +// Find and run tasks. If we can't find a runnable task, wait for one +// to become available. If we run a task, and it frees up another +// runnable task, then run that one too. This returns true if we +// should look for another task, false if we are cancelling this +// thread. -void -Workqueue::process() +bool +Workqueue::find_and_run_task(int thread_number) { - while (true) + Task* t; + Task_locker tl; + + { + Hold_lock hl(this->lock_); + + // Find a runnable task. + t = this->find_runnable_or_wait(thread_number); + + if (t == NULL) + return false; + + // Get the locks for the task. This must be called while we are + // still holding the Workqueue lock. + t->locks(&tl); + + ++this->running_; + } + + while (t != NULL) { - Task* t; - bool empty; - bool all_blocked; + gold_debug(DEBUG_TASK, "%3d running task %s", thread_number, + t->name().c_str()); - // Don't start more tasks than desired. - { - Hold_lock hl(this->completed_lock_); + t->run(this); - this->clear_completed(); - while (this->running_ >= this->desired_thread_count_) - { - this->completed_condvar_.wait(); - this->clear_completed(); - } - } + gold_debug(DEBUG_TASK, "%3d completed task %s", thread_number, + t->name().c_str()); + Task* next; { - Hold_lock hl(this->tasks_lock_); + Hold_lock hl(this->lock_); - bool first_empty; - bool all_blocked_first; - if (this->first_tasks_.empty()) - { - t = NULL; - empty = true; - first_empty = true; - all_blocked_first = false; - } - else - { - t = this->find_runnable(&this->first_tasks_, &all_blocked_first); - empty = false; - first_empty = false; - } + --this->running_; - if (t == NULL) + // Release the locks for the task. This must be done with the + // workqueue lock held. Get the next Task to run if any. + next = this->release_locks(t, &tl); + + if (next == NULL) + next = this->find_runnable(); + + // If we have another Task to run, get the Locks. This must + // be called while we are still holding the Workqueue lock. + if (next != 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; - } + tl.clear(); + next->locks(&tl); + + ++this->running_; } } - // If T != NULL, it is a task we can run. - // If T == NULL && empty, then there are no tasks waiting to - // be run. - // If T == NULL && !empty, then there tasks waiting to be - // run, but they are waiting for something to unlock. + // We are done with this task. + delete t; - if (t != NULL) - this->run(t); - else if (!empty) - { - { - Hold_lock hl(this->completed_lock_); - - // There must be something for us to wait for, or we won't - // be able to make progress. - gold_assert(this->running_ > 0 || !this->completed_.empty()); - - if (all_blocked) - { - this->cleared_blockers_ = 0; - int queued = this->queued_; - this->clear_completed(); - while (this->cleared_blockers_ == 0 - && queued == this->queued_) - { - if (this->running_ <= 0) - { - this->show_queued_tasks(); - gold_unreachable(); - } - this->completed_condvar_.wait(); - this->clear_completed(); - } - } - else - { - if (this->running_ > 0) - { - // Wait for a task to finish. - this->completed_condvar_.wait(); - } - this->clear_completed(); - } - } - } - else - { - { - Hold_lock hl(this->completed_lock_); - - // If there are no running tasks, then we are done. - if (this->running_ == 0) - { - this->clear_completed(); - return; - } - - // Wait for a task to finish. Then we have to loop around - // again in case it added any new tasks before finishing. - this->completed_condvar_.wait(); - this->clear_completed(); - } - } + t = next; } + + return true; } -// Run a task. This is always called in the main thread. +// Handle the return value of release_locks, and get tasks ready to +// run. -void -Workqueue::run(Task* t) -{ - gold_debug(DEBUG_TASK, "starting task %s", t->name().c_str()); +// 1) If T is not runnable, queue it on the appropriate token. - { - Hold_lock hl(this->completed_lock_); - ++this->running_; - } - this->runner_->run(t, t->locks(this)); -} +// 2) Otherwise, T is runnable. If *PRET is not NULL, then we have +// already decided which Task to run next. Add T to the list of +// runnable tasks, and signal another thread. -// This is called when a task is completed to put the locks on the -// list to be released. We use a list because we only want the locks -// to be released in the main thread. +// 3) Otherwise, *PRET is NULL. If IS_BLOCKER is false, then T was +// waiting on a write lock. We can grab that lock now, so we run T +// now. -void -Workqueue::completed(Task* t, Task_locker* tl) +// 4) Otherwise, IS_BLOCKER is true. If we should run T soon, then +// run it now. + +// 5) Otherwise, check whether there are other tasks to run. If there +// are, then we generally get a better ordering if we run those tasks +// now, before T. A typical example is tasks waiting on the Dirsearch +// blocker. We don't want to run those tasks right away just because +// the Dirsearch was unblocked. + +// 6) Otherwise, there are no other tasks to run, so we might as well +// run this one now. + +// This function must be called with the Workqueue lock held. + +// Return true if we set *PRET to T, false otherwise. + +bool +Workqueue::return_or_queue(Task* t, bool is_blocker, Task** pret) { - gold_debug(DEBUG_TASK, "completed task %s", t->name().c_str()); + Task_token* token = t->is_runnable(); - { - Hold_lock hl(this->completed_lock_); - gold_assert(this->running_ > 0); - --this->running_; - this->completed_.push_back(tl); - this->completed_condvar_.signal(); - } + if (token != NULL) + { + token->add_waiting(t); + ++this->waiting_; + return false; + } + + bool should_queue = false; + bool should_return = false; + + if (*pret != NULL) + should_queue = true; + else if (!is_blocker) + should_return = true; + else if (t->should_run_soon()) + should_return = true; + else if (!this->first_tasks_.empty() || !this->tasks_.empty()) + should_queue = true; + else + should_return = true; - delete t; + if (should_return) + { + gold_assert(*pret == NULL); + *pret = t; + return true; + } + else if (should_queue) + { + if (t->should_run_soon()) + this->first_tasks_.push_back(t); + else + this->tasks_.push_back(t); + this->condvar_.signal(); + return false; + } + + gold_unreachable(); } -// This is called when the last task for a blocker has completed. -// This is always called in the main thread. +// Release the locks associated with a Task. Return the first +// runnable Task that we find. If we find more runnable tasks, add +// them to the run queue and signal any other threads. This must be +// called with the Workqueue lock held. -void -Workqueue::cleared_blocker() +Task* +Workqueue::release_locks(Task* t, Task_locker* tl) { - ++this->cleared_blockers_; + Task* ret = NULL; + for (Task_locker::iterator p = tl->begin(); p != tl->end(); ++p) + { + Task_token* token = *p; + if (token->is_blocker()) + { + if (token->remove_blocker()) + { + // The token has been unblocked. Every waiting Task may + // now be runnable. + Task* t; + while ((t = token->remove_first_waiting()) != NULL) + { + --this->waiting_; + this->return_or_queue(t, true, &ret); + } + } + } + else + { + token->remove_writer(t); + + // One more waiting Task may now be runnable. If we are + // going to run it next, we can stop. Otherwise we need to + // move all the Tasks to the runnable queue, to avoid a + // potential deadlock if the locking status changes before + // we run the next thread. + Task* t; + while ((t = token->remove_first_waiting()) != NULL) + { + --this->waiting_; + if (this->return_or_queue(t, false, &ret)) + break; + } + } + } + return ret; } -// Set the number of threads to use for the workqueue, if we are using -// threads. +// Process all the tasks on the workqueue. Keep going until the +// workqueue is empty, or until we have been told to exit. This +// function is called by all threads. void -Workqueue::set_thread_count(int threads) +Workqueue::process(int thread_number) { - gold_assert(threads > 0); - this->desired_thread_count_ = threads; - this->runner_->set_thread_count(threads); + while (this->find_and_run_task(thread_number)) + ; } -// Dump the list of queued tasks and their current state, for -// debugging purposes. +// Set the number of threads to use for the workqueue, if we are using +// threads. void -Workqueue::show_queued_tasks() +Workqueue::set_thread_count(int threads) { - 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); - } + Hold_lock hl(this->lock_); + + this->threader_->set_thread_count(threads); + // Wake up all the threads, since something has changed. + this->condvar_.broadcast(); } } // End namespace gold. diff --git a/gold/workqueue.h b/gold/workqueue.h index e435739..5f2137e 100644 --- a/gold/workqueue.h +++ b/gold/workqueue.h @@ -24,250 +24,20 @@ // driven from a work queue. This permits us to parallelize the // linker where possible. -// Task_token -// A simple locking implementation to ensure proper task ordering. -// Task_read_token, Task_write_token -// Lock a Task_token for read or write. -// Task_locker -// Task locking using RAII. -// Task -// An abstract class for jobs to run. - #ifndef GOLD_WORKQUEUE_H #define GOLD_WORKQUEUE_H +#include <string> + #include "gold-threads.h" -#include "fileread.h" +#include "token.h" namespace gold { class General_options; -class Task; class Workqueue; -// Some tasks require access to shared data structures, such as the -// symbol table. Some tasks must be executed in a particular order, -// such as reading input file symbol tables--if we see foo.o -llib, we -// have to read the symbols for foo.o before we read the ones for -// -llib. To implement this safely and efficiently, we use tokens. -// Task_tokens support shared read/exclusive write access to some -// resource. Alternatively, they support blockers: blockers implement -// the requirement that some set of tasks must complete before another -// set of tasks can start. In such a case we increment the block -// count when we create the task, and decrement it when the task -// completes. Task_tokens are only manipulated by the main thread, so -// they do not themselves require any locking. - -class Task_token -{ - public: - Task_token(); - - ~Task_token(); - - // A read/write token uses these methods. - - bool - is_readable() const; - - void - add_reader(); - - void - remove_reader(); - - bool - is_writable() const; - - void - add_writer(const Task*); - - void - remove_writer(const Task*); - - bool - has_write_lock(const Task*); - - // A blocker token uses these methods. - - void - add_blocker(); - - // Returns true if block count drops to zero. - bool - remove_blocker(); - - bool - is_blocked() const; - - private: - // It makes no sense to copy these. - Task_token(const Task_token&); - Task_token& operator=(const Task_token&); - - bool is_blocker_; - int readers_; - const Task* writer_; -}; - -// In order to support tokens more reliably, we provide objects which -// handle them using RAII. - -class Task_read_token -{ - public: - Task_read_token(Task_token& token) - : token_(token) - { this->token_.add_reader(); } - - ~Task_read_token() - { this->token_.remove_reader(); } - - private: - Task_read_token(const Task_read_token&); - Task_read_token& operator=(const Task_read_token&); - - Task_token& token_; -}; - -class Task_write_token -{ - public: - Task_write_token(Task_token& token, const Task* task) - : token_(token), task_(task) - { this->token_.add_writer(this->task_); } - - ~Task_write_token() - { this->token_.remove_writer(this->task_); } - - private: - Task_write_token(const Task_write_token&); - Task_write_token& operator=(const Task_write_token&); - - Task_token& token_; - const Task* task_; -}; - -class Task_block_token -{ - public: - // The blocker count must be incremented when the task is created. - // This object is created when the task is run. When we unblock the - // last task, we notify the workqueue. - Task_block_token(Task_token& token, Workqueue* workqueue); - ~Task_block_token(); - - private: - Task_block_token(const Task_block_token&); - Task_block_token& operator=(const Task_block_token&); - - Task_token& token_; - Workqueue* workqueue_; -}; - -// An object which implements an RAII lock for any object which -// supports lock and unlock methods. - -template<typename Obj> -class Task_lock_obj -{ - public: - Task_lock_obj(Obj& obj) - : obj_(obj) - { this->obj_.lock(); } - - ~Task_lock_obj() - { this->obj_.unlock(); } - - private: - Task_lock_obj(const Task_lock_obj&); - Task_lock_obj& operator=(const Task_lock_obj&); - - Obj& obj_; -}; - -// An abstract class used to lock Task_tokens using RAII. A typical -// implementation would simply have a set of members of type -// Task_read_token, Task_write_token, and Task_block_token. - -class Task_locker -{ - public: - Task_locker() - { } - - virtual ~Task_locker() - { } -}; - -// A version of Task_locker which may be used for a single read lock. - -class Task_locker_read : public Task_locker -{ - public: - Task_locker_read(Task_token& token) - : read_token_(token) - { } - - private: - Task_locker_read(const Task_locker_read&); - Task_locker_read& operator=(const Task_locker_read&); - - Task_read_token read_token_; -}; - -// A version of Task_locker which may be used for a single write lock. - -class Task_locker_write : public Task_locker -{ - public: - Task_locker_write(Task_token& token, const Task* task) - : write_token_(token, task) - { } - - private: - Task_locker_write(const Task_locker_write&); - Task_locker_write& operator=(const Task_locker_write&); - - Task_write_token write_token_; -}; - -// A version of Task_locker which may be used for a single blocker -// lock. - -class Task_locker_block : public Task_locker -{ - public: - Task_locker_block(Task_token& token, Workqueue* workqueue) - : block_token_(token, workqueue) - { } - - private: - Task_locker_block(const Task_locker_block&); - Task_locker_block& operator=(const Task_locker_block&); - - Task_block_token block_token_; -}; - -// A version of Task_locker which may be used to hold a lock on any -// object which supports lock() and unlock() methods. - -template<typename Obj> -class Task_locker_obj : public Task_locker -{ - public: - Task_locker_obj(Obj& obj) - : obj_lock_(obj) - { } - - private: - Task_locker_obj(const Task_locker_obj&); - Task_locker_obj& operator=(const Task_locker_obj&); - - Task_lock_obj<Obj> obj_lock_; -}; - // The superclass for tasks to be placed on the workqueue. Each // specific task class will inherit from this one. @@ -275,39 +45,57 @@ class Task { public: Task() - : name_() + : list_next_(NULL), name_(), should_run_soon_(false) { } virtual ~Task() { } - // Type returned by Is_runnable. - enum Is_runnable_type - { - // Task is runnable. - IS_RUNNABLE, - // Task is waiting for a block to clear. - IS_BLOCKED, - // Task is not waiting for a block, but is not runnable--i.e., is - // waiting for a lock. - IS_LOCKED - }; - - // Return whether the task can be run now. This method is only - // called from the main thread. - virtual Is_runnable_type - is_runnable(Workqueue*) = 0; - - // Return a pointer to a Task_locker which locks all the resources - // required by the task. We delete the pointer when the task is - // complete. This method can return NULL if no locks are required. - // This method is only called from the main thread. - virtual Task_locker* - locks(Workqueue*) = 0; + // Check whether the Task can be run now. This method is only + // called with the workqueue lock held. If the Task can run, this + // returns NULL. Otherwise it returns a pointer to a token which + // must be released before the Task can run. + virtual Task_token* + is_runnable() = 0; + + // Lock all the resources required by the Task, and store the locks + // in a Task_locker. This method does not need to do anything if no + // locks are required. This method is only called with the + // workqueue lock held. + virtual void + locks(Task_locker*) = 0; // Run the task. virtual void run(Workqueue*) = 0; + // Return whether this task should run soon. + bool + should_run_soon() const + { return this->should_run_soon_; } + + // Note that this task should run soon. + void + set_should_run_soon() + { this->should_run_soon_ = true; } + + // Get the next Task on the list of Tasks. Called by Task_list. + Task* + list_next() const + { return this->list_next_; } + + // Set the next Task on the list of Tasks. Called by Task_list. + void + set_list_next(Task* t) + { + gold_assert(this->list_next_ == NULL); + this->list_next_ = t; + } + + // Clear the next Task on the list of Tasks. Called by Task_list. + void + clear_list_next() + { this->list_next_ = NULL; } + // Return the name of the Task. This is only used for debugging // purposes. const std::string& @@ -325,15 +113,24 @@ class Task get_name() const = 0; private: - // This task may not be copied. + // Tasks may not be copied. Task(const Task&); Task& operator=(const Task&); + // If this Task is on a list, this is a pointer to the next Task on + // the list. We use this simple list structure rather than building + // a container, in order to avoid memory allocation while holding + // the Workqueue lock. + Task* list_next_; // Task name, for debugging purposes. std::string name_; + // Whether this Task should be executed soon. This is used for + // Tasks which can be run after some data is read. + bool should_run_soon_; }; -// A simple task which waits for a blocker and then runs a function. +// An interface for Task_function. This is a convenience class to run +// a single function. class Task_function_runner { @@ -342,14 +139,16 @@ class Task_function_runner { } virtual void - run(Workqueue*) = 0; + run(Workqueue*, const Task*) = 0; }; +// A simple task which waits for a blocker and then runs a function. + class Task_function : public Task { public: - // Both points should be allocated using new, and will be deleted - // after the task runs. + // RUNNER and BLOCKER should be allocated using new, and will be + // deleted after the task runs. Task_function(Task_function_runner* runner, Task_token* blocker, const char* name) : runner_(runner), blocker_(blocker), name_(name) @@ -364,19 +163,19 @@ class Task_function : public Task // The standard task methods. // Wait until the task is unblocked. - Is_runnable_type - is_runnable(Workqueue*) - { return this->blocker_->is_blocked() ? IS_BLOCKED : IS_RUNNABLE; } + Task_token* + is_runnable() + { return this->blocker_->is_blocked() ? this->blocker_ : NULL; } // This type of task does not normally hold any locks. - virtual Task_locker* - locks(Workqueue*) - { return NULL; } + virtual void + locks(Task_locker*) + { } // Run the action. void run(Workqueue* workqueue) - { this->runner_->run(workqueue); } + { this->runner_->run(workqueue, this); } // The debugging name. std::string @@ -392,9 +191,9 @@ class Task_function : public Task const char* name_; }; -// The workqueue +// The workqueue itself. -class Workqueue_runner; +class Workqueue_threader; class Workqueue { @@ -411,15 +210,14 @@ class Workqueue void queue_front(Task*); - // Process all the tasks on the work queue. + // Process all the tasks on the work queue. This function runs + // until all tasks have completed. The argument is the thread + // number, used only for debugging. void - process(); + process(int); - // A complete set of blocking tasks has completed. - void - cleared_blocker(); - - // Set the thread count. + // Set the desired thread count--the number of threads we want to + // have running. void set_thread_count(int); @@ -428,59 +226,56 @@ class Workqueue Workqueue(const Workqueue&); Workqueue& operator=(const Workqueue&); - typedef std::list<Task*> Task_list; - - // Run a task. + // Add a task to a queue. void - run(Task*); + add_to_queue(Task_list* queue, Task* t); - friend class Workqueue_runner; + // Find a runnable task, or wait for one. + Task* + find_runnable_or_wait(int thread_number); // Find a runnable task. Task* - find_runnable(Task_list*, bool*); + find_runnable(); - // Add a lock to the completed queue. - void - completed(Task*, Task_locker*); + // Find a runnable task in a list. + Task* + find_runnable_in_list(Task_list*); - // Clear the completed queue. + // Find an run a task. bool - clear_completed(); + find_and_run_task(int); - // Print the list of queued tasks. - void - show_queued_tasks(); + // Release the locks for a Task. Return the next Task to run. + Task* + release_locks(Task*, Task_locker*); - // How to run a task. Only accessed from main thread. - Workqueue_runner* runner_; + // Store T into *PRET, or queue it as appropriate. + bool + return_or_queue(Task* t, bool is_blocker, Task** pret); + + // Return whether to cancel this thread. + bool + should_cancel_thread(); - // Lock for access to tasks_ members. - Lock tasks_lock_; + // Master Workqueue lock. This controls access to the following + // member variables. + Lock lock_; // 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_, 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_; + // Number of tasks waiting for a lock to release. + int waiting_; + // Condition variable associated with lock_. This is signalled when + // there may be a new Task to execute. + Condvar condvar_; + + // The threading implementation. This is set at construction time + // and not changed thereafter. + Workqueue_threader* threader_; }; } // End namespace gold. |