Add threading support.
This commit is contained in:
parent
06d063c072
commit
c79126688f
|
@ -53,7 +53,8 @@ CCFILES = \
|
|||
stringpool.cc \
|
||||
target-select.cc \
|
||||
version.cc \
|
||||
workqueue.cc
|
||||
workqueue.cc \
|
||||
workqueue-threads.cc
|
||||
|
||||
HFILES = \
|
||||
archive.h \
|
||||
|
@ -84,7 +85,8 @@ HFILES = \
|
|||
target-reloc.h \
|
||||
target-select.h \
|
||||
tls.h \
|
||||
workqueue.h
|
||||
workqueue.h \
|
||||
workqueue-internal.h
|
||||
|
||||
YFILES = \
|
||||
yyscript.y
|
||||
|
|
|
@ -78,7 +78,8 @@ am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
|
|||
output.$(OBJEXT) parameters.$(OBJEXT) readsyms.$(OBJEXT) \
|
||||
reloc.$(OBJEXT) resolve.$(OBJEXT) script.$(OBJEXT) \
|
||||
symtab.$(OBJEXT) stringpool.$(OBJEXT) target-select.$(OBJEXT) \
|
||||
version.$(OBJEXT) workqueue.$(OBJEXT)
|
||||
version.$(OBJEXT) workqueue.$(OBJEXT) \
|
||||
workqueue-threads.$(OBJEXT)
|
||||
am__objects_2 =
|
||||
am__objects_3 = yyscript.$(OBJEXT)
|
||||
am_libgold_a_OBJECTS = $(am__objects_1) $(am__objects_2) \
|
||||
|
@ -307,7 +308,8 @@ CCFILES = \
|
|||
stringpool.cc \
|
||||
target-select.cc \
|
||||
version.cc \
|
||||
workqueue.cc
|
||||
workqueue.cc \
|
||||
workqueue-threads.cc
|
||||
|
||||
HFILES = \
|
||||
archive.h \
|
||||
|
@ -338,7 +340,8 @@ HFILES = \
|
|||
target-reloc.h \
|
||||
target-select.h \
|
||||
tls.h \
|
||||
workqueue.h
|
||||
workqueue.h \
|
||||
workqueue-internal.h
|
||||
|
||||
YFILES = \
|
||||
yyscript.y
|
||||
|
@ -487,6 +490,7 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue-threads.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_64.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yyscript.Po@am__quote@
|
||||
|
|
|
@ -475,6 +475,7 @@ Add_archive_symbols::run(Workqueue*)
|
|||
{
|
||||
// We no longer need to know about this archive.
|
||||
delete this->archive_;
|
||||
this->archive_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,14 @@ class Add_archive_symbols : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{
|
||||
if (this->archive_ == NULL)
|
||||
return "Add_archive_symbols";
|
||||
return "Add_archive_symbols " + this->archive_->file().filename();
|
||||
}
|
||||
|
||||
private:
|
||||
class Add_archive_symbols_locker;
|
||||
|
||||
|
|
|
@ -226,12 +226,9 @@ Symbol_table::do_allocate_commons(const General_options&,
|
|||
|
||||
off = align_address(off, ssym->value());
|
||||
|
||||
Size_type symsize = ssym->symsize();
|
||||
ssym->init(ssym->name(), poc, off, symsize, ssym->type(),
|
||||
ssym->binding(), ssym->visibility(), ssym->nonvis(),
|
||||
false);
|
||||
ssym->allocate_common(poc, off);
|
||||
|
||||
off += symsize;
|
||||
off += ssym->symsize();
|
||||
}
|
||||
|
||||
poc->set_space_size(off);
|
||||
|
|
|
@ -54,6 +54,10 @@ class Allocate_commons_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Allocate_commons_task"; }
|
||||
|
||||
private:
|
||||
class Allocate_commons_locker;
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// debug.h -- gold internal debugging support -*- C++ -*-
|
||||
|
||||
// Copyright 2007 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.
|
||||
|
||||
#ifndef GOLD_DEBUG_H
|
||||
#define GOLD_DEBUG_H
|
||||
|
||||
#include "parameters.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace gold
|
||||
{
|
||||
|
||||
// The different types of debugging we support. These are bitflags.
|
||||
|
||||
const int DEBUG_TASK = 1;
|
||||
|
||||
const int DEBUG_ALL = DEBUG_TASK;
|
||||
|
||||
// Print a debug message if TYPE is enabled. This is a macro so that
|
||||
// we only evaluate the arguments if necessary.
|
||||
|
||||
#define gold_debug(TYPE, FORMAT, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (is_debugging_enabled(TYPE)) \
|
||||
parameters->errors()->debug(FORMAT, __VA_ARGS__); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
#endif // !defined(GOLD_DEBUG_H)
|
|
@ -158,7 +158,7 @@ Dir_caches::lookup(const char* dirname) const
|
|||
|
||||
// The caches.
|
||||
|
||||
Dir_caches caches;
|
||||
Dir_caches* caches;
|
||||
|
||||
// A Task to read the directory.
|
||||
|
||||
|
@ -169,11 +169,18 @@ class Dir_cache_task : public gold::Task
|
|||
: dir_(dir), token_(token)
|
||||
{ }
|
||||
|
||||
Is_runnable_type is_runnable(gold::Workqueue*);
|
||||
Is_runnable_type
|
||||
is_runnable(gold::Workqueue*);
|
||||
|
||||
gold::Task_locker* locks(gold::Workqueue*);
|
||||
gold::Task_locker*
|
||||
locks(gold::Workqueue*);
|
||||
|
||||
void run(gold::Workqueue*);
|
||||
void
|
||||
run(gold::Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return std::string("Dir_cache_task ") + this->dir_; }
|
||||
|
||||
private:
|
||||
const char* dir_;
|
||||
|
@ -202,7 +209,7 @@ Dir_cache_task::locks(gold::Workqueue* workqueue)
|
|||
void
|
||||
Dir_cache_task::run(gold::Workqueue*)
|
||||
{
|
||||
caches.add(this->dir_);
|
||||
caches->add(this->dir_);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -214,6 +221,8 @@ void
|
|||
Dirsearch::initialize(Workqueue* workqueue,
|
||||
const General_options::Dir_list* directories)
|
||||
{
|
||||
gold_assert(caches == NULL);
|
||||
caches = new Dir_caches;
|
||||
this->directories_ = directories;
|
||||
for (General_options::Dir_list::const_iterator p = directories->begin();
|
||||
p != directories->end();
|
||||
|
@ -235,7 +244,7 @@ Dirsearch::find(const std::string& n1, const std::string& n2,
|
|||
p != this->directories_->end();
|
||||
++p)
|
||||
{
|
||||
Dir_cache* pdc = caches.lookup(p->name().c_str());
|
||||
Dir_cache* pdc = caches->lookup(p->name().c_str());
|
||||
gold_assert(pdc != NULL);
|
||||
if (pdc->find(n1))
|
||||
{
|
||||
|
|
|
@ -39,11 +39,20 @@ namespace gold
|
|||
const int Errors::max_undefined_error_report;
|
||||
|
||||
Errors::Errors(const char* program_name)
|
||||
: program_name_(program_name), lock_(), error_count_(0), warning_count_(0),
|
||||
undefined_symbols_()
|
||||
: program_name_(program_name), lock_(NULL), error_count_(0),
|
||||
warning_count_(0), undefined_symbols_()
|
||||
{
|
||||
}
|
||||
|
||||
// Initialize the lock_ field.
|
||||
|
||||
void
|
||||
Errors::initialize_lock()
|
||||
{
|
||||
if (this->lock_ == NULL)
|
||||
this->lock_ = new Lock;
|
||||
}
|
||||
|
||||
// Report a fatal error.
|
||||
|
||||
void
|
||||
|
@ -63,8 +72,10 @@ Errors::error(const char* format, va_list args)
|
|||
fprintf(stderr, "%s: ", this->program_name_);
|
||||
vfprintf(stderr, format, args);
|
||||
fputc('\n', stderr);
|
||||
|
||||
this->initialize_lock();
|
||||
{
|
||||
Hold_lock h(this->lock_);
|
||||
Hold_lock h(*this->lock_);
|
||||
++this->error_count_;
|
||||
}
|
||||
}
|
||||
|
@ -77,8 +88,10 @@ Errors::warning(const char* format, va_list args)
|
|||
fprintf(stderr, _("%s: warning: "), this->program_name_);
|
||||
vfprintf(stderr, format, args);
|
||||
fputc('\n', stderr);
|
||||
|
||||
this->initialize_lock();
|
||||
{
|
||||
Hold_lock h(this->lock_);
|
||||
Hold_lock h(*this->lock_);
|
||||
++this->warning_count_;
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +108,10 @@ Errors::error_at_location(const Relocate_info<size, big_endian>* relinfo,
|
|||
relinfo->location(relnum, reloffset).c_str());
|
||||
vfprintf(stderr, format, args);
|
||||
fputc('\n', stderr);
|
||||
|
||||
this->initialize_lock();
|
||||
{
|
||||
Hold_lock h(this->lock_);
|
||||
Hold_lock h(*this->lock_);
|
||||
++this->error_count_;
|
||||
}
|
||||
}
|
||||
|
@ -113,8 +128,10 @@ Errors::warning_at_location(const Relocate_info<size, big_endian>* relinfo,
|
|||
relinfo->location(relnum, reloffset).c_str());
|
||||
vfprintf(stderr, format, args);
|
||||
fputc('\n', stderr);
|
||||
|
||||
this->initialize_lock();
|
||||
{
|
||||
Hold_lock h(this->lock_);
|
||||
Hold_lock h(*this->lock_);
|
||||
++this->warning_count_;
|
||||
}
|
||||
}
|
||||
|
@ -127,8 +144,9 @@ Errors::undefined_symbol(const Symbol* sym,
|
|||
const Relocate_info<size, big_endian>* relinfo,
|
||||
size_t relnum, off_t reloffset)
|
||||
{
|
||||
this->initialize_lock();
|
||||
{
|
||||
Hold_lock h(this->lock_);
|
||||
Hold_lock h(*this->lock_);
|
||||
if (++this->undefined_symbols_[sym] >= max_undefined_error_report)
|
||||
return;
|
||||
++this->error_count_;
|
||||
|
@ -138,6 +156,20 @@ Errors::undefined_symbol(const Symbol* sym,
|
|||
sym->demangled_name().c_str());
|
||||
}
|
||||
|
||||
// Issue a debugging message.
|
||||
|
||||
void
|
||||
Errors::debug(const char* format, ...)
|
||||
{
|
||||
fprintf(stderr, _("%s: "), this->program_name_);
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
// The functions which the rest of the code actually calls.
|
||||
|
||||
|
|
|
@ -80,6 +80,10 @@ class Errors
|
|||
const Relocate_info<size, big_endian>* relinfo,
|
||||
size_t relnum, off_t reloffset);
|
||||
|
||||
// Report a debugging message.
|
||||
void
|
||||
debug(const char* format, ...) ATTRIBUTE_PRINTF_2;
|
||||
|
||||
// Return the number of errors.
|
||||
int
|
||||
error_count() const
|
||||
|
@ -89,6 +93,12 @@ class Errors
|
|||
Errors(const Errors&);
|
||||
Errors& operator=(const Errors&);
|
||||
|
||||
// Initialize the lock. We don't do this in the constructor because
|
||||
// lock initialization wants to know whether we are using threads or
|
||||
// not.
|
||||
void
|
||||
initialize_lock();
|
||||
|
||||
// The number of times we report an undefined symbol.
|
||||
static const int max_undefined_error_report = 5;
|
||||
|
||||
|
@ -96,7 +106,7 @@ class Errors
|
|||
const char* program_name_;
|
||||
// This class can be accessed from multiple threads. This lock is
|
||||
// used to control access to the data structures.
|
||||
Lock lock_;
|
||||
Lock* lock_;
|
||||
// Numbers of errors reported.
|
||||
int error_count_;
|
||||
// Number of warnings reported.
|
||||
|
|
|
@ -22,25 +22,63 @@
|
|||
|
||||
#include "gold.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "parameters.h"
|
||||
#include "gold-threads.h"
|
||||
|
||||
namespace gold
|
||||
{
|
||||
|
||||
// Class Lock_impl.
|
||||
class Condvar_impl_nothreads;
|
||||
|
||||
class Lock_impl
|
||||
// The non-threaded version of Lock_impl.
|
||||
|
||||
class Lock_impl_nothreads : public Lock_impl
|
||||
{
|
||||
public:
|
||||
Lock_impl();
|
||||
~Lock_impl();
|
||||
Lock_impl_nothreads()
|
||||
: acquired_(false)
|
||||
{ }
|
||||
|
||||
~Lock_impl_nothreads()
|
||||
{ gold_assert(!this->acquired_); }
|
||||
|
||||
void
|
||||
acquire()
|
||||
{
|
||||
gold_assert(!this->acquired_);
|
||||
this->acquired_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
release()
|
||||
{
|
||||
gold_assert(this->acquired_);
|
||||
this->acquired_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Condvar_impl_nothreads;
|
||||
|
||||
bool acquired_;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
|
||||
class Condvar_impl_threads;
|
||||
|
||||
// The threaded version of Lock_impl.
|
||||
|
||||
class Lock_impl_threads : public Lock_impl
|
||||
{
|
||||
public:
|
||||
Lock_impl_threads();
|
||||
~Lock_impl_threads();
|
||||
|
||||
void acquire();
|
||||
|
||||
|
@ -48,90 +86,74 @@ class Lock_impl
|
|||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
Lock_impl(const Lock_impl&);
|
||||
Lock_impl& operator=(const Lock_impl&);
|
||||
Lock_impl_threads(const Lock_impl_threads&);
|
||||
Lock_impl_threads& operator=(const Lock_impl_threads&);
|
||||
|
||||
friend class Condvar_impl;
|
||||
friend class Condvar_impl_threads;
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
pthread_mutex_t mutex_;
|
||||
#else
|
||||
bool acquired_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
|
||||
Lock_impl::Lock_impl()
|
||||
Lock_impl_threads::Lock_impl_threads()
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(errno));
|
||||
int err = pthread_mutexattr_init(&attr);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthead_mutextattr_init failed: %s"), strerror(err));
|
||||
#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP
|
||||
if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0)
|
||||
gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(errno));
|
||||
err = pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutextattr_settype failed: %s"), strerror(err));
|
||||
#endif
|
||||
|
||||
if (pthread_mutex_init (&this->mutex_, &attr) != 0)
|
||||
gold_fatal(_("pthread_mutex_init failed: %s"), strerror(errno));
|
||||
err = pthread_mutex_init (&this->mutex_, &attr);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutex_init failed: %s"), strerror(err));
|
||||
|
||||
if (pthread_mutexattr_destroy(&attr) != 0)
|
||||
gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(errno));
|
||||
err = pthread_mutexattr_destroy(&attr);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutexattr_destroy failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
Lock_impl::~Lock_impl()
|
||||
Lock_impl_threads::~Lock_impl_threads()
|
||||
{
|
||||
if (pthread_mutex_destroy(&this->mutex_) != 0)
|
||||
gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(errno));
|
||||
int err = pthread_mutex_destroy(&this->mutex_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutex_destroy failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Lock_impl::acquire()
|
||||
Lock_impl_threads::acquire()
|
||||
{
|
||||
if (pthread_mutex_lock(&this->mutex_) != 0)
|
||||
gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(errno));
|
||||
int err = pthread_mutex_lock(&this->mutex_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutex_lock failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Lock_impl::release()
|
||||
Lock_impl_threads::release()
|
||||
{
|
||||
if (pthread_mutex_unlock(&this->mutex_) != 0)
|
||||
gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(errno));
|
||||
int err = pthread_mutex_unlock(&this->mutex_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_mutex_unlock failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
#else // !defined(ENABLE_THREADS)
|
||||
#endif // defined(ENABLE_THREADS)
|
||||
|
||||
Lock_impl::Lock_impl()
|
||||
: acquired_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Lock_impl::~Lock_impl()
|
||||
{
|
||||
gold_assert(!this->acquired_);
|
||||
}
|
||||
|
||||
void
|
||||
Lock_impl::acquire()
|
||||
{
|
||||
gold_assert(!this->acquired_);
|
||||
this->acquired_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
Lock_impl::release()
|
||||
{
|
||||
gold_assert(this->acquired_);
|
||||
this->acquired_ = false;
|
||||
}
|
||||
|
||||
#endif // !defined(ENABLE_THREADS)
|
||||
|
||||
// Methods for Lock class.
|
||||
// Class Lock.
|
||||
|
||||
Lock::Lock()
|
||||
{
|
||||
this->lock_ = new Lock_impl;
|
||||
if (!parameters->threads())
|
||||
this->lock_ = new Lock_impl_nothreads;
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_THREADS
|
||||
this->lock_ = new Lock_impl_threads;
|
||||
#else
|
||||
gold_unreachable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Lock::~Lock()
|
||||
|
@ -139,96 +161,113 @@ Lock::~Lock()
|
|||
delete this->lock_;
|
||||
}
|
||||
|
||||
void
|
||||
Lock::acquire()
|
||||
{
|
||||
this->lock_->acquire();
|
||||
}
|
||||
// The non-threaded version of Condvar_impl.
|
||||
|
||||
void
|
||||
Lock::release()
|
||||
{
|
||||
this->lock_->release();
|
||||
}
|
||||
|
||||
// Class Condvar_impl.
|
||||
|
||||
class Condvar_impl
|
||||
class Condvar_impl_nothreads : public Condvar_impl
|
||||
{
|
||||
public:
|
||||
Condvar_impl();
|
||||
~Condvar_impl();
|
||||
Condvar_impl_nothreads()
|
||||
{ }
|
||||
|
||||
void wait(Lock_impl*);
|
||||
void signal();
|
||||
~Condvar_impl_nothreads()
|
||||
{ }
|
||||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
Condvar_impl(const Condvar_impl&);
|
||||
Condvar_impl& operator=(const Condvar_impl&);
|
||||
void
|
||||
wait(Lock_impl* li)
|
||||
{ gold_assert(static_cast<Lock_impl_nothreads*>(li)->acquired_); }
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
pthread_cond_t cond_;
|
||||
#endif
|
||||
void
|
||||
signal()
|
||||
{ }
|
||||
|
||||
void
|
||||
broadcast()
|
||||
{ }
|
||||
};
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
|
||||
Condvar_impl::Condvar_impl()
|
||||
// The threaded version of Condvar_impl.
|
||||
|
||||
class Condvar_impl_threads : public Condvar_impl
|
||||
{
|
||||
if (pthread_cond_init(&this->cond_, NULL) != 0)
|
||||
gold_fatal(_("pthread_cond_init failed: %s"), strerror(errno));
|
||||
public:
|
||||
Condvar_impl_threads();
|
||||
~Condvar_impl_threads();
|
||||
|
||||
void
|
||||
wait(Lock_impl*);
|
||||
|
||||
void
|
||||
signal();
|
||||
|
||||
void
|
||||
broadcast();
|
||||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
Condvar_impl_threads(const Condvar_impl_threads&);
|
||||
Condvar_impl_threads& operator=(const Condvar_impl_threads&);
|
||||
|
||||
pthread_cond_t cond_;
|
||||
};
|
||||
|
||||
Condvar_impl_threads::Condvar_impl_threads()
|
||||
{
|
||||
int err = pthread_cond_init(&this->cond_, NULL);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_cond_init failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
Condvar_impl::~Condvar_impl()
|
||||
Condvar_impl_threads::~Condvar_impl_threads()
|
||||
{
|
||||
if (pthread_cond_destroy(&this->cond_) != 0)
|
||||
gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(errno));
|
||||
int err = pthread_cond_destroy(&this->cond_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_cond_destroy failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Condvar_impl::wait(Lock_impl* li)
|
||||
Condvar_impl_threads::wait(Lock_impl* li)
|
||||
{
|
||||
if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0)
|
||||
gold_fatal(_("pthread_cond_wait failed: %s"), strerror(errno));
|
||||
Lock_impl_threads* lit = static_cast<Lock_impl_threads*>(li);
|
||||
int err = pthread_cond_wait(&this->cond_, &lit->mutex_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_cond_wait failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Condvar_impl::signal()
|
||||
{
|
||||
if (pthread_cond_signal(&this->cond_) != 0)
|
||||
gold_fatal(_("pthread_cond_signal failed: %s"), strerror(errno));
|
||||
}
|
||||
|
||||
#else // !defined(ENABLE_THREADS)
|
||||
|
||||
Condvar_impl::Condvar_impl()
|
||||
{
|
||||
}
|
||||
|
||||
Condvar_impl::~Condvar_impl()
|
||||
Condvar_impl_threads::signal()
|
||||
{
|
||||
int err = pthread_cond_signal(&this->cond_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_cond_signal failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Condvar_impl::wait(Lock_impl* li)
|
||||
Condvar_impl_threads::broadcast()
|
||||
{
|
||||
gold_assert(li->acquired_);
|
||||
int err = pthread_cond_broadcast(&this->cond_);
|
||||
if (err != 0)
|
||||
gold_fatal(_("pthread_cond_broadcast failed: %s"), strerror(err));
|
||||
}
|
||||
|
||||
void
|
||||
Condvar_impl::signal()
|
||||
{
|
||||
}
|
||||
|
||||
#endif // !defined(ENABLE_THREADS)
|
||||
#endif // defined(ENABLE_THREADS)
|
||||
|
||||
// Methods for Condvar class.
|
||||
|
||||
Condvar::Condvar(Lock& lock)
|
||||
: lock_(lock)
|
||||
{
|
||||
this->condvar_ = new Condvar_impl;
|
||||
if (!parameters->threads())
|
||||
this->condvar_ = new Condvar_impl_nothreads;
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_THREADS
|
||||
this->condvar_ = new Condvar_impl_threads;
|
||||
#else
|
||||
gold_unreachable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Condvar::~Condvar()
|
||||
|
@ -236,16 +275,4 @@ Condvar::~Condvar()
|
|||
delete this->condvar_;
|
||||
}
|
||||
|
||||
void
|
||||
Condvar::wait()
|
||||
{
|
||||
this->condvar_->wait(this->lock_.get_impl());
|
||||
}
|
||||
|
||||
void
|
||||
Condvar::signal()
|
||||
{
|
||||
this->condvar_->signal();
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
|
|
@ -34,24 +34,45 @@
|
|||
namespace gold
|
||||
{
|
||||
|
||||
class Lock_impl;
|
||||
class Condvar;
|
||||
|
||||
// The interface for the implementation of a Lock.
|
||||
|
||||
class Lock_impl
|
||||
{
|
||||
public:
|
||||
Lock_impl()
|
||||
{ }
|
||||
|
||||
virtual
|
||||
~Lock_impl()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
acquire() = 0;
|
||||
|
||||
virtual void
|
||||
release() = 0;
|
||||
};
|
||||
|
||||
// A simple lock class.
|
||||
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock();
|
||||
|
||||
~Lock();
|
||||
|
||||
// Acquire the lock.
|
||||
void
|
||||
acquire();
|
||||
acquire()
|
||||
{ this->lock_->acquire(); }
|
||||
|
||||
// Release the lock.
|
||||
void
|
||||
release();
|
||||
release()
|
||||
{ this->lock_->release(); }
|
||||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
|
@ -86,7 +107,27 @@ class Hold_lock
|
|||
Lock& lock_;
|
||||
};
|
||||
|
||||
class Condvar_impl;
|
||||
// The interface for the implementation of a condition variable.
|
||||
|
||||
class Condvar_impl
|
||||
{
|
||||
public:
|
||||
Condvar_impl()
|
||||
{ }
|
||||
|
||||
virtual
|
||||
~Condvar_impl()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
wait(Lock_impl*) = 0;
|
||||
|
||||
virtual void
|
||||
signal() = 0;
|
||||
|
||||
virtual void
|
||||
broadcast() = 0;
|
||||
};
|
||||
|
||||
// A simple condition variable class. It is always associated with a
|
||||
// specific lock.
|
||||
|
@ -100,12 +141,22 @@ class Condvar
|
|||
// Wait for the condition variable to be signalled. This should
|
||||
// only be called when the lock is held.
|
||||
void
|
||||
wait();
|
||||
wait()
|
||||
{ this->condvar_->wait(this->lock_.get_impl()); }
|
||||
|
||||
// Signal the condition variable. This should only be called when
|
||||
// the lock is held.
|
||||
// Signal the condition variable--wake up at least one thread
|
||||
// waiting on the condition variable. This should only be called
|
||||
// when the lock is held.
|
||||
void
|
||||
signal();
|
||||
signal()
|
||||
{ this->condvar_->signal(); }
|
||||
|
||||
// Broadcast the condition variable--wake up all threads waiting on
|
||||
// the condition variable. This should only be called when the lock
|
||||
// is held.
|
||||
void
|
||||
broadcast()
|
||||
{ this->condvar_->broadcast(); }
|
||||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
|
|
|
@ -143,7 +143,8 @@ queue_initial_tasks(const General_options& options,
|
|||
input_objects,
|
||||
symtab,
|
||||
layout),
|
||||
this_blocker));
|
||||
this_blocker,
|
||||
"Task_function Middle_runner"));
|
||||
}
|
||||
|
||||
// Queue up the middle set of tasks. These are the tasks which run
|
||||
|
@ -239,7 +240,8 @@ queue_middle_tasks(const General_options& options,
|
|||
input_objects,
|
||||
symtab,
|
||||
layout),
|
||||
blocker));
|
||||
blocker,
|
||||
"Task_function Layout_task_runner"));
|
||||
}
|
||||
|
||||
// Queue up the final set of tasks. This is called at the end of
|
||||
|
@ -312,7 +314,8 @@ queue_final_tasks(const General_options& options,
|
|||
// Queue a task to close the output file. This will be blocked by
|
||||
// FINAL_BLOCKER.
|
||||
workqueue->queue(new Task_function(new Close_task_runner(of),
|
||||
final_blocker));
|
||||
final_blocker,
|
||||
"Task_function Close_task_runner"));
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
|
|
@ -463,6 +463,10 @@ class Write_sections_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Write_sections_task"; }
|
||||
|
||||
private:
|
||||
class Write_sections_locker;
|
||||
|
||||
|
@ -494,6 +498,10 @@ class Write_data_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Write_data_task"; }
|
||||
|
||||
private:
|
||||
const Layout* layout_;
|
||||
const Symbol_table* symtab_;
|
||||
|
@ -525,6 +533,10 @@ class Write_symbols_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Write_symbols_task"; }
|
||||
|
||||
private:
|
||||
const Symbol_table* symtab_;
|
||||
const Input_objects* input_objects_;
|
||||
|
@ -561,6 +573,10 @@ class Write_after_input_sections_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Write_after_input_sections_task"; }
|
||||
|
||||
private:
|
||||
class Write_sections_locker;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "filenames.h"
|
||||
#include "libiberty.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "options.h"
|
||||
|
||||
namespace gold
|
||||
|
@ -106,6 +107,17 @@ struct options::One_z_option
|
|||
void (General_options::*set)();
|
||||
};
|
||||
|
||||
// We have a separate table for --debug options.
|
||||
|
||||
struct options::One_debug_option
|
||||
{
|
||||
// The name of the option.
|
||||
const char* name;
|
||||
|
||||
// The flags to turn on.
|
||||
unsigned int debug_flags;
|
||||
};
|
||||
|
||||
class options::Command_line_options
|
||||
{
|
||||
public:
|
||||
|
@ -113,6 +125,8 @@ class options::Command_line_options
|
|||
static const int options_size;
|
||||
static const One_z_option z_options[];
|
||||
static const int z_options_size;
|
||||
static const One_debug_option debug_options[];
|
||||
static const int debug_options_size;
|
||||
};
|
||||
|
||||
} // End namespace gold.
|
||||
|
@ -247,7 +261,7 @@ help(int, char**, char*, bool, gold::Command_line*)
|
|||
std::puts(options[i].doc);
|
||||
}
|
||||
|
||||
::exit(0);
|
||||
::exit(EXIT_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -258,7 +272,7 @@ int
|
|||
version(int, char**, char* opt, bool, gold::Command_line*)
|
||||
{
|
||||
gold::print_version(opt[0] == 'v' && opt[1] == '\0');
|
||||
::exit(0);
|
||||
::exit(EXIT_SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -466,7 +480,10 @@ options::Command_line_options::options[] =
|
|||
SPECIAL('\0', "help", N_("Report usage information"), NULL,
|
||||
TWO_DASHES, &help),
|
||||
SPECIAL('v', "version", N_("Report version information"), NULL,
|
||||
TWO_DASHES, &version)
|
||||
TWO_DASHES, &version),
|
||||
GENERAL_ARG('\0', "debug", N_("Turn on debugging (all,task)"),
|
||||
N_("--debug=TYPE"), TWO_DASHES,
|
||||
&General_options::handle_debug_option)
|
||||
};
|
||||
|
||||
const int options::Command_line_options::options_size =
|
||||
|
@ -484,6 +501,18 @@ options::Command_line_options::z_options[] =
|
|||
const int options::Command_line_options::z_options_size =
|
||||
sizeof(z_options) / sizeof(z_options[0]);
|
||||
|
||||
// The --debug options.
|
||||
|
||||
const options::One_debug_option
|
||||
options::Command_line_options::debug_options[] =
|
||||
{
|
||||
{ "all", DEBUG_ALL },
|
||||
{ "task", DEBUG_TASK },
|
||||
};
|
||||
|
||||
const int options::Command_line_options::debug_options_size =
|
||||
sizeof(debug_options) / sizeof(debug_options[0]);
|
||||
|
||||
// The default values for the general options.
|
||||
|
||||
General_options::General_options()
|
||||
|
@ -509,7 +538,8 @@ General_options::General_options()
|
|||
thread_count_initial_(0),
|
||||
thread_count_middle_(0),
|
||||
thread_count_final_(0),
|
||||
execstack_(EXECSTACK_FROM_INPUT)
|
||||
execstack_(EXECSTACK_FROM_INPUT),
|
||||
debug_(0)
|
||||
{
|
||||
// We initialize demangle_ based on the environment variable
|
||||
// COLLECT_NO_DEMANGLE. The gcc collect2 program will demangle the
|
||||
|
@ -547,7 +577,30 @@ General_options::handle_z_option(const char* arg)
|
|||
|
||||
fprintf(stderr, _("%s: unrecognized -z subcommand: %s\n"),
|
||||
program_name, arg);
|
||||
::exit(1);
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Handle the --debug option.
|
||||
|
||||
void
|
||||
General_options::handle_debug_option(const char* arg)
|
||||
{
|
||||
const int debug_options_size =
|
||||
options::Command_line_options::debug_options_size;
|
||||
const gold::options::One_debug_option* debug_options =
|
||||
options::Command_line_options::debug_options;
|
||||
for (int i = 0; i < debug_options_size; ++i)
|
||||
{
|
||||
if (strcmp(arg, debug_options[i].name) == 0)
|
||||
{
|
||||
this->set_debug(debug_options[i].debug_flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, _("%s: unrecognized --debug subcommand: %s\n"),
|
||||
program_name, arg);
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Add the sysroot, if any, to the search paths.
|
||||
|
@ -961,7 +1014,7 @@ Command_line::usage()
|
|||
fprintf(stderr,
|
||||
_("%s: use the --help option for usage information\n"),
|
||||
program_name);
|
||||
::exit(1);
|
||||
::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -46,11 +46,13 @@ class Command_line;
|
|||
class Input_file_group;
|
||||
class Position_dependent_options;
|
||||
|
||||
namespace options {
|
||||
namespace options
|
||||
{
|
||||
|
||||
class Command_line_options;
|
||||
struct One_option;
|
||||
struct One_z_option;
|
||||
struct One_debug_option;
|
||||
|
||||
} // End namespace gold::options.
|
||||
|
||||
|
@ -249,6 +251,11 @@ class General_options
|
|||
is_stack_executable() const
|
||||
{ return this->execstack_ == EXECSTACK_YES; }
|
||||
|
||||
// --debug
|
||||
unsigned int
|
||||
debug() const
|
||||
{ return this->debug_; }
|
||||
|
||||
private:
|
||||
// Don't copy this structure.
|
||||
General_options(const General_options&);
|
||||
|
@ -444,10 +451,18 @@ class General_options
|
|||
set_noexecstack()
|
||||
{ this->execstack_ = EXECSTACK_NO; }
|
||||
|
||||
void
|
||||
set_debug(unsigned int flags)
|
||||
{ this->debug_ = flags; }
|
||||
|
||||
// Handle the -z option.
|
||||
void
|
||||
handle_z_option(const char*);
|
||||
|
||||
// Handle the --debug option.
|
||||
void
|
||||
handle_debug_option(const char*);
|
||||
|
||||
// Apply any sysroot to the directory lists.
|
||||
void
|
||||
add_sysroot();
|
||||
|
@ -476,6 +491,7 @@ class General_options
|
|||
int thread_count_middle_;
|
||||
int thread_count_final_;
|
||||
Execstack execstack_;
|
||||
unsigned int debug_;
|
||||
};
|
||||
|
||||
// The current state of the position dependent options.
|
||||
|
|
|
@ -31,11 +31,11 @@ namespace gold
|
|||
// Initialize the parameters from the options.
|
||||
|
||||
Parameters::Parameters(Errors* errors)
|
||||
: errors_(errors), output_file_name_(NULL),
|
||||
: errors_(errors), threads_(false), output_file_name_(NULL),
|
||||
output_file_type_(OUTPUT_INVALID), sysroot_(),
|
||||
strip_(STRIP_INVALID), allow_shlib_undefined_(false),
|
||||
symbolic_(false), demangle_(false), detect_odr_violations_(false),
|
||||
optimization_level_(0), export_dynamic_(false),
|
||||
optimization_level_(0), export_dynamic_(false), debug_(0),
|
||||
is_doing_static_link_valid_(false), doing_static_link_(false),
|
||||
is_size_and_endian_valid_(false), size_(0), is_big_endian_(false)
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ Parameters::Parameters(Errors* errors)
|
|||
void
|
||||
Parameters::set_from_options(const General_options* options)
|
||||
{
|
||||
this->threads_ = options->threads();
|
||||
this->output_file_name_ = options->output_file_name();
|
||||
this->sysroot_ = options->sysroot();
|
||||
this->allow_shlib_undefined_ = options->allow_shlib_undefined();
|
||||
|
@ -54,6 +55,7 @@ Parameters::set_from_options(const General_options* options)
|
|||
this->detect_odr_violations_ = options->detect_odr_violations();
|
||||
this->optimization_level_ = options->optimization_level();
|
||||
this->export_dynamic_ = options->export_dynamic();
|
||||
this->debug_ = options->debug();
|
||||
|
||||
if (options->is_shared())
|
||||
this->output_file_type_ = OUTPUT_SHARED;
|
||||
|
|
|
@ -47,6 +47,14 @@ class Parameters
|
|||
errors() const
|
||||
{ return this->errors_; }
|
||||
|
||||
// Whether to use threads.
|
||||
bool
|
||||
threads() const
|
||||
{
|
||||
gold_assert(this->options_valid_);
|
||||
return this->threads_;
|
||||
}
|
||||
|
||||
// Return the output file name.
|
||||
const char*
|
||||
output_file_name() const
|
||||
|
@ -166,6 +174,15 @@ class Parameters
|
|||
return this->export_dynamic_;
|
||||
}
|
||||
|
||||
// Return the debug flags. These are the flags for which we should
|
||||
// report internal debugging information.
|
||||
unsigned int
|
||||
debug() const
|
||||
{
|
||||
gold_assert(this->options_valid_);
|
||||
return this->debug_;
|
||||
}
|
||||
|
||||
// Whether we are doing a static link--a link in which none of the
|
||||
// input files are shared libraries. This is only known after we
|
||||
// have seen all the input files.
|
||||
|
@ -239,6 +256,8 @@ class Parameters
|
|||
|
||||
// Whether the fields set from the options are valid.
|
||||
bool options_valid_;
|
||||
// Whether to use threads.
|
||||
bool threads_;
|
||||
// The output file name.
|
||||
const char* output_file_name_;
|
||||
// The type of the output file.
|
||||
|
@ -259,6 +278,8 @@ class Parameters
|
|||
int optimization_level_;
|
||||
// Whether the -E/--export-dynamic flag is set.
|
||||
bool export_dynamic_;
|
||||
// The debug flags.
|
||||
unsigned int debug_;
|
||||
|
||||
// Whether the doing_static_link_ field is valid.
|
||||
bool is_doing_static_link_valid_;
|
||||
|
@ -287,6 +308,13 @@ extern void set_parameters_size_and_endianness(int size, bool is_big_endian);
|
|||
// Set whether we are doing a static link.
|
||||
extern void set_parameters_doing_static_link(bool doing_static_link);
|
||||
|
||||
// Return whether we are doing a particular debugging type. The
|
||||
// argument is one of the flags from debug.h.
|
||||
|
||||
inline bool
|
||||
is_debugging_enabled(unsigned int type)
|
||||
{ return (parameters->debug() & type) != 0; }
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
#endif // !defined(GOLD_PARAMETERS_H)
|
||||
|
|
|
@ -54,4 +54,6 @@ tls.h
|
|||
version.cc
|
||||
workqueue.cc
|
||||
workqueue.h
|
||||
workqueue-internal.h
|
||||
workqueue-threads.cc
|
||||
x86_64.cc
|
||||
|
|
522
gold/po/gold.pot
522
gold/po/gold.pot
File diff suppressed because it is too large
Load Diff
|
@ -72,6 +72,10 @@ class Unblock_token : public Task
|
|||
run(Workqueue*)
|
||||
{ }
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Unblock_token"; }
|
||||
|
||||
private:
|
||||
Task_token* this_blocker_;
|
||||
Task_token* next_blocker_;
|
||||
|
@ -273,6 +277,35 @@ Read_symbols::do_group(Workqueue* workqueue)
|
|||
this->next_blocker_));
|
||||
}
|
||||
|
||||
// Return a debugging name for a Read_symbols task.
|
||||
|
||||
std::string
|
||||
Read_symbols::get_name() const
|
||||
{
|
||||
if (!this->input_argument_->is_group())
|
||||
{
|
||||
std::string ret("Read_symbols ");
|
||||
if (this->input_argument_->file().is_lib())
|
||||
ret += "-l";
|
||||
ret += this->input_argument_->file().name();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ret("Read_symbols group (");
|
||||
bool add_space = false;
|
||||
const Input_file_group* group = this->input_argument_->group();
|
||||
for (Input_file_group::const_iterator p = group->begin();
|
||||
p != group->end();
|
||||
++p)
|
||||
{
|
||||
if (add_space)
|
||||
ret += ' ';
|
||||
ret += p->file().name();
|
||||
add_space = true;
|
||||
}
|
||||
return ret + ')';
|
||||
}
|
||||
|
||||
// Class Add_symbols.
|
||||
|
||||
Add_symbols::~Add_symbols()
|
||||
|
|
|
@ -77,6 +77,9 @@ class Read_symbols : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const;
|
||||
|
||||
private:
|
||||
// Handle an archive group.
|
||||
void
|
||||
|
@ -129,6 +132,10 @@ class Add_symbols : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Add_symbols " + this->object_->name(); }
|
||||
|
||||
private:
|
||||
class Add_symbols_locker;
|
||||
|
||||
|
@ -201,6 +208,10 @@ class Finish_group : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Finish_group"; }
|
||||
|
||||
private:
|
||||
Input_objects* input_objects_;
|
||||
Symbol_table* symtab_;
|
||||
|
|
|
@ -63,6 +63,14 @@ Read_relocs::run(Workqueue* workqueue)
|
|||
this->symtab_lock_, this->blocker_));
|
||||
}
|
||||
|
||||
// Return a debugging name for the task.
|
||||
|
||||
std::string
|
||||
Read_relocs::get_name() const
|
||||
{
|
||||
return "Read_relocs " + this->object_->name();
|
||||
}
|
||||
|
||||
// Scan_relocs methods.
|
||||
|
||||
// These tasks scan the relocations read by Read_relocs and mark up
|
||||
|
@ -114,6 +122,14 @@ Scan_relocs::run(Workqueue*)
|
|||
this->rd_ = NULL;
|
||||
}
|
||||
|
||||
// Return a debugging name for the task.
|
||||
|
||||
std::string
|
||||
Scan_relocs::get_name() const
|
||||
{
|
||||
return "Scan_relocs " + this->object_->name();
|
||||
}
|
||||
|
||||
// Relocate_task methods.
|
||||
|
||||
// We may have to wait for the output sections to be written.
|
||||
|
@ -125,6 +141,9 @@ Relocate_task::is_runnable(Workqueue*)
|
|||
&& this->output_sections_blocker_->is_blocked())
|
||||
return IS_BLOCKED;
|
||||
|
||||
if (this->object_->is_locked())
|
||||
return IS_LOCKED;
|
||||
|
||||
return IS_RUNNABLE;
|
||||
}
|
||||
|
||||
|
@ -166,6 +185,14 @@ Relocate_task::run(Workqueue*)
|
|||
this->of_);
|
||||
}
|
||||
|
||||
// Return a debugging name for the task.
|
||||
|
||||
std::string
|
||||
Relocate_task::get_name() const
|
||||
{
|
||||
return "Relocate_task " + this->object_->name();
|
||||
}
|
||||
|
||||
// Read the relocs and local symbols from the object file and store
|
||||
// the information in RD.
|
||||
|
||||
|
|
|
@ -78,6 +78,9 @@ class Read_relocs : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const;
|
||||
|
||||
private:
|
||||
const General_options& options_;
|
||||
Symbol_table* symtab_;
|
||||
|
@ -113,6 +116,9 @@ class Scan_relocs : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const;
|
||||
|
||||
private:
|
||||
class Scan_relocs_locker;
|
||||
|
||||
|
@ -151,6 +157,9 @@ class Relocate_task : public Task
|
|||
void
|
||||
run(Workqueue*);
|
||||
|
||||
std::string
|
||||
get_name() const;
|
||||
|
||||
private:
|
||||
class Relocate_locker;
|
||||
|
||||
|
|
|
@ -815,6 +815,10 @@ class Script_unblock : public Task
|
|||
run(Workqueue*)
|
||||
{ }
|
||||
|
||||
std::string
|
||||
get_name() const
|
||||
{ return "Script_unblock"; }
|
||||
|
||||
private:
|
||||
Task_token* this_blocker_;
|
||||
Task_token* next_blocker_;
|
||||
|
|
|
@ -159,6 +159,17 @@ Symbol::init_base(const char* name, elfcpp::STT type,
|
|||
this->in_reg_ = true;
|
||||
}
|
||||
|
||||
// Allocate a common symbol in the base.
|
||||
|
||||
void
|
||||
Symbol::allocate_base_common(Output_data* od)
|
||||
{
|
||||
gold_assert(this->is_common());
|
||||
this->source_ = IN_OUTPUT_DATA;
|
||||
this->u_.in_output_data.output_data = od;
|
||||
this->u_.in_output_data.offset_is_from_end = false;
|
||||
}
|
||||
|
||||
// Initialize the fields in Sized_symbol for SYM in OBJECT.
|
||||
|
||||
template<int size>
|
||||
|
@ -219,6 +230,16 @@ Sized_symbol<size>::init(const char* name, Value_type value, Size_type symsize,
|
|||
this->symsize_ = symsize;
|
||||
}
|
||||
|
||||
// Allocate a common symbol.
|
||||
|
||||
template<int size>
|
||||
void
|
||||
Sized_symbol<size>::allocate_common(Output_data* od, Value_type value)
|
||||
{
|
||||
this->allocate_base_common(od);
|
||||
this->value_ = value;
|
||||
}
|
||||
|
||||
// Return true if this symbol should be added to the dynamic symbol
|
||||
// table.
|
||||
|
||||
|
@ -2017,6 +2038,18 @@ Warnings::issue_warning(const Symbol* sym,
|
|||
// script to restrict this to only the ones needed for implemented
|
||||
// targets.
|
||||
|
||||
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
|
||||
template
|
||||
void
|
||||
Sized_symbol<32>::allocate_common(Output_data*, Value_type);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
|
||||
template
|
||||
void
|
||||
Sized_symbol<64>::allocate_common(Output_data*, Value_type);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TARGET_32_LITTLE
|
||||
template
|
||||
void
|
||||
|
|
|
@ -598,6 +598,11 @@ class Symbol
|
|||
void
|
||||
override_base_with_special(const Symbol* from);
|
||||
|
||||
// Allocate a common symbol by giving it a location in the output
|
||||
// file.
|
||||
void
|
||||
allocate_base_common(Output_data*);
|
||||
|
||||
private:
|
||||
Symbol(const Symbol&);
|
||||
Symbol& operator=(const Symbol&);
|
||||
|
@ -798,6 +803,11 @@ class Sized_symbol : public Symbol
|
|||
set_value(Value_type value)
|
||||
{ this->value_ = value; }
|
||||
|
||||
// Allocate a common symbol by giving it a location in the output
|
||||
// file.
|
||||
void
|
||||
allocate_common(Output_data*, Value_type value);
|
||||
|
||||
private:
|
||||
Sized_symbol(const Sized_symbol&);
|
||||
Sized_symbol& operator=(const Sized_symbol&);
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
// workqueue-internal.h -- internal work queue header for gold -*- C++ -*-
|
||||
|
||||
// Copyright 2006, 2007 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.
|
||||
|
||||
#ifndef GOLD_WORKQUEUE_INTERNAL_H
|
||||
#define GOLD_WORKQUEUE_INTERNAL_H
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "gold-threads.h"
|
||||
#include "workqueue.h"
|
||||
|
||||
// This is an internal header file for different gold workqueue
|
||||
// implementations.
|
||||
|
||||
namespace gold
|
||||
{
|
||||
|
||||
class Workqueue_thread;
|
||||
|
||||
// The Workqueue_runner abstract class. This is the interface used by
|
||||
// the general workqueue code to actually run a task.
|
||||
|
||||
class Workqueue_runner
|
||||
{
|
||||
public:
|
||||
Workqueue_runner(Workqueue* workqueue)
|
||||
: workqueue_(workqueue)
|
||||
{ }
|
||||
virtual ~Workqueue_runner()
|
||||
{ }
|
||||
|
||||
// Run a task. This is always called in the main thread.
|
||||
virtual void
|
||||
run(Task*, Task_locker*) = 0;
|
||||
|
||||
// Set the number of threads to use. This is ignored when not using
|
||||
// threads.
|
||||
virtual void
|
||||
set_thread_count(int) = 0;
|
||||
|
||||
protected:
|
||||
// This is called by an implementation when a task is completed.
|
||||
void completed(Task* t, Task_locker* tl)
|
||||
{ this->workqueue_->completed(t, tl); }
|
||||
|
||||
Workqueue* get_workqueue() const
|
||||
{ return this->workqueue_; }
|
||||
|
||||
private:
|
||||
Workqueue* workqueue_;
|
||||
};
|
||||
|
||||
// The threaded instantiation of Workqueue_runner.
|
||||
|
||||
class Workqueue_runner_threadpool : public Workqueue_runner
|
||||
{
|
||||
public:
|
||||
Workqueue_runner_threadpool(Workqueue* workqueue);
|
||||
|
||||
~Workqueue_runner_threadpool();
|
||||
|
||||
void
|
||||
run(Task*, Task_locker*);
|
||||
|
||||
void
|
||||
set_thread_count(int);
|
||||
|
||||
private:
|
||||
// This class can not be copied.
|
||||
Workqueue_runner_threadpool(const Workqueue_runner_threadpool&);
|
||||
Workqueue_runner_threadpool& operator=(const Workqueue_runner_threadpool&);
|
||||
|
||||
// Return the next Task and Task_locker to run. This returns false
|
||||
// if the calling thread should simply exit.
|
||||
bool
|
||||
get_next(Task**, Task_locker**);
|
||||
|
||||
// This is called when the thread completes a task.
|
||||
void
|
||||
thread_completed(Task*, Task_locker*);
|
||||
|
||||
// The Workqueue_thread class calls functions from this and from the
|
||||
// parent Workqueue_runner.
|
||||
friend class Workqueue_thread;
|
||||
|
||||
// An entry on the queue of tasks to run.
|
||||
typedef std::pair<Task*, Task_locker*> Task_queue_entry;
|
||||
|
||||
// A queue of tasks to run.
|
||||
typedef std::queue<Task_queue_entry> Task_queue;
|
||||
|
||||
// The number of threads we want to create. This is only changed in
|
||||
// the main thread or when only one thread is running. This is set
|
||||
// to zero when all threads should exit.
|
||||
int desired_thread_count_;
|
||||
// A lock controlling access to the remaining fields.
|
||||
Lock lock_;
|
||||
// The number of threads we have created.
|
||||
int actual_thread_count_;
|
||||
// The number of threads which are running a task.
|
||||
int running_thread_count_;
|
||||
// A queue of tasks to run.
|
||||
Task_queue task_queue_;
|
||||
// A condition variable which signals when the task_queue_ changed.
|
||||
Condvar task_queue_condvar_;
|
||||
};
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
#endif // !defined(GOLD_WORKQUEUE_INTERNAL_H)
|
|
@ -0,0 +1,266 @@
|
|||
// workqueue-threads.cc -- the threaded workqueue for gold
|
||||
|
||||
// Copyright 2007 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_runner_threadpool*);
|
||||
|
||||
~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*);
|
||||
|
||||
// The main loop of the thread.
|
||||
void
|
||||
run();
|
||||
|
||||
// A pointer to the threadpool that this thread is part of.
|
||||
Workqueue_runner_threadpool* threadpool_;
|
||||
// The thread ID.
|
||||
pthread_t tid_;
|
||||
};
|
||||
|
||||
// Create the thread in the constructor.
|
||||
|
||||
Workqueue_thread::Workqueue_thread(Workqueue_runner_threadpool* threadpool)
|
||||
: threadpool_(threadpool)
|
||||
{
|
||||
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->run();
|
||||
|
||||
// Delete the thread object as we exit.
|
||||
delete pwt;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This is the main loop of a worker thread. It picks up a new Task
|
||||
// and runs it.
|
||||
|
||||
void
|
||||
Workqueue_thread::run()
|
||||
{
|
||||
Workqueue_runner_threadpool* threadpool = this->threadpool_;
|
||||
Workqueue* workqueue = threadpool->get_workqueue();
|
||||
|
||||
while (true)
|
||||
{
|
||||
Task* t;
|
||||
Task_locker* tl;
|
||||
if (!threadpool->get_next(&t, &tl))
|
||||
return;
|
||||
|
||||
gold_debug(DEBUG_TASK, "running task %s", t->name().c_str());
|
||||
|
||||
t->run(workqueue);
|
||||
threadpool->thread_completed(t, tl);
|
||||
}
|
||||
}
|
||||
|
||||
// Class Workqueue_runner_threadpool.
|
||||
|
||||
// Constructor.
|
||||
|
||||
Workqueue_runner_threadpool::Workqueue_runner_threadpool(Workqueue* workqueue)
|
||||
: Workqueue_runner(workqueue),
|
||||
desired_thread_count_(0),
|
||||
lock_(),
|
||||
actual_thread_count_(0),
|
||||
running_thread_count_(0),
|
||||
task_queue_(),
|
||||
task_queue_condvar_(this->lock_)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
|
||||
Workqueue_runner_threadpool::~Workqueue_runner_threadpool()
|
||||
{
|
||||
// Tell the threads to exit.
|
||||
Hold_lock hl(this->lock_);
|
||||
this->desired_thread_count_ = 0;
|
||||
this->task_queue_condvar_.broadcast();
|
||||
}
|
||||
|
||||
// Run a task. This doesn't actually run the task: it pushes on the
|
||||
// queue of tasks to run. This is always called in the main thread.
|
||||
|
||||
void
|
||||
Workqueue_runner_threadpool::run(Task* t, Task_locker* tl)
|
||||
{
|
||||
Hold_lock hl(this->lock_);
|
||||
|
||||
// This is where we create threads as needed, subject to the limit
|
||||
// of the desired thread count.
|
||||
gold_assert(this->desired_thread_count_ > 0);
|
||||
gold_assert(this->actual_thread_count_ >= this->running_thread_count_);
|
||||
if (this->actual_thread_count_ == this->running_thread_count_
|
||||
&& this->actual_thread_count_ < this->desired_thread_count_)
|
||||
{
|
||||
// Note that threads delete themselves when they exit, so we
|
||||
// don't keep pointers to them.
|
||||
new Workqueue_thread(this);
|
||||
++this->actual_thread_count_;
|
||||
}
|
||||
|
||||
this->task_queue_.push(std::make_pair(t, tl));
|
||||
this->task_queue_condvar_.signal();
|
||||
}
|
||||
|
||||
// Set the thread count. This is only called in the main thread, and
|
||||
// is only called when there are no threads running.
|
||||
|
||||
void
|
||||
Workqueue_runner_threadpool::set_thread_count(int thread_count)
|
||||
{
|
||||
gold_assert(this->running_thread_count_ <= 1);
|
||||
gold_assert(thread_count > 0);
|
||||
this->desired_thread_count_ = thread_count;
|
||||
}
|
||||
|
||||
// Get the next task to run. This is always called by an instance of
|
||||
// Workqueue_thread, and is never called in the main thread. It
|
||||
// returns false if the calling thread should exit.
|
||||
|
||||
bool
|
||||
Workqueue_runner_threadpool::get_next(Task** pt, Task_locker** ptl)
|
||||
{
|
||||
Hold_lock hl(this->lock_);
|
||||
|
||||
// This is where we destroy threads, by telling them to exit.
|
||||
gold_assert(this->actual_thread_count_ > this->running_thread_count_);
|
||||
if (this->actual_thread_count_ > this->desired_thread_count_)
|
||||
{
|
||||
--this->actual_thread_count_;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (this->task_queue_.empty() && this->desired_thread_count_ > 0)
|
||||
{
|
||||
// Wait for a new task to become available.
|
||||
this->task_queue_condvar_.wait();
|
||||
}
|
||||
|
||||
// Check whether we are exiting.
|
||||
if (this->desired_thread_count_ == 0)
|
||||
{
|
||||
gold_assert(this->actual_thread_count_ > 0);
|
||||
--this->actual_thread_count_;
|
||||
return false;
|
||||
}
|
||||
|
||||
*pt = this->task_queue_.front().first;
|
||||
*ptl = this->task_queue_.front().second;
|
||||
this->task_queue_.pop();
|
||||
|
||||
++this->running_thread_count_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is called when a thread completes its task.
|
||||
|
||||
void
|
||||
Workqueue_runner_threadpool::thread_completed(Task* t, Task_locker* tl)
|
||||
{
|
||||
{
|
||||
Hold_lock hl(this->lock_);
|
||||
gold_assert(this->actual_thread_count_ > 0);
|
||||
gold_assert(this->running_thread_count_ > 0);
|
||||
--this->running_thread_count_;
|
||||
}
|
||||
|
||||
this->completed(t, tl);
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
#endif // defined(ENABLE_THREADS)
|
|
@ -22,11 +22,9 @@
|
|||
|
||||
#include "gold.h"
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "workqueue.h"
|
||||
#include "workqueue-internal.h"
|
||||
|
||||
namespace gold
|
||||
{
|
||||
|
@ -145,38 +143,6 @@ Task_block_token::~Task_block_token()
|
|||
}
|
||||
}
|
||||
|
||||
// The Workqueue_runner abstract class.
|
||||
|
||||
class Workqueue_runner
|
||||
{
|
||||
public:
|
||||
Workqueue_runner(Workqueue* workqueue)
|
||||
: workqueue_(workqueue)
|
||||
{ }
|
||||
virtual ~Workqueue_runner()
|
||||
{ }
|
||||
|
||||
// Run a task. This is always called in the main thread.
|
||||
virtual void
|
||||
run(Task*, Task_locker*) = 0;
|
||||
|
||||
// Set the number of threads to use. This is ignored when not using
|
||||
// threads.
|
||||
virtual void
|
||||
set_thread_count(int) = 0;
|
||||
|
||||
protected:
|
||||
// This is called by an implementation when a task is completed.
|
||||
void completed(Task* t, Task_locker* tl)
|
||||
{ this->workqueue_->completed(t, tl); }
|
||||
|
||||
Workqueue* get_workqueue() const
|
||||
{ return this->workqueue_; }
|
||||
|
||||
private:
|
||||
Workqueue* workqueue_;
|
||||
};
|
||||
|
||||
// The simple single-threaded implementation of Workqueue_runner.
|
||||
|
||||
class Workqueue_runner_single : public Workqueue_runner
|
||||
|
@ -212,12 +178,15 @@ Workqueue_runner_single::set_thread_count(int thread_count)
|
|||
|
||||
Workqueue::Workqueue(const General_options& options)
|
||||
: tasks_lock_(),
|
||||
first_tasks_(),
|
||||
tasks_(),
|
||||
completed_lock_(),
|
||||
completed_(),
|
||||
running_(0),
|
||||
queued_(0),
|
||||
completed_condvar_(this->completed_lock_),
|
||||
cleared_blockers_(0)
|
||||
cleared_blockers_(0),
|
||||
desired_thread_count_(1)
|
||||
{
|
||||
bool threads = options.threads();
|
||||
#ifndef ENABLE_THREADS
|
||||
|
@ -226,11 +195,18 @@ Workqueue::Workqueue(const General_options& options)
|
|||
if (!threads)
|
||||
this->runner_ = new Workqueue_runner_single(this);
|
||||
else
|
||||
gold_unreachable();
|
||||
{
|
||||
#ifdef ENABLE_THREADS
|
||||
this->runner_ = new Workqueue_runner_threadpool(this);
|
||||
#else
|
||||
gold_unreachable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Workqueue::~Workqueue()
|
||||
{
|
||||
gold_assert(this->first_tasks_.empty());
|
||||
gold_assert(this->tasks_.empty());
|
||||
gold_assert(this->completed_.empty());
|
||||
gold_assert(this->running_ == 0);
|
||||
|
@ -241,8 +217,14 @@ Workqueue::~Workqueue()
|
|||
void
|
||||
Workqueue::queue(Task* t)
|
||||
{
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
this->tasks_.push_back(t);
|
||||
{
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
this->tasks_.push_back(t);
|
||||
}
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
++this->queued_;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a task to the front of the queue.
|
||||
|
@ -250,8 +232,14 @@ Workqueue::queue(Task* t)
|
|||
void
|
||||
Workqueue::queue_front(Task* t)
|
||||
{
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
this->tasks_.push_front(t);
|
||||
{
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
this->first_tasks_.push_front(t);
|
||||
}
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
++this->queued_;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the list of completed tasks. Return whether we cleared
|
||||
|
@ -277,48 +265,36 @@ Workqueue::clear_completed()
|
|||
// a blocker.
|
||||
|
||||
Task*
|
||||
Workqueue::find_runnable(Task_list& tasks, bool* all_blocked)
|
||||
Workqueue::find_runnable(Task_list* tasks, bool* all_blocked)
|
||||
{
|
||||
Task* tlast = tasks.back();
|
||||
Task* tlast = tasks->back();
|
||||
*all_blocked = true;
|
||||
while (true)
|
||||
Task* t;
|
||||
do
|
||||
{
|
||||
Task* t = tasks.front();
|
||||
tasks.pop_front();
|
||||
t = tasks->front();
|
||||
tasks->pop_front();
|
||||
|
||||
Task::Is_runnable_type is_runnable = t->is_runnable(this);
|
||||
if (is_runnable == Task::IS_RUNNABLE)
|
||||
return t;
|
||||
{
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
--this->queued_;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
if (is_runnable != Task::IS_BLOCKED)
|
||||
*all_blocked = false;
|
||||
|
||||
tasks.push_back(t);
|
||||
|
||||
if (t == tlast)
|
||||
{
|
||||
// We couldn't find any runnable task. If there are any
|
||||
// completed tasks, free their locks and try again.
|
||||
|
||||
{
|
||||
Hold_lock hl2(this->completed_lock_);
|
||||
|
||||
if (!this->clear_completed())
|
||||
{
|
||||
// There had better be some tasks running, or we will
|
||||
// never find a runnable task.
|
||||
gold_assert(this->running_ > 0);
|
||||
|
||||
// We couldn't find any runnable tasks, and we
|
||||
// couldn't release any locks.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// We're going around again, so recompute ALL_BLOCKED.
|
||||
*all_blocked = true;
|
||||
}
|
||||
tasks->push_back(t);
|
||||
}
|
||||
while (t != tlast);
|
||||
|
||||
// We couldn't find any runnable task.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Process all the tasks on the workqueue. This is the main loop in
|
||||
|
@ -334,28 +310,56 @@ Workqueue::process()
|
|||
bool empty;
|
||||
bool all_blocked;
|
||||
|
||||
// Don't start more tasks than desired.
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
|
||||
this->clear_completed();
|
||||
while (this->running_ >= this->desired_thread_count_)
|
||||
{
|
||||
this->completed_condvar_.wait();
|
||||
this->clear_completed();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
|
||||
if (this->tasks_.empty())
|
||||
bool first_empty;
|
||||
bool all_blocked_first;
|
||||
if (this->first_tasks_.empty())
|
||||
{
|
||||
t = NULL;
|
||||
empty = true;
|
||||
all_blocked = false;
|
||||
first_empty = true;
|
||||
all_blocked_first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = this->find_runnable(this->tasks_, &all_blocked);
|
||||
t = this->find_runnable(&this->first_tasks_, &all_blocked_first);
|
||||
empty = false;
|
||||
first_empty = false;
|
||||
}
|
||||
|
||||
if (t == NULL)
|
||||
{
|
||||
if (this->tasks_.empty())
|
||||
all_blocked = false;
|
||||
else
|
||||
{
|
||||
t = this->find_runnable(&this->tasks_, &all_blocked);
|
||||
if (!first_empty && !all_blocked_first)
|
||||
all_blocked = false;
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If T != NULL, it is a task we can run.
|
||||
// If T == NULL && empty, then there are no tasks waiting to
|
||||
// be run at this level.
|
||||
// be run.
|
||||
// If T == NULL && !empty, then there tasks waiting to be
|
||||
// run at this level, but they are waiting for something to
|
||||
// unlock.
|
||||
// run, but they are waiting for something to unlock.
|
||||
|
||||
if (t != NULL)
|
||||
this->run(t);
|
||||
|
@ -371,10 +375,16 @@ Workqueue::process()
|
|||
if (all_blocked)
|
||||
{
|
||||
this->cleared_blockers_ = 0;
|
||||
int queued = this->queued_;
|
||||
this->clear_completed();
|
||||
while (this->cleared_blockers_ == 0)
|
||||
while (this->cleared_blockers_ == 0
|
||||
&& queued == this->queued_)
|
||||
{
|
||||
gold_assert(this->running_ > 0);
|
||||
if (this->running_ <= 0)
|
||||
{
|
||||
this->show_queued_tasks();
|
||||
gold_unreachable();
|
||||
}
|
||||
this->completed_condvar_.wait();
|
||||
this->clear_completed();
|
||||
}
|
||||
|
@ -416,7 +426,12 @@ Workqueue::process()
|
|||
void
|
||||
Workqueue::run(Task* t)
|
||||
{
|
||||
++this->running_;
|
||||
gold_debug(DEBUG_TASK, "starting task %s", t->name().c_str());
|
||||
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
++this->running_;
|
||||
}
|
||||
this->runner_->run(t, t->locks(this));
|
||||
}
|
||||
|
||||
|
@ -427,6 +442,8 @@ Workqueue::run(Task* t)
|
|||
void
|
||||
Workqueue::completed(Task* t, Task_locker* tl)
|
||||
{
|
||||
gold_debug(DEBUG_TASK, "completed task %s", t->name().c_str());
|
||||
|
||||
{
|
||||
Hold_lock hl(this->completed_lock_);
|
||||
gold_assert(this->running_ > 0);
|
||||
|
@ -434,6 +451,7 @@ Workqueue::completed(Task* t, Task_locker* tl)
|
|||
this->completed_.push_back(tl);
|
||||
this->completed_condvar_.signal();
|
||||
}
|
||||
|
||||
delete t;
|
||||
}
|
||||
|
||||
|
@ -452,7 +470,40 @@ Workqueue::cleared_blocker()
|
|||
void
|
||||
Workqueue::set_thread_count(int threads)
|
||||
{
|
||||
gold_assert(threads > 0);
|
||||
this->desired_thread_count_ = threads;
|
||||
this->runner_->set_thread_count(threads);
|
||||
}
|
||||
|
||||
// Dump the list of queued tasks and their current state, for
|
||||
// debugging purposes.
|
||||
|
||||
void
|
||||
Workqueue::show_queued_tasks()
|
||||
{
|
||||
fprintf(stderr, _("gold task queue:\n"));
|
||||
Hold_lock hl(this->tasks_lock_);
|
||||
for (Task_list::const_iterator p = this->tasks_.begin();
|
||||
p != this->tasks_.end();
|
||||
++p)
|
||||
{
|
||||
fprintf(stderr, " %s ", (*p)->name().c_str());
|
||||
switch ((*p)->is_runnable(this))
|
||||
{
|
||||
case Task::IS_RUNNABLE:
|
||||
fprintf(stderr, "runnable");
|
||||
break;
|
||||
case Task::IS_BLOCKED:
|
||||
fprintf(stderr, "blocked");
|
||||
break;
|
||||
case Task::IS_LOCKED:
|
||||
fprintf(stderr, "locked");
|
||||
break;
|
||||
default:
|
||||
gold_unreachable();
|
||||
}
|
||||
putc('\n', stderr);
|
||||
}
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
|
|
@ -275,6 +275,7 @@ class Task
|
|||
{
|
||||
public:
|
||||
Task()
|
||||
: name_()
|
||||
{ }
|
||||
virtual ~Task()
|
||||
{ }
|
||||
|
@ -307,9 +308,29 @@ class Task
|
|||
virtual void
|
||||
run(Workqueue*) = 0;
|
||||
|
||||
// Return the name of the Task. This is only used for debugging
|
||||
// purposes.
|
||||
const std::string&
|
||||
name()
|
||||
{
|
||||
if (this->name_.empty())
|
||||
this->name_ = this->get_name();
|
||||
return this->name_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Get the name of the task. This must be implemented by the child
|
||||
// class.
|
||||
virtual std::string
|
||||
get_name() const = 0;
|
||||
|
||||
private:
|
||||
// This task may not be copied.
|
||||
Task(const Task&);
|
||||
Task& operator=(const Task&);
|
||||
|
||||
// Task name, for debugging purposes.
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
// A simple task which waits for a blocker and then runs a function.
|
||||
|
@ -329,8 +350,9 @@ class Task_function : public Task
|
|||
public:
|
||||
// Both points should be allocated using new, and will be deleted
|
||||
// after the task runs.
|
||||
Task_function(Task_function_runner* runner, Task_token* blocker)
|
||||
: runner_(runner), blocker_(blocker)
|
||||
Task_function(Task_function_runner* runner, Task_token* blocker,
|
||||
const char* name)
|
||||
: runner_(runner), blocker_(blocker), name_(name)
|
||||
{ }
|
||||
|
||||
~Task_function()
|
||||
|
@ -356,12 +378,18 @@ class Task_function : public Task
|
|||
run(Workqueue* workqueue)
|
||||
{ this->runner_->run(workqueue); }
|
||||
|
||||
// The debugging name.
|
||||
std::string
|
||||
get_name() const
|
||||
{ return this->name_; }
|
||||
|
||||
private:
|
||||
Task_function(const Task_function&);
|
||||
Task_function& operator=(const Task_function&);
|
||||
|
||||
Task_function_runner* runner_;
|
||||
Task_token* blocker_;
|
||||
const char* name_;
|
||||
};
|
||||
|
||||
// The workqueue
|
||||
|
@ -403,39 +431,56 @@ class Workqueue
|
|||
typedef std::list<Task*> Task_list;
|
||||
|
||||
// Run a task.
|
||||
void run(Task*);
|
||||
void
|
||||
run(Task*);
|
||||
|
||||
friend class Workqueue_runner;
|
||||
|
||||
// Find a runnable task.
|
||||
Task* find_runnable(Task_list&, bool*);
|
||||
Task*
|
||||
find_runnable(Task_list*, bool*);
|
||||
|
||||
// Add a lock to the completed queue.
|
||||
void completed(Task*, Task_locker*);
|
||||
void
|
||||
completed(Task*, Task_locker*);
|
||||
|
||||
// Clear the completed queue.
|
||||
bool clear_completed();
|
||||
bool
|
||||
clear_completed();
|
||||
|
||||
// Print the list of queued tasks.
|
||||
void
|
||||
show_queued_tasks();
|
||||
|
||||
// How to run a task. Only accessed from main thread.
|
||||
Workqueue_runner* runner_;
|
||||
|
||||
// Lock for access to tasks_ members.
|
||||
Lock tasks_lock_;
|
||||
// List of tasks to execute at each link level.
|
||||
// List of tasks to execute soon.
|
||||
Task_list first_tasks_;
|
||||
// List of tasks to execute after the ones in first_tasks_.
|
||||
Task_list tasks_;
|
||||
|
||||
// Lock for access to completed_ and running_ members.
|
||||
// Lock for access to completed_, running_, and queued_.
|
||||
Lock completed_lock_;
|
||||
// List of Task_locker objects for main thread to free.
|
||||
std::list<Task_locker*> completed_;
|
||||
// Number of tasks currently running.
|
||||
int running_;
|
||||
// Number of tasks currently on queue (both first_tasks_ and
|
||||
// tasks_).
|
||||
int queued_;
|
||||
// Condition variable signalled when a new entry is added to completed_.
|
||||
Condvar completed_condvar_;
|
||||
|
||||
// Number of blocker tokens which were fully cleared. Only accessed
|
||||
// from main thread.
|
||||
int cleared_blockers_;
|
||||
|
||||
// The desired thread count. Only set by the main thread or by a
|
||||
// singleton thread. Only accessed from the main thread.
|
||||
int desired_thread_count_;
|
||||
};
|
||||
|
||||
} // End namespace gold.
|
||||
|
|
Loading…
Reference in New Issue