* 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.
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
// 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);
}
// 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)
{
#include <vector>
+#include "gold-threads.h"
+
namespace gold
{
-class Lock;
-
// This class manages file descriptors for gold.
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.
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_()
{
}
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.
// 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.
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.
{
class Condvar;
+class Initialize_lock_once;
// The interface for the implementation of a Lock.
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)
(big_endian ? "elf32-powerpc" : "elf32-powerpcle")))
{ }
- Target* instantiated_target_;
-
Target* do_recognize(int machine, int, int)
{
switch (size)
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;
(size == 64 ? "elf64-sparc" : "elf32-sparc"))
{ }
- Target* instantiated_target_;
-
Target* do_recognize(int machine, int, int)
{
switch (size)
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;
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*
#include <vector>
+#include "gold-threads.h"
+
namespace gold
{
// 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.
// 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.