// workqueue-threads.cc -- the threaded workqueue for gold
// Copyright (C) 2007-2022 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.
// This file holds the workqueue implementation which may be used when
// using threads.
#include "gold.h"
#ifdef ENABLE_THREADS
#include <cstring>
#include <pthread.h>
#include "debug.h"
#include "gold-threads.h"
#include "workqueue.h"
#include "workqueue-internal.h"
namespace gold
{
// Class Workqueue_thread represents a single thread. Creating an
// instance of this spawns a new thread.
class Workqueue_thread
{
public:
Workqueue_thread(Workqueue_threader_threadpool*, int thread_number);
~Workqueue_thread();
private:
// This class can not be copied.
Workqueue_thread(const Workqueue_thread&);
Workqueue_thread& operator=(const Workqueue_thread&);
// Check for error from a pthread function.
void
check(const char* function, int err) const;
// A function to pass to pthread_create. This is called with a
// pointer to an instance of this object.
static void*
thread_body(void*);
// A pointer to the threadpool that this thread is part of.
Workqueue_threader_threadpool* threadpool_;
// The thread number.
int thread_number_;
// The thread ID.
pthread_t tid_;
};
// Create the thread in the constructor.
Workqueue_thread::Workqueue_thread(Workqueue_threader_threadpool* threadpool,
int thread_number)
: threadpool_(threadpool), thread_number_(thread_number)
{
pthread_attr_t attr;
int err = pthread_attr_init(&attr);
this->check("pthread_attr_init", err);
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
this->check("pthread_attr_setdetachstate", err);
err = pthread_create(&this->tid_, &attr, &Workqueue_thread::thread_body,
reinterpret_cast<void*>(this));
this->check("pthread_create", err);
err = pthread_attr_destroy(&attr);
this->check("pthread_attr_destroy", err);
}
// The destructor will be called when the thread is exiting.
Workqueue_thread::~Workqueue_thread()
{
}
// Check for an error.
void
Workqueue_thread::check(const char* function, int err) const
{
if (err != 0)
gold_fatal(_("%s failed: %s"), function, strerror(err));
}
// Passed to pthread_create.
extern "C"
void*
Workqueue_thread::thread_body(void* arg)
{
Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg);
pwt->threadpool_->process(pwt->thread_number_);
// Delete the thread object as we exit.
delete pwt;
return NULL;
}
// Class Workqueue_threader_threadpool.
// Constructor.
Workqueue_threader_threadpool::Workqueue_threader_threadpool(
Workqueue* workqueue)
: Workqueue_threader(workqueue),
check_thread_count_(0),
lock_(),
desired_thread_count_(1),
threads_(1)
{
}
// Destructor.
Workqueue_threader_threadpool::~Workqueue_threader_threadpool()
{
// Tell the threads to exit.
this->get_workqueue()->set_thread_count(0);
}
// Set the thread count.
void
Workqueue_threader_threadpool::set_thread_count(int thread_count)
{
int create;
{
Hold_lock hl(this->lock_);
this->desired_thread_count_ = thread_count;
create = this->desired_thread_count_ - this->threads_;
if (create < 0)
this->check_thread_count_ = 1;
}
if (create > 0)
{
for (int i = 0; i < create; ++i)
{
// Note that threads delete themselves when they exit, so we
// don't keep pointers to them.
new Workqueue_thread(this, this->threads_);
++this->threads_;
}
}
}
// Return whether the current thread should be cancelled.
bool
Workqueue_threader_threadpool::should_cancel_thread(int thread_number)
{
// Fast exit without taking a lock.
if (!this->check_thread_count_)
return false;
{
Hold_lock hl(this->lock_);
if (thread_number > this->desired_thread_count_)
{
--this->threads_;
if (this->threads_ <= this->desired_thread_count_)
this->check_thread_count_ = 0;
return true;
}
}
return false;
}
} // End namespace gold.
#endif // defined(ENABLE_THREADS)