2009-03-23 Ian Lance Taylor <iant@google.com>
authorIan Lance Taylor <ian@airs.com>
Tue, 24 Mar 2009 04:50:32 +0000 (04:50 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 24 Mar 2009 04:50:32 +0000 (04:50 +0000)
* gold-threads.h (class Initialize_lock): Define.
* gold-threads.cc (class Initialize_lock_once): Define.
(initialize_lock_control): New static variable.
(initialize_lock_pointer): New static variable.
(initialize_lock_once): New static function.
(Initialize_lock::Initialize_lock): Define.
(Initialize_lock::initialize): Define.
* target-select.h: Include "gold-threads.h".
(class Target_selector): Add lock_ and initialize_lock_ fields.
Don't define instantiate_target, just declare it.
* target-select.cc (Target_selector::Target_selector): Initialize
new fields.
(Target_selector::instantiate_target): Define.
* descriptors.h: Include "gold-threads.h".
(class Descriptors): Add initialize_lock_ field.
* descriptors.cc (Descriptors::Descriptors): Initialize new
field.
(Descriptors::open): Use initialize_lock_ field
* errors.h (class Errors): Add initialize_lock_ field.
* errors.cc (Errors::Errors): Initialize new field.
(Errors::initialize_lock): Use initialize_lock_ field.
* powerpc.cc (class Target_selector_powerpc): Remove
instantiated_target_ field.  In do_recognize call
instantiate_target rather than do_instantiate_target.  In
do_instantiate_target just allocate a new target.
* sparc.cc (class Target_selector_sparc): Likewise.

gold/ChangeLog
gold/descriptors.cc
gold/descriptors.h
gold/errors.cc
gold/errors.h
gold/gold-threads.cc
gold/gold-threads.h
gold/powerpc.cc
gold/sparc.cc
gold/target-select.cc
gold/target-select.h

index 89e9d73..d1ba5e4 100644 (file)
@@ -1,5 +1,32 @@
 2009-03-23  Ian Lance Taylor  <iant@google.com>
 
+       * gold-threads.h (class Initialize_lock): Define.
+       * gold-threads.cc (class Initialize_lock_once): Define.
+       (initialize_lock_control): New static variable.
+       (initialize_lock_pointer): New static variable.
+       (initialize_lock_once): New static function.
+       (Initialize_lock::Initialize_lock): Define.
+       (Initialize_lock::initialize): Define.
+       * target-select.h: Include "gold-threads.h".
+       (class Target_selector): Add lock_ and initialize_lock_ fields.
+       Don't define instantiate_target, just declare it.
+       * target-select.cc (Target_selector::Target_selector): Initialize
+       new fields.
+       (Target_selector::instantiate_target): Define.
+       * descriptors.h: Include "gold-threads.h".
+       (class Descriptors): Add initialize_lock_ field.
+       * descriptors.cc (Descriptors::Descriptors): Initialize new
+       field.
+       (Descriptors::open): Use initialize_lock_ field
+       * errors.h (class Errors): Add initialize_lock_ field.
+       * errors.cc (Errors::Errors): Initialize new field.
+       (Errors::initialize_lock): Use initialize_lock_ field.
+       * powerpc.cc (class Target_selector_powerpc): Remove
+       instantiated_target_ field.  In do_recognize call
+       instantiate_target rather than do_instantiate_target.  In
+       do_instantiate_target just allocate a new target.
+       * sparc.cc (class Target_selector_sparc): Likewise.
+
        * freebsd.h: New file.
        * i386.cc: Include "freebsd.h".
        (Target_i386): Derive from Target_freebsd rather than
index b05bdf1..3d059e2 100644 (file)
@@ -51,8 +51,8 @@ namespace gold
 // adjusted downward if we run out of file descriptors.
 
 Descriptors::Descriptors()
-  : lock_(NULL), open_descriptors_(), stack_top_(-1), current_(0),
-    limit_(8192 - 16)
+  : lock_(NULL), initialize_lock_(&this->lock_), open_descriptors_(),
+    stack_top_(-1), current_(0), limit_(8192 - 16)
 {
   this->open_descriptors_.reserve(128);
 }
@@ -66,13 +66,9 @@ Descriptors::open(int descriptor, const char* name, int flags, int mode)
   // initialize a Lock until we have parsed the options to find out
   // whether we are running with threads.  We can be called before
   // options are valid when reading a linker script.
-  if (this->lock_ == NULL)
-    {
-      if (parameters->options_valid())
-       this->lock_ = new Lock();
-      else
-       gold_assert(descriptor < 0);
-    }
+  bool lock_initialized = this->initialize_lock_.initialize();
+
+  gold_assert(lock_initialized || descriptor < 0);
 
   if (descriptor >= 0)
     {
index 44c2475..8e154a6 100644 (file)
 
 #include <vector>
 
+#include "gold-threads.h"
+
 namespace gold
 {
 
-class Lock;
-
 // This class manages file descriptors for gold.
 
 class Descriptors
@@ -78,6 +78,8 @@ class Descriptors
 
   // We need to lock before accessing any fields.
   Lock* lock_;
+  // Used to initialize the lock_ field exactly once.
+  Initialize_lock initialize_lock_;
   // Information for descriptors.
   std::vector<Open_descriptor> open_descriptors_;
   // Top of stack.
index d45d6a5..618f9cd 100644 (file)
@@ -39,8 +39,8 @@ namespace gold
 const int Errors::max_undefined_error_report;
 
 Errors::Errors(const char* program_name)
-  : program_name_(program_name), lock_(NULL), error_count_(0),
-    warning_count_(0), undefined_symbols_()
+  : program_name_(program_name), lock_(NULL), initialize_lock_(&this->lock_),
+    error_count_(0), warning_count_(0), undefined_symbols_()
 {
 }
 
@@ -53,9 +53,7 @@ Errors::Errors(const char* program_name)
 bool
 Errors::initialize_lock()
 {
-  if (this->lock_ == NULL && parameters->options_valid())
-    this->lock_ = new Lock;
-  return this->lock_ != NULL;
+  return this->initialize_lock_.initialize();
 }
 
 // Increment a counter, holding the lock if available.
index 4b71e6c..a8f823d 100644 (file)
@@ -116,6 +116,8 @@ class Errors
   // This class can be accessed from multiple threads.  This lock is
   // used to control access to the data structures.
   Lock* lock_;
+  // Used to initialize the lock_ field exactly once.
+  Initialize_lock initialize_lock_;
   // Numbers of errors reported.
   int error_count_;
   // Number of warnings reported.
index 9aa883d..3471708 100644 (file)
@@ -276,4 +276,128 @@ Condvar::~Condvar()
   delete this->condvar_;
 }
 
+#ifdef ENABLE_THREADS
+
+// Class Initialize_lock_once.  This exists to hold a pthread_once_t
+// structure for Initialize_lock.
+
+class Initialize_lock_once
+{
+ public:
+  Initialize_lock_once()
+    : once_(PTHREAD_ONCE_INIT)
+  { }
+
+  // Return a pointer to the pthread_once_t variable.
+  pthread_once_t*
+  once_control()
+  { return &this->once_; }
+
+ private:
+  pthread_once_t once_;
+};
+
+#endif // !defined(ENABLE_THREADS)
+
+#ifdef ENABLE_THREADS
+
+// A single lock which controls access to initialize_lock_pointer.
+// This is used because we can't pass parameters to functions passed
+// to pthread_once.
+
+static pthread_mutex_t initialize_lock_control = PTHREAD_MUTEX_INITIALIZER;
+
+// A pointer to a pointer to the lock which we need to initialize
+// once.  Access to this is controlled by initialize_lock_pointer.
+
+static Lock** initialize_lock_pointer;
+
+// A routine passed to pthread_once which initializes the lock which
+// initialize_lock_pointer points to.
+
+extern "C"
+{
+
+static void
+initialize_lock_once()
+{
+  *initialize_lock_pointer = new Lock();
+}
+
+}
+
+#endif // !defined(ENABLE_THREADS)
+
+// Class Initialize_lock.
+
+Initialize_lock::Initialize_lock(Lock** pplock)
+  : pplock_(pplock)
+{
+#ifndef ENABLE_THREADS
+  this->once_ = NULL;
+#else
+  this->once_ = new Initialize_lock_once();
+#endif
+}
+
+// Initialize the lock.
+
+bool
+Initialize_lock::initialize()
+{
+  // If the lock has already been initialized, we don't need to do
+  // anything.  Note that this assumes that the pointer value will be
+  // set completely or not at all.  I hope this is always safe.  We
+  // want to do this for efficiency.
+  if (*this->pplock_ != NULL)
+    return true;
+
+  // We can't initialize the lock until we have read the options.
+  if (!parameters->options_valid())
+    return false;
+
+  // If the user did not use --threads, then we can initialize
+  // directly.
+  if (!parameters->options().threads())
+    {
+      *this->pplock_ = new Lock();
+      return true;
+    }
+
+#ifndef ENABLE_THREADS
+
+  // If there is no threads support, we don't need to use
+  // pthread_once.
+  *this->pplock_ = new Lock();
+
+#else // !defined(ENABLE_THREADS)
+
+  // Since we can't pass parameters to routines called by
+  // pthread_once, we use a static variable: initialize_lock_pointer.
+  // That in turns means that we need to use a mutex to control access
+  // to initialize_lock_pointer.
+
+  int err = pthread_mutex_lock(&initialize_lock_control);
+  if (err != 0)
+    gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err));
+
+  initialize_lock_pointer = this->pplock_;
+
+  err = pthread_once(this->once_->once_control(), initialize_lock_once);
+  if (err != 0)
+    gold_fatal(_("pthread_once failed: %s"), strerror(err));
+
+  initialize_lock_pointer = NULL;
+
+  err = pthread_mutex_unlock(&initialize_lock_control);
+  if (err != 0)
+    gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err));
+
+  gold_assert(*this->pplock_ != NULL);
+
+#endif // !defined(ENABLE_THREADS)
+
+  return true;
+}
+
 } // End namespace gold.
