summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@airs.com>2009-03-24 04:50:32 +0000
committerIan Lance Taylor <ian@airs.com>2009-03-24 04:50:32 +0000
commit7f055c204a4371154123a1816fbec9855ee60ad5 (patch)
tree847b6ee09451800cc64d2f3fd933daf82f3a699b
parentc39465150b2129c17f0a520d91dad23cc0632694 (diff)
downloadbinutils-gdb-7f055c204a4371154123a1816fbec9855ee60ad5.tar.gz
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.
-rw-r--r--gold/ChangeLog27
-rw-r--r--gold/descriptors.cc14
-rw-r--r--gold/descriptors.h6
-rw-r--r--gold/errors.cc8
-rw-r--r--gold/errors.h2
-rw-r--r--gold/gold-threads.cc124
-rw-r--r--gold/gold-threads.h28
-rw-r--r--gold/powerpc.cc10
-rw-r--r--gold/sparc.cc10
-rw-r--r--gold/target-select.cc16
-rw-r--r--gold/target-select.h14
11 files changed, 220 insertions, 39 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 89e9d736a51..d1ba5e4529d 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -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
diff --git a/gold/descriptors.cc b/gold/descriptors.cc
index b05bdf1a89d..3d059e2c222 100644
--- a/gold/descriptors.cc
+++ b/gold/descriptors.cc
@@ -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)
{
diff --git a/gold/descriptors.h b/gold/descriptors.h
index 44c2475087e..8e154a631d3 100644
--- a/gold/descriptors.h
+++ b/gold/descriptors.h
@@ -25,11 +25,11 @@
#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.
diff --git a/gold/errors.cc b/gold/errors.cc
index d45d6a58b61..618f9cd643e 100644
--- a/gold/errors.cc
+++ b/gold/errors.cc
@@ -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.
diff --git a/gold/errors.h b/gold/errors.h
index 4b71e6cf86d..a8f823d1ff5 100644
--- a/gold/errors.h
+++ b/gold/errors.h
@@ -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.
diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc
index 9aa883d6cc1..347170814d5 100644
--- a/gold/gold-threads.cc
+++ b/gold/gold-threads.cc
@@ -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.
diff --git a/gold/gold-threads.h b/gold/gold-threads.h
index c901e42e50e..9c49b57d071 100644
--- a/gold/gold-threads.h
+++ b/gold/gold-threads.h
@@ -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)
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 3e8cd75ff21..cf83a55f741 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -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;
diff --git a/gold/sparc.cc b/gold/sparc.cc
index 41aa7cd9d6c..fe1ffa66518 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -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;
diff --git a/gold/target-select.cc b/gold/target-select.cc
index b81f5a28ea8..55d63b2dace 100644
--- a/gold/target-select.cc
+++ b/gold/target-select.cc
@@ -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*
diff --git a/gold/target-select.h b/gold/target-select.h
index b0b1d92e0c1..d1cd44f229d 100644
--- a/gold/target-select.h
+++ b/gold/target-select.h
@@ -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.