X-Git-Url: https://oss.titaniummirror.com/gitweb?p=msp430-binutils.git;a=blobdiff_plain;f=gold%2Fgold-threads.cc;fp=gold%2Fgold-threads.cc;h=b99cf27584746d906f82a465da1e5bb100c0b30b;hp=0000000000000000000000000000000000000000;hb=88750007d7869f178f0ba528f41efd3b74c424cf;hpb=6df9443a374e2b81278c61b8afc0a1eef7db280b diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc new file mode 100644 index 0000000..b99cf27 --- /dev/null +++ b/gold/gold-threads.cc @@ -0,0 +1,403 @@ +// gold-threads.cc -- thread support for gold + +// Copyright 2006, 2007, 2008 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 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; + +// The threaded version of Lock_impl. + +class Lock_impl_threads : public Lock_impl +{ + public: + Lock_impl_threads(); + ~Lock_impl_threads(); + + void acquire(); + + void release(); + +private: + // This class can not be copied. + Lock_impl_threads(const Lock_impl_threads&); + Lock_impl_threads& operator=(const Lock_impl_threads&); + + friend class Condvar_impl_threads; + + pthread_mutex_t mutex_; +}; + +Lock_impl_threads::Lock_impl_threads() +{ + pthread_mutexattr_t attr; + int err = pthread_mutexattr_init(&attr); + if (err != 0) + gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(err)); +#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP + err = pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + if (err != 0) + gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(err)); +#endif + + err = pthread_mutex_init (&this->mutex_, &attr); + if (err != 0) + gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err)); + + err = pthread_mutexattr_destroy(&attr); + if (err != 0) + gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(err)); +} + +Lock_impl_threads::~Lock_impl_threads() +{ + int err = pthread_mutex_destroy(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(err)); +} + +void +Lock_impl_threads::acquire() +{ + int err = pthread_mutex_lock(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err)); +} + +void +Lock_impl_threads::release() +{ + int err = pthread_mutex_unlock(&this->mutex_); + if (err != 0) + gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err)); +} + +#endif // defined(ENABLE_THREADS) + +// Class Lock. + +Lock::Lock() +{ + if (!parameters->options().threads()) + this->lock_ = new Lock_impl_nothreads; + else + { +#ifdef ENABLE_THREADS + this->lock_ = new Lock_impl_threads; +#else + gold_unreachable(); +#endif + } +} + +Lock::~Lock() +{ + delete this->lock_; +} + +// The non-threaded version of Condvar_impl. + +class Condvar_impl_nothreads : public Condvar_impl +{ + public: + Condvar_impl_nothreads() + { } + + ~Condvar_impl_nothreads() + { } + + void + wait(Lock_impl* li) + { gold_assert(static_cast(li)->acquired_); } + + void + signal() + { } + + void + broadcast() + { } +}; + +#ifdef ENABLE_THREADS + +// The threaded version of Condvar_impl. + +class Condvar_impl_threads : public Condvar_impl +{ + public: + Condvar_impl_threads(); + ~Condvar_impl_threads(); + + void + wait(Lock_impl*); + + void + signal(); + + void + broadcast(); + + private: + // This class can not be copied. + Condvar_impl_threads(const Condvar_impl_threads&); + Condvar_impl_threads& operator=(const Condvar_impl_threads&); + + pthread_cond_t cond_; +}; + +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_threads::~Condvar_impl_threads() +{ + int err = pthread_cond_destroy(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(err)); +} + +void +Condvar_impl_threads::wait(Lock_impl* li) +{ + 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_threads::signal() +{ + int err = pthread_cond_signal(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err)); +} + +void +Condvar_impl_threads::broadcast() +{ + int err = pthread_cond_broadcast(&this->cond_); + if (err != 0) + gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err)); +} + +#endif // defined(ENABLE_THREADS) + +// Methods for Condvar class. + +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::~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_control. + +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.