X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gold%2Fgold-threads.cc;h=36f3625e5152b2faa2c9aec80d3d71c1194ff3e9;hb=e46d79a76ea748165a3ecd1102dd10498d089a49;hp=c6e1170488fdf277e0acf8bd998bc8ecb56705ab;hpb=bae7f79e03d6405f5ceec0e3e24671e6b30f29ed;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc index c6e1170488..36f3625e51 100644 --- a/gold/gold-threads.cc +++ b/gold/gold-threads.cc @@ -1,25 +1,85 @@ // gold-threads.cc -- thread support for gold -#include +// Copyright (C) 2006-2019 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// 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. #include "gold.h" +#include + #ifdef ENABLE_THREADS #include #endif +#include "options.h" +#include "parameters.h" #include "gold-threads.h" namespace gold { -// Class Lock_impl. +class Condvar_impl_nothreads; + +// The non-threaded version of Lock_impl. + +class Lock_impl_nothreads : public Lock_impl +{ + public: + Lock_impl_nothreads() + : acquired_(false) + { } + + ~Lock_impl_nothreads() + { gold_assert(!this->acquired_); } + + void + acquire() + { + gold_assert(!this->acquired_); + this->acquired_ = true; + } + + void + release() + { + gold_assert(this->acquired_); + this->acquired_ = false; + } + + private: + friend class Condvar_impl_nothreads; + + bool acquired_; +}; + +#ifdef ENABLE_THREADS + +class Condvar_impl_threads; -class Lock_impl +// The threaded version of Lock_impl. + +class Lock_impl_threads : public Lock_impl { public: - Lock_impl(); - ~Lock_impl(); + Lock_impl_threads(); + ~Lock_impl_threads(); void acquire(); @@ -27,204 +87,364 @@ class Lock_impl private: // This class can not be copied. - Lock_impl(const Lock_impl&); - Lock_impl& operator=(const Lock_impl&); + Lock_impl_threads(const Lock_impl_threads&); + Lock_impl_threads& operator=(const Lock_impl_threads&); - friend class Condvar_impl; + friend class Condvar_impl_threads; -#ifdef ENABLE_THREADS pthread_mutex_t mutex_; -#else - bool acquired_; -#endif }; -#ifdef ENABLE_THREADS - -Lock_impl::Lock_impl() +Lock_impl_threads::Lock_impl_threads() { pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr) != 0) - gold_fatal(_("pthead_mutextattr_init failed"), true); -#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP - if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0) - gold_fatal(_("pthread_mutextattr_settype failed"), true); + int err = pthread_mutexattr_init(&attr); + if (err != 0) + gold_fatal(_("pthead_mutexattr_init failed: %s"), strerror(err)); +#ifdef PTHREAD_MUTEX_ADAPTIVE_NP + err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + if (err != 0) + gold_fatal(_("pthread_mutexattr_settype failed: %s"), strerror(err)); #endif - if (pthread_mutex_init (&this->mutex_, &attr) != 0) - gold_fatal(_("pthread_mutex_init failed"), true); + err = pthread_mutex_init(&this->mutex_, &attr); + if (err != 0) + gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err)); - if (pthread_mutexattr_destroy(&attr) != 0) - gold_fatal(_("pthread_mutexattr_destroy failed"), true); + err = pthread_mutexattr_destroy(&attr); + if (err != 0) + gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(err)); } -Lock_impl::~Lock_impl() +Lock_impl_threads::~Lock_impl_threads() { - if (pthread_mutex_destroy(&this->mutex_) != 0) - gold_fatal(_("pthread_mutex_destroy failed"), true); + int err = pthread_mutex_destroy(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(err)); } void -Lock_impl::acquire() +Lock_impl_threads::acquire() { - if (pthread_mutex_lock(&this->mutex_) != 0) - gold_fatal(_("pthread_mutex_lock failed"), true); + int err = pthread_mutex_lock(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err)); } void -Lock_impl::release() +Lock_impl_threads::release() { - if (pthread_mutex_unlock(&this->mutex_) != 0) - gold_fatal(_("pthread_mutex_unlock failed"), true); + int err = pthread_mutex_unlock(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err)); } -#else // !defined(ENABLE_THREADS) +#endif // defined(ENABLE_THREADS) -Lock_impl::Lock_impl() - : acquired_(false) -{ -} +// Class Lock. -Lock_impl::~Lock_impl() +Lock::Lock() { - assert(!this->acquired_); + if (!parameters->options().threads()) + this->lock_ = new Lock_impl_nothreads; + else + { +#ifdef ENABLE_THREADS + this->lock_ = new Lock_impl_threads; +#else + gold_unreachable(); +#endif + } } -void -Lock_impl::acquire() +Lock::~Lock() { - assert(!this->acquired_); - this->acquired_ = true; + delete this->lock_; } -void -Lock_impl::release() +// The non-threaded version of Condvar_impl. + +class Condvar_impl_nothreads : public Condvar_impl { - assert(this->acquired_); - this->acquired_ = false; -} + public: + Condvar_impl_nothreads() + { } -#endif // !defined(ENABLE_THREADS) + ~Condvar_impl_nothreads() + { } -// Methods for Lock class. + void + wait(Lock_impl* li) + { gold_assert(static_cast(li)->acquired_); } -Lock::Lock() -{ - this->lock_ = new Lock_impl; -} - -Lock::~Lock() -{ - delete this->lock_; -} + void + signal() + { } -void -Lock::acquire() -{ - this->lock_->acquire(); -} + void + broadcast() + { } +}; -void -Lock::release() -{ - this->lock_->release(); -} +#ifdef ENABLE_THREADS -// Class Condvar_impl. +// The threaded version of Condvar_impl. -class Condvar_impl +class Condvar_impl_threads : public Condvar_impl { public: - Condvar_impl(); - ~Condvar_impl(); + Condvar_impl_threads(); + ~Condvar_impl_threads(); - void wait(Lock_impl*); - void signal(); + void + wait(Lock_impl*); + + void + signal(); + + void + broadcast(); private: // This class can not be copied. - Condvar_impl(const Condvar_impl&); - Condvar_impl& operator=(const Condvar_impl&); + Condvar_impl_threads(const Condvar_impl_threads&); + Condvar_impl_threads& operator=(const Condvar_impl_threads&); -#ifdef ENABLE_THREADS pthread_cond_t cond_; -#endif }; -#ifdef ENABLE_THREADS +Condvar_impl_threads::Condvar_impl_threads() +{ + int err = pthread_cond_init(&this->cond_, NULL); + if (err != 0) + gold_fatal(_("pthread_cond_init failed: %s"), strerror(err)); +} -Condvar_impl::Condvar_impl() +Condvar_impl_threads::~Condvar_impl_threads() { - if (pthread_cond_init(&this->cond_, NULL) != 0) - gold_fatal(_("pthread_cond_init failed"), true); + int err = pthread_cond_destroy(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(err)); } -Condvar_impl::~Condvar_impl() +void +Condvar_impl_threads::wait(Lock_impl* li) { - if (pthread_cond_destroy(&this->cond_) != 0) - gold_fatal(_("pthread_cond_destroy failed"), true); + Lock_impl_threads* lit = static_cast(li); + int err = pthread_cond_wait(&this->cond_, &lit->mutex_); + if (err != 0) + gold_fatal(_("pthread_cond_wait failed: %s"), strerror(err)); } void -Condvar_impl::wait(Lock_impl* li) +Condvar_impl_threads::signal() { - if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0) - gold_fatal(_("pthread_cond_wait failed"), true); + int err = pthread_cond_signal(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err)); } void -Condvar_impl::signal() +Condvar_impl_threads::broadcast() { - if (pthread_cond_signal(&this->cond_) != 0) - gold_fatal(_("pthread_cond_signal failed"), true); + int err = pthread_cond_broadcast(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err)); } -#else // !defined(ENABLE_THREADS) +#endif // defined(ENABLE_THREADS) + +// Methods for Condvar class. -Condvar_impl::Condvar_impl() +Condvar::Condvar(Lock& lock) + : lock_(lock) { + if (!parameters->options().threads()) + this->condvar_ = new Condvar_impl_nothreads; + else + { +#ifdef ENABLE_THREADS + this->condvar_ = new Condvar_impl_threads; +#else + gold_unreachable(); +#endif + } } -Condvar_impl::~Condvar_impl() +Condvar::~Condvar() { + delete this->condvar_; } -void -Condvar_impl::wait(Lock_impl* li) +#ifdef ENABLE_THREADS + +// Class Once_initialize. This exists to hold a pthread_once_t +// structure for Once. + +class Once_initialize { - assert(li->acquired_); -} + public: + Once_initialize() + : once_(PTHREAD_ONCE_INIT) + { } -void -Condvar_impl::signal() + // 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 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; + +// A pointer to Once structure we want to run. Access to this is +// controlled by once_pointer_control. + +static Once* once_pointer; + +// The argument to pass to the Once structure. Access to this is +// controlled by once_pointer_control. + +static void* once_arg; + +// A routine passed to pthread_once which runs the Once pointer. + +extern "C" +{ + +static void +c_run_once(void) { + once_pointer->internal_run(once_arg); +} + } -#endif // !defined(ENABLE_THREADS) +#endif // defined(ENABLE_THREADS) -// Methods for Condvar class. +// Class Once. -Condvar::Condvar(Lock& lock) - : lock_(lock) +Once::Once() + : was_run_(false) +#if defined(ENABLE_THREADS) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) + , was_run_lock_(0) +#endif { - this->condvar_ = new Condvar_impl; +#ifndef ENABLE_THREADS + this->once_ = NULL; +#else + this->once_ = new Once_initialize(); +#endif } -Condvar::~Condvar() +// Run the function once. + +void +Once::run_once(void* arg) { - delete this->condvar_; +#ifndef ENABLE_THREADS + + // If there is no threads support, we don't need to use pthread_once. + if (!this->was_run_) + this->internal_run(arg); + +#else // defined(ENABLE_THREADS) + + if (parameters->options_valid() && !parameters->options().threads()) + { + // If we are not using threads, we don't need to lock. + if (!this->was_run_) + this->internal_run(arg); + return; + } + + // 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: once_pointer. This in + // turns means that we need to use a mutex to control access to + // once_pointer. + + int err = pthread_mutex_lock(&once_pointer_control); + if (err != 0) + gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err)); + + once_pointer = this; + once_arg = arg; + + err = pthread_once(this->once_->once_control(), c_run_once); + if (err != 0) + gold_fatal(_("pthread_once failed: %s"), strerror(err)); + + once_pointer = NULL; + once_arg = NULL; + + err = pthread_mutex_unlock(&once_pointer_control); + if (err != 0) + gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err)); + +#endif // defined(ENABLE_THREADS) } +// Actually run the function in the child class. This function will +// be run only once. + void -Condvar::wait() +Once::internal_run(void* arg) { - this->condvar_->wait(this->lock_.get_impl()); + 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; + } +} + +// Initialize the lock exactly once. + void -Condvar::signal() +Initialize_lock::do_run_once(void*) { - this->condvar_->signal(); + *this->pplock_ = new Lock(); } } // End namespace gold.