// gold-threads.cc -- thread support for gold
-// Copyright 2006, 2007 Free Software Foundation, Inc.
+// Copyright (C) 2006-2020 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
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);
+ 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_mutextattr_settype failed: %s"), strerror(err));
+ gold_fatal(_("pthread_mutexattr_settype failed: %s"), strerror(err));
#endif
- err = pthread_mutex_init (&this->mutex_, &attr);
+ err = pthread_mutex_init(&this->mutex_, &attr);
if (err != 0)
gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err));
delete this->condvar_;
}
+#ifdef ENABLE_THREADS
+
+// Class Once_initialize. This exists to hold a pthread_once_t
+// structure for Once.
+
+class Once_initialize
+{
+ public:
+ Once_initialize()
+ : 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 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)
+
+// Class Once.
+
+Once::Once()
+ : was_run_(false)
+#if defined(ENABLE_THREADS) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ , was_run_lock_(0)
+#endif
+{
+#ifndef ENABLE_THREADS
+ this->once_ = NULL;
+#else
+ this->once_ = new Once_initialize();
+#endif
+}
+
+// Run the function once.
+
+void
+Once::run_once(void* arg)
+{
+#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
+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;
+ }
+}
+
+// Initialize the lock exactly once.
+
+void
+Initialize_lock::do_run_once(void*)
+{
+ *this->pplock_ = new Lock();
+}
+
} // End namespace gold.