index c901e42..9c49b57 100644 (file)
@@ -35,6 +35,7 @@ namespace gold
 {
 
 class Condvar;
+class Initialize_lock_once;
 
 // The interface for the implementation of a Lock.
 
@@ -190,6 +191,33 @@ class Condvar
   Condvar_impl* condvar_;
 };
 
+// A class used to initialize a lock exactly once, after the options
+// have been read.  This is needed because the implementation of locks
+// depends on whether we've seen the --threads option.  Before the
+// options have been read, we know we are single-threaded, so we can
+// get by without using a lock.  This class should be an instance
+// variable of the class which has a lock which needs to be
+// initialized.
+
+class Initialize_lock
+{
+ public:
+  // The class which uses this will have a pointer to a lock.  This
+  // must be constructed with a pointer to that pointer.
+  Initialize_lock(Lock** pplock);
+
+  // Initialize the lock.  Return true if the lock is now initialized,
+  // false if it is not (because the options have not yet been read).
+  bool
+  initialize();
+
+ private:
+  // A pointer to the lock pointer which must be initialized.
+  Lock** const pplock_;
+  // If needed, a pointer to a pthread_once_t structure.
+  Initialize_lock_once* once_;
+};
+
 } // End namespace gold.
 
 #endif // !defined(GOLD_THREADS_H)
