Home | History | Annotate | Download | only in gold
      1 // workqueue-threads.cc -- the threaded workqueue for gold
      2 
      3 // Copyright (C) 2007-2016 Free Software Foundation, Inc.
      4 // Written by Ian Lance Taylor <iant (at) google.com>.
      5 
      6 // This file is part of gold.
      7 
      8 // This program is free software; you can redistribute it and/or modify
      9 // it under the terms of the GNU General Public License as published by
     10 // the Free Software Foundation; either version 3 of the License, or
     11 // (at your option) any later version.
     12 
     13 // This program is distributed in the hope that it will be useful,
     14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 // GNU General Public License for more details.
     17 
     18 // You should have received a copy of the GNU General Public License
     19 // along with this program; if not, write to the Free Software
     20 // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     21 // MA 02110-1301, USA.
     22 
     23 // This file holds the workqueue implementation which may be used when
     24 // using threads.
     25 
     26 #include "gold.h"
     27 
     28 #ifdef ENABLE_THREADS
     29 
     30 #include <cstring>
     31 #include <pthread.h>
     32 
     33 #include "debug.h"
     34 #include "gold-threads.h"
     35 #include "workqueue.h"
     36 #include "workqueue-internal.h"
     37 
     38 namespace gold
     39 {
     40 
     41 // Class Workqueue_thread represents a single thread.  Creating an
     42 // instance of this spawns a new thread.
     43 
     44 class Workqueue_thread
     45 {
     46  public:
     47   Workqueue_thread(Workqueue_threader_threadpool*, int thread_number);
     48 
     49   ~Workqueue_thread();
     50 
     51  private:
     52   // This class can not be copied.
     53   Workqueue_thread(const Workqueue_thread&);
     54   Workqueue_thread& operator=(const Workqueue_thread&);
     55 
     56   // Check for error from a pthread function.
     57   void
     58   check(const char* function, int err) const;
     59 
     60   // A function to pass to pthread_create.  This is called with a
     61   // pointer to an instance of this object.
     62   static void*
     63   thread_body(void*);
     64 
     65   // A pointer to the threadpool that this thread is part of.
     66   Workqueue_threader_threadpool* threadpool_;
     67   // The thread number.
     68   int thread_number_;
     69   // The thread ID.
     70   pthread_t tid_;
     71 };
     72 
     73 // Create the thread in the constructor.
     74 
     75 Workqueue_thread::Workqueue_thread(Workqueue_threader_threadpool* threadpool,
     76 				   int thread_number)
     77   : threadpool_(threadpool), thread_number_(thread_number)
     78 {
     79   pthread_attr_t attr;
     80   int err = pthread_attr_init(&attr);
     81   this->check("pthread_attr_init", err);
     82 
     83   err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     84   this->check("pthread_attr_setdetachstate", err);
     85 
     86   err = pthread_create(&this->tid_, &attr, &Workqueue_thread::thread_body,
     87 		       reinterpret_cast<void*>(this));
     88   this->check("pthread_create", err);
     89 
     90   err = pthread_attr_destroy(&attr);
     91   this->check("pthread_attr_destroy", err);
     92 }
     93 
     94 // The destructor will be called when the thread is exiting.
     95 
     96 Workqueue_thread::~Workqueue_thread()
     97 {
     98 }
     99 
    100 // Check for an error.
    101 
    102 void
    103 Workqueue_thread::check(const char* function, int err) const
    104 {
    105   if (err != 0)
    106     gold_fatal(_("%s failed: %s"), function, strerror(err));
    107 }
    108 
    109 // Passed to pthread_create.
    110 
    111 extern "C"
    112 void*
    113 Workqueue_thread::thread_body(void* arg)
    114 {
    115   Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg);
    116 
    117   pwt->threadpool_->process(pwt->thread_number_);
    118 
    119   // Delete the thread object as we exit.
    120   delete pwt;
    121 
    122   return NULL;
    123 }
    124 
    125 // Class Workqueue_threader_threadpool.
    126 
    127 // Constructor.
    128 
    129 Workqueue_threader_threadpool::Workqueue_threader_threadpool(
    130     Workqueue* workqueue)
    131   : Workqueue_threader(workqueue),
    132     check_thread_count_(0),
    133     lock_(),
    134     desired_thread_count_(1),
    135     threads_(1)
    136 {
    137 }
    138 
    139 // Destructor.
    140 
    141 Workqueue_threader_threadpool::~Workqueue_threader_threadpool()
    142 {
    143   // Tell the threads to exit.
    144   this->get_workqueue()->set_thread_count(0);
    145 }
    146 
    147 // Set the thread count.
    148 
    149 void
    150 Workqueue_threader_threadpool::set_thread_count(int thread_count)
    151 {
    152   int create;
    153   {
    154     Hold_lock hl(this->lock_);
    155 
    156     this->desired_thread_count_ = thread_count;
    157     create = this->desired_thread_count_ - this->threads_;
    158     if (create < 0)
    159       this->check_thread_count_ = 1;
    160   }
    161 
    162   if (create > 0)
    163     {
    164       for (int i = 0; i < create; ++i)
    165 	{
    166 	  // Note that threads delete themselves when they exit, so we
    167 	  // don't keep pointers to them.
    168 	  new Workqueue_thread(this, this->threads_);
    169 	  ++this->threads_;
    170 	}
    171     }
    172 }
    173 
    174 // Return whether the current thread should be cancelled.
    175 
    176 bool
    177 Workqueue_threader_threadpool::should_cancel_thread(int thread_number)
    178 {
    179   // Fast exit without taking a lock.
    180   if (!this->check_thread_count_)
    181     return false;
    182 
    183   {
    184     Hold_lock hl(this->lock_);
    185     if (thread_number > this->desired_thread_count_)
    186       {
    187 	--this->threads_;
    188 	if (this->threads_ <= this->desired_thread_count_)
    189 	  this->check_thread_count_ = 0;
    190 	return true;
    191       }
    192   }
    193 
    194   return false;
    195 }
    196 
    197 } // End namespace gold.
    198 
    199 #endif // defined(ENABLE_THREADS)
    200