gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gold / gold-threads.cc
index c6e1170488fdf277e0acf8bd998bc8ecb56705ab..f972a8c97d35a2cb17001658e87eb5ca74ef5407 100644 (file)
@@ -1,25 +1,85 @@
 // gold-threads.cc -- thread support for gold
 
-#include <cassert>
+// Copyright (C) 2006-2020 Free Software Foundation, Inc.
+// Written by Ian Lance Taylor <iant@google.com>.
+
+// 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 <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;
+
+// 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<Lock_impl_nothreads*>(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<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"), 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.
This page took 0.029829 seconds and 4 git commands to generate.