index 3e8cd75..cf83a55 100644 (file)
@@ -1982,8 +1982,6 @@ public:
                       (big_endian ? "elf32-powerpc" : "elf32-powerpcle")))
   { }
 
-  Target* instantiated_target_;
-
   Target* do_recognize(int machine, int, int)
   {
     switch (size)
@@ -2002,15 +2000,11 @@ public:
        return NULL;
       }
 
-    return do_instantiate_target();
+    return this->instantiate_target();
   }
 
   Target* do_instantiate_target()
-  {
-    if (this->instantiated_target_ == NULL)
-      this->instantiated_target_ = new Target_powerpc<size, big_endian>();
-    return this->instantiated_target_;
-  }
+  { return new Target_powerpc<size, big_endian>(); }
 };
 
 Target_selector_powerpc<32, true> target_selector_ppc32;
index 41aa7cd..fe1ffa6 100644 (file)
@@ -3240,8 +3240,6 @@ public:
                      (size == 64 ? "elf64-sparc" : "elf32-sparc"))
   { }
 
-  Target* instantiated_target_;
-
   Target* do_recognize(int machine, int, int)
   {
     switch (size)
@@ -3261,15 +3259,11 @@ public:
        return NULL;
       }
 
-    return do_instantiate_target();
+    return this->instantiate_target();
   }
 
   Target* do_instantiate_target()
-  {
-    if (this->instantiated_target_ == NULL)
-      this->instantiated_target_ = new Target_sparc<size, big_endian>();
-    return this->instantiated_target_;
-  }
+  { return new Target_sparc<size, big_endian>(); }
 };
 
 Target_selector_sparc<32, true> target_selector_sparc32;
index b81f5a2..55d63b2 100644 (file)
@@ -46,13 +46,27 @@ namespace gold
 Target_selector::Target_selector(int machine, int size, bool is_big_endian,
                                 const char* bfd_name)
   : machine_(machine), size_(size), is_big_endian_(is_big_endian),
-    bfd_name_(bfd_name), instantiated_target_(NULL)
+    bfd_name_(bfd_name), instantiated_target_(NULL), lock_(NULL),
+    initialize_lock_(&this->lock_)
     
 {
   this->next_ = target_selectors;
   target_selectors = this;
 }
 
+// Instantiate the target and return it.  Use a lock to avoid
+// instantiating two instances of the same target.
+
+Target*
+Target_selector::instantiate_target()
+{
+  this->initialize_lock_.initialize();
+  Hold_optional_lock hl(this->lock_);
+  if (this->instantiated_target_ == NULL)
+    this->instantiated_target_ = this->do_instantiate_target();
+  return this->instantiated_target_;
+}
+
 // Find the target for an ELF file.
 
 Target*
index b0b1d92..d1cd44f 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <vector>
 
+#include "gold-threads.h"
+
 namespace gold
 {
 
@@ -136,12 +138,7 @@ class Target_selector
 
   // Instantiate the target and return it.
   Target*
-  instantiate_target()
-  {
-    if (this->instantiated_target_ == NULL)
-      this->instantiated_target_ = this->do_instantiate_target();
-    return this->instantiated_target_;
-  }
+  instantiate_target();
 
  private:
   // ELF machine code.
@@ -157,6 +154,11 @@ class Target_selector
   // The singleton Target structure--this points to an instance of the
   // real implementation.
   Target* instantiated_target_;
+  // Lock to make sure that we don't instantiate the target more than
+  // once.
+  Lock* lock_;
+  // We only want to initialize the lock_ pointer once.
+  Initialize_lock initialize_lock_;
 };
 
 // Select the target for an ELF file.