// gold-threads.cc -- thread support for gold
-// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Copyright (C) 2006-2019 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
#include "gold.h"
-#include <cerrno>
#include <cstring>
#ifdef ENABLE_THREADS
#include <pthread.h>
#endif
+#include "options.h"
+#include "parameters.h"
#include "gold-threads.h"
namespace gold
{
-// Class Lock_impl.
+class Condvar_impl_nothreads;
-class Lock_impl
+// The non-threaded version of Lock_impl.
+
+class Lock_impl_nothreads : public Lock_impl
{
public:
- Lock_impl();
- ~Lock_impl();
+ 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();
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: %s"), strerror(errno));
-#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP
- if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0)
- gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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()
{
- gold_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()
{
- gold_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
{
- gold_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<Lock_impl_nothreads*>(li)->acquired_); }
-Lock::Lock()
-{
- this->lock_ = new Lock_impl;
-}
+ void
+ signal()
+ { }
-Lock::~Lock()
-{
- delete this->lock_;
-}
-
-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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ Lock_impl_threads* lit = static_cast<Lock_impl_threads*>(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: %s"), strerror(errno));
+ 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: %s"), strerror(errno));
+ 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
{
- gold_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)
+}
-// Methods for Condvar class.
+#endif // defined(ENABLE_THREADS)
-Condvar::Condvar(Lock& lock)
- : lock_(lock)
+// Class Once.
+
+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.