1 // workqueue-threads.cc -- the threaded workqueue for gold
2 
3 // Copyright (C) 2007-2020 Free Software Foundation, Inc.
4 // Written by Ian Lance Taylor <iant@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