1// workqueue-threads.cc -- the threaded workqueue for gold
2
3// Copyright (C) 2007-2017 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
38namespace gold
39{
40
41// Class Workqueue_thread represents a single thread.  Creating an
42// instance of this spawns a new thread.
43
44class 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
75Workqueue_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
96Workqueue_thread::~Workqueue_thread()
97{
98}
99
100// Check for an error.
101
102void
103Workqueue_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
111extern "C"
112void*
113Workqueue_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
129Workqueue_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
141Workqueue_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
149void
150Workqueue_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
176bool
177Workqueue_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