diff options
Diffstat (limited to 'gold/gold-threads.cc')
-rw-r--r-- | gold/gold-threads.cc | 160 |
1 files changed, 102 insertions, 58 deletions
diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc index b99cf27..f5347bf 100644 --- a/gold/gold-threads.cc +++ b/gold/gold-threads.cc @@ -1,6 +1,6 @@ // gold-threads.cc -- thread support for gold -// Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -278,13 +278,13 @@ Condvar::~Condvar() #ifdef ENABLE_THREADS -// Class Initialize_lock_once. This exists to hold a pthread_once_t -// structure for Initialize_lock. +// Class Once_initialize. This exists to hold a pthread_once_t +// structure for Once. -class Initialize_lock_once +class Once_initialize { public: - Initialize_lock_once() + Once_initialize() : once_(PTHREAD_ONCE_INIT) { } @@ -297,107 +297,151 @@ class Initialize_lock_once pthread_once_t once_; }; -#endif // !defined(ENABLE_THREADS) +#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. +// A single lock which controls access to once_pointer. This is used +// because we can't pass parameters to functions passed to +// pthread_once. + +static pthread_mutex_t once_pointer_control = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t initialize_lock_control = PTHREAD_MUTEX_INITIALIZER; +// A pointer to Once structure we want to run. Access to this is +// controlled by once_pointer_control. -// A pointer to a pointer to the lock which we need to initialize -// once. Access to this is controlled by initialize_lock_control. +static Once* once_pointer; -static Lock** initialize_lock_pointer; +// The argument to pass to the Once structure. Access to this is +// controlled by once_pointer_control. -// A routine passed to pthread_once which initializes the lock which -// initialize_lock_pointer points to. +static void* once_arg; + +// A routine passed to pthread_once which runs the Once pointer. extern "C" { static void -initialize_lock_once() +c_run_once(void) { - *initialize_lock_pointer = new Lock(); + once_pointer->internal_run(once_arg); } } -#endif // !defined(ENABLE_THREADS) +#endif // defined(ENABLE_THREADS) -// Class Initialize_lock. +// Class Once. -Initialize_lock::Initialize_lock(Lock** pplock) - : pplock_(pplock) +Once::Once() + : was_run_(false), was_run_lock_(0) { #ifndef ENABLE_THREADS this->once_ = NULL; #else - this->once_ = new Initialize_lock_once(); + this->once_ = new Once_initialize(); #endif } -// Initialize the lock. +// Run the function once. -bool -Initialize_lock::initialize() +void +Once::run_once(void* arg) { - // 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; +#ifndef ENABLE_THREADS - // We can't initialize the lock until we have read the options. - if (!parameters->options_valid()) - return false; + // If there is no threads support, we don't need to use pthread_once. + if (!this->was_run_) + this->internal_run(arg); - // If the user did not use --threads, then we can initialize - // directly. - if (!parameters->options().threads()) +#else // defined(ENABLE_THREADS) + + if (parameters->options_valid() && !parameters->options().threads()) { - *this->pplock_ = new Lock(); - return true; + // If we are not using threads, we don't need to lock. + if (!this->was_run_) + this->internal_run(arg); + return; } -#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) + // If we have the sync builtins, use them to skip the lock if the + // value has already been initialized. +#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 + while (true) + { + if (__sync_bool_compare_and_swap(&this->was_run_lock_, 0, 1)) + break; + } + bool was_run = this->was_run_; + while (true) + { + if (__sync_bool_compare_and_swap(&this->was_run_lock_, 1, 0)) + break; + } + if (was_run) + return; +#endif // 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. + // pthread_once, we use a static variable: once_pointer. This in + // turns means that we need to use a mutex to control access to + // once_pointer. - int err = pthread_mutex_lock(&initialize_lock_control); + int err = pthread_mutex_lock(&once_pointer_control); if (err != 0) gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err)); - initialize_lock_pointer = this->pplock_; + once_pointer = this; + once_arg = arg; - err = pthread_once(this->once_->once_control(), initialize_lock_once); + err = pthread_once(this->once_->once_control(), c_run_once); if (err != 0) gold_fatal(_("pthread_once failed: %s"), strerror(err)); - initialize_lock_pointer = NULL; + once_pointer = NULL; + once_arg = NULL; - err = pthread_mutex_unlock(&initialize_lock_control); + err = pthread_mutex_unlock(&once_pointer_control); if (err != 0) - gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err)); + gold_fatal(_("pthread_mutex_unlock falied: %s"), strerror(err)); - gold_assert(*this->pplock_ != NULL); +#endif // defined(ENABLE_THREADS) +} + +// Actually run the function in the child class. This function will +// be run only once. + +void +Once::internal_run(void* arg) +{ + this->do_run_once(arg); + this->was_run_ = true; +} + +// Class Initialize_lock. + +// Initialize the lock. + +bool +Initialize_lock::initialize() +{ + // We can't initialize the lock until we have read the options. + if (!parameters->options_valid()) + return false; + else + { + this->run_once(NULL); + return true; + } +} -#endif // !defined(ENABLE_THREADS) +// Initialize the lock exactly once. - return true; +void +Initialize_lock::do_run_once(void*) +{ + *this->pplock_ = new Lock(); } } // End namespace gold. |