]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Rewrite workqueue. This version eliminates the master thread, and
authorIan Lance Taylor <iant@google.com>
Fri, 14 Dec 2007 19:00:21 +0000 (19:00 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 14 Dec 2007 19:00:21 +0000 (19:00 +0000)
reduces the amount of locking required to find a new thread to run.

33 files changed:
gold/Makefile.am
gold/Makefile.in
gold/archive.cc
gold/archive.h
gold/common.cc
gold/common.h
gold/dirsearch.cc
gold/dirsearch.h
gold/fileread.cc
gold/fileread.h
gold/gold.cc
gold/gold.h
gold/layout.cc
gold/layout.h
gold/main.cc
gold/object.cc
gold/object.h
gold/po/POTFILES.in
gold/po/gold.pot
gold/readsyms.cc
gold/readsyms.h
gold/reloc.cc
gold/reloc.h
gold/script.cc
gold/script.h
gold/symtab.cc
gold/symtab.h
gold/testsuite/object_unittest.cc
gold/token.h [new file with mode: 0644]
gold/workqueue-internal.h
gold/workqueue-threads.cc
gold/workqueue.cc
gold/workqueue.h

index 59501c6477b844fe70fb29f61c7e2bfb09e32f97..ce17e96ed71c98c40713bc690d14189f6a7643a3 100644 (file)
@@ -87,6 +87,7 @@ HFILES = \
        target-reloc.h \
        target-select.h \
        tls.h \
+       token.h \
        workqueue.h \
        workqueue-internal.h
 
index 93457019f340d80c67b8146b910ef3ac818bf978..bfeaf86e9596e7ab6e824acd6ee746c8de6c6964 100644 (file)
@@ -344,6 +344,7 @@ HFILES = \
        target-reloc.h \
        target-select.h \
        tls.h \
+       token.h \
        workqueue.h \
        workqueue-internal.h
 
index dc12ea95701bbb4797238af472c54e8e98c5a5ae..b80dea57132da86bd74df0c1b89ff79710e8026c 100644 (file)
@@ -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
index 57af1673563835e3715acab82d2674d9397f069e..b135cd6f377f208ad0946810c0a5c113b757facf 100644 (file)
@@ -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_;
index 652dfa174dba8c6745048cf3a9339374a40cb8cf..d94f2c3c9f6dae100c10b7afa5bd3851067aacf9 100644 (file)
@@ -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.
index bd24985c9bea51fe17afa131e20f5b16f15dcf4f..b85192e5128f87e276a6f2444482fdd4a1ebb22f 100644 (file)
@@ -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_;
index dd1c7e6eb2c884d4cb47b6f49027741787822b9c..960c8eb4e55deb6ff0188d88213a190e21307194 100644 (file)
@@ -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.
index c1af843b1bf2771a7d945c96c849a8880aaa7cca..e590f41796beacbf4326c7e1b46b6233feb1f01b 100644 (file)
 #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.
index e5df08b226af2badc1fde591c217ed434b7954c9..774179d30b9beab734887527eeac8f71c2335cb6 100644 (file)
@@ -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));
index eddb88707bf6d2a805cc48d40949226872d0f856..fd9b1bacdd2e7656df1834034144b3f618edf37e 100644 (file)
@@ -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*
index a3ead18fe3d743ce8f5a8ea77f8e72f623f5e162..b30087173a18e6a47a2c6a1920a3b8b69e98a309 100644 (file)
@@ -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.
index fcb77ec14bc44b1cad27659c67494a2e10197074..32fcc43c9bff483d1ea890a9b82844aac7bdcc9a 100644 (file)
@@ -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*,
index 1cea1a6429466fbb655e9f29331e0b6207f9ae40..71e3ab473b3ab4d133b34fe1d865a4d35f156e44 100644 (file)
@@ -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();
 }
index 37a70db50e2e015ff0f3e139b0d071067614ea2d..3084d606a3e95e785b45034e298e131e8035fe4e 100644 (file)
@@ -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_;
index c7c2d95dbfe71e2afc3acdbc8b0612608c077627..2874a2d4bd6c59374467b529e304361e08e0a4a6 100644 (file)
@@ -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())
     {
index 3bf274adf6b9be96d29784e35c51250910d45891..9d6288110c63d5a050e87395c67dbf5f7cd612f2 100644 (file)
@@ -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;
index 1fcdd1047dc7f884e5b7b4ba96c6ff32b83a7ae7..a43ef0c41fac644a2794d077bb887ceb98bfd94a 100644 (file)
@@ -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
index e8a5ba742f58a3df001fa870eeffb790ab1ff44e..6335c9ed97522749fe6fa4cf9521f57b3d73b0c1 100644 (file)
@@ -53,6 +53,7 @@ target-reloc.h
 target-select.cc
 target-select.h
 tls.h
+token.h
 version.cc
 workqueue.cc
 workqueue.h
index 8d97ea1e2268cbb39377f654298ef66c1efaf5f0..4b786be4a3aab553a78a151959441d9d128b98e7 100644 (file)
@@ -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 ""
index 5625f59f0e6a3991f154ceae299abe0596170cf6..7966c98ca9ea8e44d9fc6e95846efeee1b0419a7 100644 (file)
@@ -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_);
index c02a0ee4672952f140a14bfe08c84614ad0c8944..7a4db419cfb04588ab700820aac79911c6f78a0e 100644 (file)
@@ -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*);
index e34cd041e8253f0966257d4ccf55dece4bb4a764..a91c3548cf62877d2fee4c4ec8e21e223517cbce 100644 (file)
@@ -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;
index d84dc88828b35ff69a1be6633b5dc5f41ede8611..c997030feb8111c9145fc0ced9637a36c90d2d28 100644 (file)
@@ -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_;
index 1ebd100b932714aa599da76fd55681f87c77031e..1373064d379f6e38172d9f2c9aa5c9353fe9e8ed 100644 (file)
@@ -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;
 }
 
index 16483a03c9df4798268a1cdd669377a9c00f4f19..16caf03dcfc0da74450f3e4139197e9a21ba99ac 100644 (file)
@@ -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);
index 770628cac8cc97b4120a7e04384f1f72d41ae140..7868c39e808eaadb8dc95fe82db8d70af1262555 100644 (file)
@@ -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,
index cb3be9b37ee667492cf4b908abfdc6d161bae2b6..244783e78a47a037c5905006e5160f16ffcdfc56 100644 (file)
@@ -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.
index 9b03425eea7b4a84cef0786b8e2efdfa81bf4ddf..4510e0c4fd58cfa14648f1e2824b60c1f2192572 100644 (file)
@@ -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 (file)
index 0000000..49a7d51
--- /dev/null
@@ -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)
index d9fd1608502a2628c94b20e0d7898d84c04f9337..201caa42550ddb88e487e7f7a0911451f5c8ac7a 100644 (file)
@@ -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.
index 3c30b325399cb6f8a22cb03ef07bff6d27e7fde0..ae09e29cf191b69a43cc54d8d01d699c78a66b7e 100644 (file)
@@ -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.
index 018c96bdbd299d17fd7f41e6180a993ce3a0dd19..647daf2ed6ee9a468a2530fe5906aacabd6ccfb7 100644 (file)
 #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.
index e435739c4ad0613974c82ca4c1a1f9717f03845a..5f2137e46acd0d7a961c7e527772a016999a38a2 100644 (file)
 // 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.