Rewrite workqueue. This version eliminates the master thread, and

reduces the amount of locking required to find a new thread to run.
This commit is contained in:
Ian Lance Taylor 2007-12-14 19:00:21 +00:00
parent 7004837e8d
commit 17a1d0a9b2
33 changed files with 1491 additions and 1433 deletions

View File

@ -87,6 +87,7 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
token.h \
workqueue.h \
workqueue-internal.h

View File

@ -344,6 +344,7 @@ HFILES = \
target-reloc.h \
target-select.h \
tls.h \
token.h \
workqueue.h \
workqueue-internal.h

View File

@ -73,12 +73,12 @@ const char Archive::arfmag[2] = { '`', '\n' };
// table.
void
Archive::setup()
Archive::setup(Task* task)
{
// We need to ignore empty archives.
if (this->input_file_->file().filesize() == sarmag)
{
this->input_file_->file().unlock();
this->input_file_->file().unlock(task);
return;
}
@ -109,7 +109,7 @@ Archive::setup()
}
// Opening the file locked it. Unlock it now.
this->input_file_->file().unlock();
this->input_file_->file().unlock(task);
}
// Read the archive symbol map.
@ -434,33 +434,19 @@ Add_archive_symbols::~Add_archive_symbols()
// Return whether we can add the archive symbols. We are blocked by
// this_blocker_. We block next_blocker_. We also lock the file.
Task::Is_runnable_type
Add_archive_symbols::is_runnable(Workqueue*)
Task_token*
Add_archive_symbols::is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
return this->this_blocker_;
return NULL;
}
class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker
void
Add_archive_symbols::locks(Task_locker* tl)
{
public:
Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue,
File_read& file)
: blocker_(token, workqueue), filelock_(file)
{ }
private:
Task_locker_block blocker_;
Task_locker_obj<File_read> filelock_;
};
Task_locker*
Add_archive_symbols::locks(Workqueue* workqueue)
{
return new Add_archive_symbols_locker(*this->next_blocker_,
workqueue,
this->archive_->file());
tl->add(this, this->next_blocker_);
tl->add(this, this->archive_->token());
}
void
@ -469,6 +455,8 @@ Add_archive_symbols::run(Workqueue*)
this->archive_->add_symbols(this->symtab_, this->layout_,
this->input_objects_);
this->archive_->release();
if (this->input_group_ != NULL)
this->input_group_->add_archive(this->archive_);
else

View File

@ -31,6 +31,7 @@
namespace gold
{
class Task;
class Input_file;
class Input_objects;
class Input_group;
@ -64,7 +65,7 @@ class Archive
// Set up the archive: read the symbol map.
void
setup();
setup(Task*);
// Get a reference to the underlying file.
File_read&
@ -73,19 +74,29 @@ class Archive
// Lock the underlying file.
void
lock()
{ this->input_file_->file().lock(); }
lock(const Task* t)
{ this->input_file_->file().lock(t); }
// Unlock the underlying file.
void
unlock()
{ this->input_file_->file().unlock(); }
unlock(const Task* t)
{ this->input_file_->file().unlock(t); }
// Return whether the underlying file is locked.
bool
is_locked() const
{ return this->input_file_->file().is_locked(); }
// Return the token, so that the task can be queued.
Task_token*
token()
{ return this->input_file_->file().token(); }
// Release the underlying file.
void
release()
{ this->input_file_->file().release(); }
// Select members from the archive as needed and add them to the
// link.
void
@ -178,11 +189,11 @@ class Add_archive_symbols : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -196,8 +207,6 @@ class Add_archive_symbols : public Task
}
private:
class Add_archive_symbols_locker;
Symbol_table* symtab_;
Layout* layout_;
Input_objects* input_objects_;

View File

@ -38,35 +38,21 @@ namespace gold
// This task allocates the common symbols. We need a lock on the
// symbol table.
Task::Is_runnable_type
Allocate_commons_task::is_runnable(Workqueue*)
Task_token*
Allocate_commons_task::is_runnable()
{
if (!this->symtab_lock_->is_writable())
return IS_LOCKED;
return IS_RUNNABLE;
return this->symtab_lock_;
return NULL;
}
// Return the locks we hold: one on the symbol table, and one blocker.
class Allocate_commons_task::Allocate_commons_locker : public Task_locker
void
Allocate_commons_task::locks(Task_locker* tl)
{
public:
Allocate_commons_locker(Task_token& symtab_lock, Task* task,
Task_token& blocker, Workqueue* workqueue)
: symtab_locker_(symtab_lock, task),
blocker_(blocker, workqueue)
{ }
private:
Task_locker_write symtab_locker_;
Task_locker_block blocker_;
};
Task_locker*
Allocate_commons_task::locks(Workqueue* workqueue)
{
return new Allocate_commons_locker(*this->symtab_lock_, this,
*this->blocker_, workqueue);
tl->add(this, this->blocker_);
tl->add(this, this->symtab_lock_);
}
// Allocate the common symbols.

View File

@ -45,11 +45,11 @@ class Allocate_commons_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -59,8 +59,6 @@ class Allocate_commons_task : public Task
{ return "Allocate_commons_task"; }
private:
class Allocate_commons_locker;
const General_options& options_;
Symbol_table* symtab_;
Layout* layout_;

View File

@ -28,6 +28,8 @@
#include <dirent.h>
#include "gold-threads.h"
#include "options.h"
#include "workqueue.h"
#include "dirsearch.h"
namespace
@ -169,11 +171,11 @@ class Dir_cache_task : public gold::Task
: dir_(dir), token_(token)
{ }
Is_runnable_type
is_runnable(gold::Workqueue*);
gold::Task_token*
is_runnable();
gold::Task_locker*
locks(gold::Workqueue*);
void
locks(gold::Task_locker*);
void
run(gold::Workqueue*);
@ -189,19 +191,19 @@ class Dir_cache_task : public gold::Task
// We can always run the task to read the directory.
gold::Task::Is_runnable_type
Dir_cache_task::is_runnable(gold::Workqueue*)
gold::Task_token*
Dir_cache_task::is_runnable()
{
return IS_RUNNABLE;
return NULL;
}
// Return the locks to hold. We use a blocker lock to prevent file
// lookups from starting until the directory contents have been read.
gold::Task_locker*
Dir_cache_task::locks(gold::Workqueue* workqueue)
void
Dir_cache_task::locks(gold::Task_locker* tl)
{
return new gold::Task_locker_block(this->token_, workqueue);
tl->add(this, &this->token_);
}
// Run the task--read the directory contents.

View File

@ -26,12 +26,14 @@
#include <string>
#include <list>
#include "workqueue.h"
#include "options.h"
#include "token.h"
namespace gold
{
class General_options;
class Workqueue;
// A simple interface to manage directories to be searched for
// libraries.
@ -40,7 +42,7 @@ class Dirsearch
{
public:
Dirsearch()
: directories_(NULL), token_()
: directories_(NULL), token_(true)
{ }
// Set the list of directories to search.
@ -55,10 +57,10 @@ class Dirsearch
std::string
find(const std::string&, const std::string& n2, bool *is_in_sysroot) const;
// Return a reference to the blocker token which controls access.
const Task_token&
token() const
{ return this->token_; }
// Return the blocker token which controls access.
Task_token*
token()
{ return &this->token_; }
private:
// We can not copy this class.

View File

@ -83,7 +83,7 @@ unsigned long long File_read::maximum_mapped_bytes;
File_read::~File_read()
{
gold_assert(this->lock_count_ == 0);
gold_assert(this->token_.is_writable());
if (this->descriptor_ >= 0)
{
if (close(this->descriptor_) < 0)
@ -98,9 +98,9 @@ File_read::~File_read()
// Open the file.
bool
File_read::open(const std::string& name)
File_read::open(const Task* task, const std::string& name)
{
gold_assert(this->lock_count_ == 0
gold_assert(this->token_.is_writable()
&& this->descriptor_ < 0
&& this->name_.empty());
this->name_ = name;
@ -116,7 +116,7 @@ File_read::open(const std::string& name)
this->size_ = s.st_size;
}
++this->lock_count_;
this->token_.add_writer(task);
return this->descriptor_ >= 0;
}
@ -124,46 +124,67 @@ File_read::open(const std::string& name)
// Open the file for testing purposes.
bool
File_read::open(const std::string& name, const unsigned char* contents,
off_t size)
File_read::open(const Task* task, const std::string& name,
const unsigned char* contents, off_t size)
{
gold_assert(this->lock_count_ == 0
gold_assert(this->token_.is_writable()
&& this->descriptor_ < 0
&& this->name_.empty());
this->name_ = name;
this->contents_ = contents;
this->size_ = size;
++this->lock_count_;
this->token_.add_writer(task);
return true;
}
void
File_read::lock()
{
++this->lock_count_;
}
// Release the file. This is called when we are done with the file in
// a Task.
void
File_read::unlock()
File_read::release()
{
gold_assert(this->lock_count_ > 0);
--this->lock_count_;
if (this->lock_count_ == 0)
{
File_read::total_mapped_bytes += this->mapped_bytes_;
File_read::current_mapped_bytes += this->mapped_bytes_;
this->mapped_bytes_ = 0;
if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes)
File_read::maximum_mapped_bytes = File_read::current_mapped_bytes;
gold_assert(this->is_locked());
this->clear_views(false);
}
File_read::total_mapped_bytes += this->mapped_bytes_;
File_read::current_mapped_bytes += this->mapped_bytes_;
this->mapped_bytes_ = 0;
if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes)
File_read::maximum_mapped_bytes = File_read::current_mapped_bytes;
this->clear_views(false);
this->released_ = true;
}
// Lock the file.
void
File_read::lock(const Task* task)
{
gold_assert(this->released_);
this->token_.add_writer(task);
this->released_ = false;
}
// Unlock the file.
void
File_read::unlock(const Task* task)
{
this->release();
this->token_.remove_writer(task);
}
// Return whether the file is locked.
bool
File_read::is_locked() const
{
return this->lock_count_ > 0;
if (!this->token_.is_writable())
return true;
// The file is not locked, so it should have been released.
gold_assert(this->released_);
return false;
}
// See if we have a view which covers the file starting at START for
@ -238,7 +259,8 @@ File_read::read(off_t start, off_t size, void* p) const
File_read::View*
File_read::find_or_make_view(off_t start, off_t size, bool cache)
{
gold_assert(this->lock_count_ > 0);
gold_assert(!this->token_.is_writable());
this->released_ = false;
off_t poff = File_read::page_offset(start);
@ -301,14 +323,11 @@ File_read::find_or_make_view(off_t start, off_t size, bool cache)
return v;
}
// This implementation of get_view just reads into a memory buffer,
// which we store on view_list_. At some point we should support
// mmap.
// Get a view into the file.
const unsigned char*
File_read::get_view(off_t start, off_t size, bool cache)
{
gold_assert(this->lock_count_ > 0);
File_read::View* pv = this->find_or_make_view(start, size, cache);
return pv->data() + (start - pv->start());
}
@ -316,7 +335,6 @@ File_read::get_view(off_t start, off_t size, bool cache)
File_view*
File_read::get_lasting_view(off_t start, off_t size, bool cache)
{
gold_assert(this->lock_count_ > 0);
File_read::View* pv = this->find_or_make_view(start, size, cache);
pv->lock();
return new File_view(*this, pv, pv->data() + (start - pv->start()));
@ -388,13 +406,13 @@ File_view::~File_view()
// Create a file for testing.
Input_file::Input_file(const char* name, const unsigned char* contents,
off_t size)
Input_file::Input_file(const Task* task, const char* name,
const unsigned char* contents, off_t size)
: file_()
{
this->input_argument_ =
new Input_file_argument(name, false, "", Position_dependent_options());
bool ok = file_.open(name, contents, size);
bool ok = file_.open(task, name, contents, size);
gold_assert(ok);
}
@ -408,7 +426,8 @@ Input_file::Input_file(const char* name, const unsigned char* contents,
// the file location, rather than the current directory.
bool
Input_file::open(const General_options& options, const Dirsearch& dirpath)
Input_file::open(const General_options& options, const Dirsearch& dirpath,
const Task* task)
{
std::string name;
@ -477,7 +496,7 @@ Input_file::open(const General_options& options, const Dirsearch& dirpath)
}
// Now that we've figured out where the file lives, try to open it.
if (!this->file_.open(name))
if (!this->file_.open(task, name))
{
gold_error(_("cannot open %s: %s"),
name.c_str(), strerror(errno));

View File

@ -30,6 +30,7 @@
#include <string>
#include "options.h"
#include "token.h"
namespace gold
{
@ -45,41 +46,57 @@ class File_read
{
public:
File_read()
: name_(), descriptor_(-1), size_(0), lock_count_(0), views_(),
saved_views_(), contents_(NULL), mapped_bytes_(0)
: name_(), descriptor_(-1), size_(0), token_(false), views_(),
saved_views_(), contents_(NULL), mapped_bytes_(0), released_(true)
{ }
~File_read();
// Open a file.
bool
open(const std::string& name);
open(const Task*, const std::string& name);
// Pretend to open the file, but provide the file contents. No
// actual file system activity will occur. This is used for
// testing.
bool
open(const std::string& name, const unsigned char* contents, off_t size);
open(const Task*, const std::string& name, const unsigned char* contents,
off_t size);
// Return the file name.
const std::string&
filename() const
{ return this->name_; }
// Lock the file for access within a particular Task::run execution.
// This means that the descriptor can not be closed. This routine
// may only be called from the main thread.
// Lock the file for exclusive access within a particular Task::run
// execution. This means that the descriptor can not be closed.
// This routine may only be called when the workqueue lock is held.
void
lock();
lock(const Task* t);
// Unlock the descriptor, permitting it to be closed if necessary.
void
unlock();
unlock(const Task* t);
// Test whether the object is locked.
bool
is_locked() const;
// Return the token, so that the task can be queued.
Task_token*
token()
{ return &this->token_; }
// Release the file. This indicates that we aren't going to do
// anything further with it until it is unlocked. This is used
// because a Task which locks the file never calls either lock or
// unlock; it just locks the token. The basic rule is that a Task
// which locks a file via the Task::locks interface must explicitly
// call release() when it is done. This is not necessary for code
// which calls unlock() on the file.
void
release();
// Return the size of the file.
off_t
filesize() const
@ -118,16 +135,16 @@ class File_read
File_read(const File_read&);
File_read& operator=(const File_read&);
// Total bytes mapped into memory during the link. This variable is
// only accessed from the main thread, when unlocking the object.
// Total bytes mapped into memory during the link. This variable
// may not be accurate when running multi-threaded.
static unsigned long long total_mapped_bytes;
// Current number of bytes mapped into memory during the link. This
// variable is only accessed from the main thread.
// variable may not be accurate when running multi-threaded.
static unsigned long long current_mapped_bytes;
// High water mark of bytes mapped into memory during the link.
// This variable is only accessed from the main thread.
// This variable may not be accurate when running multi-threaded.
static unsigned long long maximum_mapped_bytes;
// A view into the file.
@ -227,8 +244,8 @@ class File_read
int descriptor_;
// File size.
off_t size_;
// Number of locks on the file.
int lock_count_;
// A token used to lock the file.
Task_token token_;
// Buffered views into the file.
Views views_;
// List of views which were locked but had to be removed from views_
@ -240,6 +257,8 @@ class File_read
// while the file is locked. When we unlock the file, we transfer
// the total to total_mapped_bytes, and reset this to zero.
size_t mapped_bytes_;
// Whether the file was released.
bool released_;
};
// A view of file data that persists even when the file is unlocked.
@ -288,12 +307,13 @@ class Input_file
// Create an input file with the contents already provided. This is
// only used for testing. With this path, don't call the open
// method.
Input_file(const char* name, const unsigned char* contents, off_t size);
Input_file(const Task*, const char* name, const unsigned char* contents,
off_t size);
// Open the file. If the open fails, this will report an error and
// return false.
bool
open(const General_options&, const Dirsearch&);
open(const General_options&, const Dirsearch&, const Task*);
// Return the name given by the user. For -lc this will return "c".
const char*

View File

@ -89,7 +89,7 @@ class Middle_runner : public Task_function_runner
{ }
void
run(Workqueue*);
run(Workqueue*, const Task*);
private:
const General_options& options_;
@ -99,9 +99,9 @@ class Middle_runner : public Task_function_runner
};
void
Middle_runner::run(Workqueue* workqueue)
Middle_runner::run(Workqueue* workqueue, const Task* task)
{
queue_middle_tasks(this->options_, this->input_objects_, this->symtab_,
queue_middle_tasks(this->options_, task, this->input_objects_, this->symtab_,
this->layout_, workqueue);
}
@ -109,7 +109,7 @@ Middle_runner::run(Workqueue* workqueue)
void
queue_initial_tasks(const General_options& options,
const Dirsearch& search_path,
Dirsearch& search_path,
const Command_line& cmdline,
Workqueue* workqueue, Input_objects* input_objects,
Symbol_table* symtab, Layout* layout)
@ -131,10 +131,10 @@ queue_initial_tasks(const General_options& options,
p != cmdline.end();
++p)
{
Task_token* next_blocker = new Task_token();
Task_token* next_blocker = new Task_token(true);
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(options, input_objects, symtab, layout,
search_path, &*p, NULL, this_blocker,
&search_path, &*p, NULL, this_blocker,
next_blocker));
this_blocker = next_blocker;
}
@ -153,6 +153,7 @@ queue_initial_tasks(const General_options& options,
void
queue_middle_tasks(const General_options& options,
const Task* task,
const Input_objects* input_objects,
Symbol_table* symtab,
Layout* layout,
@ -187,7 +188,7 @@ queue_middle_tasks(const General_options& options,
// See if any of the input definitions violate the One Definition Rule.
// TODO: if this is too slow, do this as a task, rather than inline.
symtab->detect_odr_violations(options.output_file_name());
symtab->detect_odr_violations(task, options.output_file_name());
// Define some sections and symbols needed for a dynamic link. This
// handles some cases we want to see before we read the relocs.
@ -212,8 +213,8 @@ queue_middle_tasks(const General_options& options,
// Doing that is more complex, since we may later decide to discard
// some of the sections, and thus change our minds about the types
// of references made to the symbols.
Task_token* blocker = new Task_token();
Task_token* symtab_lock = new Task_token();
Task_token* blocker = new Task_token(true);
Task_token* symtab_lock = new Task_token(false);
for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
p != input_objects->relobj_end();
++p)
@ -260,30 +261,20 @@ queue_final_tasks(const General_options& options,
thread_count = input_objects->number_of_input_objects();
workqueue->set_thread_count(thread_count);
bool any_postprocessing_sections = layout->any_postprocessing_sections();
// Use a blocker to wait until all the input sections have been
// written out.
Task_token* input_sections_blocker = new Task_token();
Task_token* input_sections_blocker = NULL;
if (!any_postprocessing_sections)
input_sections_blocker = new Task_token(true);
// Use a blocker to block any objects which have to wait for the
// output sections to complete before they can apply relocations.
Task_token* output_sections_blocker = new Task_token();
Task_token* output_sections_blocker = new Task_token(true);
// Use a blocker to block the final cleanup task.
Task_token* final_blocker = new Task_token();
// Queue a task for each input object to relocate the sections and
// write out the local symbols.
for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
p != input_objects->relobj_end();
++p)
{
input_sections_blocker->add_blocker();
final_blocker->add_blocker();
workqueue->queue(new Relocate_task(options, symtab, layout, *p, of,
input_sections_blocker,
output_sections_blocker,
final_blocker));
}
Task_token* final_blocker = new Task_token(true);
// Queue a task to write out the symbol table.
if (!options.strip_all())
@ -307,12 +298,43 @@ queue_final_tasks(const General_options& options,
final_blocker->add_blocker();
workqueue->queue(new Write_data_task(layout, symtab, of, final_blocker));
// Queue a task for each input object to relocate the sections and
// write out the local symbols.
for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
p != input_objects->relobj_end();
++p)
{
if (input_sections_blocker != NULL)
input_sections_blocker->add_blocker();
final_blocker->add_blocker();
workqueue->queue(new Relocate_task(options, symtab, layout, *p, of,
input_sections_blocker,
output_sections_blocker,
final_blocker));
}
// Queue a task to write out the output sections which depend on
// input sections.
final_blocker->add_blocker();
workqueue->queue(new Write_after_input_sections_task(layout, of,
input_sections_blocker,
final_blocker));
// input sections. If there are any sections which require
// postprocessing, then we need to do this last, since it may resize
// the output file.
if (!any_postprocessing_sections)
{
final_blocker->add_blocker();
Task* t = new Write_after_input_sections_task(layout, of,
input_sections_blocker,
final_blocker);
workqueue->queue(t);
}
else
{
Task_token *new_final_blocker = new Task_token(true);
new_final_blocker->add_blocker();
Task* t = new Write_after_input_sections_task(layout, of,
final_blocker,
new_final_blocker);
workqueue->queue(t);
final_blocker = new_final_blocker;
}
// Queue a task to close the output file. This will be blocked by
// FINAL_BLOCKER.

View File

@ -174,6 +174,7 @@ class Input_objects;
class Symbol;
class Symbol_table;
class Layout;
class Task;
class Workqueue;
class Output_file;
template<int size, bool big_endian>
@ -252,7 +253,7 @@ get_version_string();
// Queue up the first set of tasks.
extern void
queue_initial_tasks(const General_options&,
const Dirsearch&,
Dirsearch&,
const Command_line&,
Workqueue*,
Input_objects*,
@ -262,6 +263,7 @@ queue_initial_tasks(const General_options&,
// Queue up the middle set of tasks.
extern void
queue_middle_tasks(const General_options&,
const Task*,
const Input_objects*,
Symbol_table*,
Layout*,

View File

@ -44,10 +44,11 @@ namespace gold
// have been read.
void
Layout_task_runner::run(Workqueue* workqueue)
Layout_task_runner::run(Workqueue* workqueue, const Task* task)
{
off_t file_size = this->layout_->finalize(this->input_objects_,
this->symtab_);
this->symtab_,
task);
// Now we know the final size of the output file and we know where
// each piece of information goes.
@ -72,7 +73,8 @@ Layout::Layout(const General_options& options)
input_requires_executable_stack_(false),
input_with_gnu_stack_note_(false),
input_without_gnu_stack_note_(false),
has_static_tls_(false)
has_static_tls_(false),
any_postprocessing_sections_(false)
{
// Make space for more than enough segments for a typical file.
// This is just for efficiency--it's OK if we wind up needing more.
@ -653,13 +655,14 @@ Layout::find_first_load_seg()
// This function returns the size of the output file.
off_t
Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
const Task* task)
{
Target* const target = input_objects->target();
target->finalize_sections(this);
this->count_local_symbols(input_objects);
this->count_local_symbols(task, input_objects);
this->create_gold_note();
this->create_executable_stack_info(target);
@ -730,7 +733,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
off_t off = this->set_segment_offsets(target, load_seg, &shndx);
// Create the symbol table sections.
this->create_symtab_sections(input_objects, symtab, &off);
this->create_symtab_sections(input_objects, symtab, task, &off);
if (!parameters->doing_static_link())
this->assign_local_dynsym_offsets(input_objects);
@ -747,6 +750,12 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
// Create the section table header.
this->create_shdrs(&off);
// If there are no sections which require postprocessing, we can
// handle the section names now, and avoid a resize later.
if (!this->any_postprocessing_sections_)
off = this->set_section_offsets(off,
STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS);
file_header->set_section_info(this->section_headers_, shstrtab_section);
// Now we know exactly where everything goes in the output file
@ -1106,16 +1115,19 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass)
if (pass == BEFORE_INPUT_SECTIONS_PASS
&& (*p)->requires_postprocessing())
(*p)->create_postprocessing_buffer();
{
(*p)->create_postprocessing_buffer();
this->any_postprocessing_sections_ = true;
}
if (pass == BEFORE_INPUT_SECTIONS_PASS
&& (*p)->after_input_sections())
continue;
else if (pass == AFTER_INPUT_SECTIONS_PASS
else if (pass == POSTPROCESSING_SECTIONS_PASS
&& (!(*p)->after_input_sections()
|| (*p)->type() == elfcpp::SHT_STRTAB))
continue;
else if (pass == STRTAB_AFTER_INPUT_SECTIONS_PASS
else if (pass == STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS
&& (!(*p)->after_input_sections()
|| (*p)->type() != elfcpp::SHT_STRTAB))
continue;
@ -1126,7 +1138,7 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass)
off += (*p)->data_size();
// At this point the name must be set.
if (pass != STRTAB_AFTER_INPUT_SECTIONS_PASS)
if (pass != STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS)
this->namepool_.add((*p)->name(), false, NULL);
}
return off;
@ -1152,7 +1164,8 @@ Layout::set_section_indexes(unsigned int shndx)
// symbol table, and build the respective string pools.
void
Layout::count_local_symbols(const Input_objects* input_objects)
Layout::count_local_symbols(const Task* task,
const Input_objects* input_objects)
{
// First, figure out an upper bound on the number of symbols we'll
// be inserting into each pool. This helps us create the pools with
@ -1177,7 +1190,7 @@ Layout::count_local_symbols(const Input_objects* input_objects)
p != input_objects->relobj_end();
++p)
{
Task_lock_obj<Object> tlo(**p);
Task_lock_obj<Object> tlo(task, *p);
(*p)->count_local_symbols(&this->sympool_, &this->dynpool_);
}
}
@ -1189,6 +1202,7 @@ Layout::count_local_symbols(const Input_objects* input_objects)
void
Layout::create_symtab_sections(const Input_objects* input_objects,
Symbol_table* symtab,
const Task* task,
off_t* poff)
{
int symsize;
@ -1262,7 +1276,7 @@ Layout::create_symtab_sections(const Input_objects* input_objects,
== this->dynsym_section_->data_size() - locsize);
}
off = symtab->finalize(local_symcount, off, dynoff, dyn_global_index,
off = symtab->finalize(task, local_symcount, off, dynoff, dyn_global_index,
dyncount, &this->sympool_);
if (!parameters->strip_all())
@ -2004,16 +2018,21 @@ Layout::write_sections_after_input_sections(Output_file* of)
// file size. Note we finalize the .shstrab last, to allow the
// after_input_section sections to modify their section-names before
// writing.
off_t off = this->output_file_size_;
off = this->set_section_offsets(off, AFTER_INPUT_SECTIONS_PASS);
// Now that we've finalized the names, we can finalize the shstrab.
off = this->set_section_offsets(off, STRTAB_AFTER_INPUT_SECTIONS_PASS);
if (off > this->output_file_size_)
if (this->any_postprocessing_sections_)
{
of->resize(off);
this->output_file_size_ = off;
off_t off = this->output_file_size_;
off = this->set_section_offsets(off, POSTPROCESSING_SECTIONS_PASS);
// Now that we've finalized the names, we can finalize the shstrab.
off =
this->set_section_offsets(off,
STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS);
if (off > this->output_file_size_)
{
of->resize(off);
this->output_file_size_ = off;
}
}
for (Section_list::const_iterator p = this->section_list_.begin();
@ -2049,36 +2068,20 @@ Layout::print_stats() const
// We can always run this task.
Task::Is_runnable_type
Write_sections_task::is_runnable(Workqueue*)
Task_token*
Write_sections_task::is_runnable()
{
return IS_RUNNABLE;
return NULL;
}
// We need to unlock both OUTPUT_SECTIONS_BLOCKER and FINAL_BLOCKER
// when finished.
class Write_sections_task::Write_sections_locker : public Task_locker
void
Write_sections_task::locks(Task_locker* tl)
{
public:
Write_sections_locker(Task_token& output_sections_blocker,
Task_token& final_blocker,
Workqueue* workqueue)
: output_sections_block_(output_sections_blocker, workqueue),
final_block_(final_blocker, workqueue)
{ }
private:
Task_block_token output_sections_block_;
Task_block_token final_block_;
};
Task_locker*
Write_sections_task::locks(Workqueue* workqueue)
{
return new Write_sections_locker(*this->output_sections_blocker_,
*this->final_blocker_,
workqueue);
tl->add(this, this->output_sections_blocker_);
tl->add(this, this->final_blocker_);
}
// Run the task--write out the data.
@ -2093,18 +2096,18 @@ Write_sections_task::run(Workqueue*)
// We can always run this task.
Task::Is_runnable_type
Write_data_task::is_runnable(Workqueue*)
Task_token*
Write_data_task::is_runnable()
{
return IS_RUNNABLE;
return NULL;
}
// We need to unlock FINAL_BLOCKER when finished.
Task_locker*
Write_data_task::locks(Workqueue* workqueue)
void
Write_data_task::locks(Task_locker* tl)
{
return new Task_locker_block(*this->final_blocker_, workqueue);
tl->add(this, this->final_blocker_);
}
// Run the task--write out the data.
@ -2119,18 +2122,18 @@ Write_data_task::run(Workqueue*)
// We can always run this task.
Task::Is_runnable_type
Write_symbols_task::is_runnable(Workqueue*)
Task_token*
Write_symbols_task::is_runnable()
{
return IS_RUNNABLE;
return NULL;
}
// We need to unlock FINAL_BLOCKER when finished.
Task_locker*
Write_symbols_task::locks(Workqueue* workqueue)
void
Write_symbols_task::locks(Task_locker* tl)
{
return new Task_locker_block(*this->final_blocker_, workqueue);
tl->add(this, this->final_blocker_);
}
// Run the task--write out the symbols.
@ -2146,20 +2149,20 @@ Write_symbols_task::run(Workqueue*)
// We can only run this task after the input sections have completed.
Task::Is_runnable_type
Write_after_input_sections_task::is_runnable(Workqueue*)
Task_token*
Write_after_input_sections_task::is_runnable()
{
if (this->input_sections_blocker_->is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
return this->input_sections_blocker_;
return NULL;
}
// We need to unlock FINAL_BLOCKER when finished.
Task_locker*
Write_after_input_sections_task::locks(Workqueue* workqueue)
void
Write_after_input_sections_task::locks(Task_locker* tl)
{
return new Task_locker_block(*this->final_blocker_, workqueue);
tl->add(this, this->final_blocker_);
}
// Run the task.
@ -2175,7 +2178,7 @@ Write_after_input_sections_task::run(Workqueue*)
// Run the task--close the file.
void
Close_task_runner::run(Workqueue*)
Close_task_runner::run(Workqueue*, const Task*)
{
this->of_->close();
}

View File

@ -67,7 +67,7 @@ class Layout_task_runner : public Task_function_runner
// Run the operation.
void
run(Workqueue*);
run(Workqueue*, const Task*);
private:
Layout_task_runner(const Layout_task_runner&);
@ -168,7 +168,12 @@ class Layout
// Finalize the layout after all the input sections have been added.
off_t
finalize(const Input_objects*, Symbol_table*);
finalize(const Input_objects*, Symbol_table*, const Task*);
// Return whether any sections require postprocessing.
bool
any_postprocessing_sections() const
{ return this->any_postprocessing_sections_; }
// Return the size of the output file.
off_t
@ -283,11 +288,12 @@ class Layout
// Count the local symbols in the regular symbol table and the dynamic
// symbol table, and build the respective string pools.
void
count_local_symbols(const Input_objects*);
count_local_symbols(const Task*, const Input_objects*);
// Create the output sections for the symbol table.
void
create_symtab_sections(const Input_objects*, Symbol_table*, off_t*);
create_symtab_sections(const Input_objects*, Symbol_table*, const Task*,
off_t*);
// Create the .shstrtab section.
Output_section*
@ -368,14 +374,14 @@ class Layout
// Set the final file offsets of all the sections not associated
// with a segment. We set section offsets in three passes: the
// first handles all allocated sections, the second sections that
// can be handled after input-sections are processed, and the last
// the late-bound STRTAB sections (probably only shstrtab, which is
// the one we care about because it holds section names).
// require postprocessing, and the last the late-bound STRTAB
// sections (probably only shstrtab, which is the one we care about
// because it holds section names).
enum Section_offset_pass
{
BEFORE_INPUT_SECTIONS_PASS,
AFTER_INPUT_SECTIONS_PASS,
STRTAB_AFTER_INPUT_SECTIONS_PASS
POSTPROCESSING_SECTIONS_PASS,
STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS
};
off_t
set_section_offsets(off_t, Section_offset_pass pass);
@ -472,6 +478,8 @@ class Layout
bool input_without_gnu_stack_note_;
// Whether we have seen an object file that uses the static TLS model.
bool has_static_tls_;
// Whether any sections require postprocessing.
bool any_postprocessing_sections_;
};
// This task handles writing out data in output sections which is not
@ -492,11 +500,11 @@ class Write_sections_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -527,11 +535,11 @@ class Write_data_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -562,11 +570,11 @@ class Write_symbols_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -602,11 +610,11 @@ class Write_after_input_sections_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -616,8 +624,6 @@ class Write_after_input_sections_task : public Task
{ return "Write_after_input_sections_task"; }
private:
class Write_sections_locker;
Layout* layout_;
Output_file* of_;
Task_token* input_sections_blocker_;
@ -635,7 +641,7 @@ class Close_task_runner : public Task_function_runner
// Run the operation.
void
run(Workqueue*);
run(Workqueue*, const Task*);
private:
Output_file* of_;

View File

@ -94,7 +94,7 @@ main(int argc, char** argv)
&symtab, &layout);
// Run the main task processing loop.
workqueue.process();
workqueue.process(0);
if (command_line.options().print_stats())
{

View File

@ -726,8 +726,8 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
}
// Finalize the local symbols. Here we add their names to *POOL and
// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_.
// This function is always called from the main thread. The actual
// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_. This
// function is always called from a singleton thread. The actual
// output of the local symbols will occur in a separate task.
template<int size, bool big_endian>
@ -831,7 +831,7 @@ Sized_relobj<size, big_endian>::do_count_local_symbols(Stringpool* pool,
// Finalize the local symbols. Here we add their values to
// THIS->LOCAL_VALUES_ and set their output symbol table indexes.
// This function is always called from the main thread. The actual
// This function is always called from a singleton thread. The actual
// output of the local symbols will occur in a separate task.
template<int size, bool big_endian>
@ -987,9 +987,10 @@ Sized_relobj<size, big_endian>::local_value(unsigned int shndx,
template<int size, bool big_endian>
void
Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
const Stringpool* sympool,
const Stringpool* dynpool)
Sized_relobj<size, big_endian>::write_local_symbols(
Output_file* of,
const Stringpool* sympool,
const Stringpool* dynpool)
{
if (parameters->strip_all() && this->output_local_dynsym_count_ == 0)
return;

View File

@ -35,6 +35,7 @@ namespace gold
{
class General_options;
class Task;
class Layout;
class Output_section;
class Output_file;
@ -165,19 +166,29 @@ class Object
// Lock the underlying file.
void
lock()
{ this->input_file()->file().lock(); }
lock(const Task* t)
{ this->input_file()->file().lock(t); }
// Unlock the underlying file.
void
unlock()
{ this->input_file()->file().unlock(); }
unlock(const Task* t)
{ this->input_file()->file().unlock(t); }
// Return whether the underlying file is locked.
bool
is_locked() const
{ return this->input_file()->file().is_locked(); }
// Return the token, so that the task can be queued.
Task_token*
token()
{ return this->input_file()->file().token(); }
// Release the underlying file.
void
release()
{ this->input_file_->file().release(); }
// Return the sized target structure associated with this object.
// This is like the target method but it returns a pointer of
// appropriate checked type.
@ -322,7 +333,7 @@ class Object
virtual unsigned int
do_section_info(unsigned int shndx) = 0;
// Get the file.
// Get the file. We pass on const-ness.
Input_file*
input_file()
{ return this->input_file_; }
@ -508,7 +519,7 @@ class Relobj : public Object
// any relocations for sections which require special handling, such
// as the exception frame section.
bool
relocs_must_follow_section_writes()
relocs_must_follow_section_writes() const
{ return this->relocs_must_follow_section_writes_; }
// Return the object merge map.
@ -1101,7 +1112,7 @@ class Sized_relobj : public Relobj
// Write section data to the output file. Record the views and
// sizes in VIEWS for use when relocating.
void
write_sections(const unsigned char* pshdrs, Output_file*, Views*);
write_sections(const unsigned char* pshdrs, Output_file*, Views*) const;
// Relocate the sections in the output file.
void

View File

@ -53,6 +53,7 @@ target-reloc.h
target-select.cc
target-select.h
tls.h
token.h
version.cc
workqueue.cc
workqueue.h

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-11-29 16:33-0800\n"
"POT-Creation-Date: 2007-12-14 09:45-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -61,11 +61,11 @@ msgstr ""
msgid "%s: member at %zu is not an ELF object"
msgstr ""
#: compressed_output.cc:140
msgid "Not compressing section data: zlib error"
#: compressed_output.cc:126
msgid "not compressing section data: zlib error"
msgstr ""
#: dirsearch.cc:68
#: dirsearch.cc:70
#, c-format
msgid "%s: can not read directory: %s"
msgstr ""
@ -114,7 +114,7 @@ msgstr ""
msgid "dynamic symbol table name section has wrong type: %u"
msgstr ""
#: dynobj.cc:402 object.cc:236 object.cc:571
#: dynobj.cc:402 object.cc:238 object.cc:574
#, c-format
msgid "bad section name offset for section %u: %lu"
msgstr ""
@ -218,42 +218,42 @@ msgstr ""
msgid "%s: fstat failed: %s"
msgstr ""
#: fileread.cc:210
#: fileread.cc:231
#, c-format
msgid "%s: pread failed: %s"
msgstr ""
#: fileread.cc:216
#: fileread.cc:237
#, c-format
msgid "%s: file too short: read only %lld of %lld bytes at %lld"
msgstr ""
#: fileread.cc:292
#: fileread.cc:312
#, c-format
msgid "%s: mmap offset %lld size %lld failed: %s"
msgstr ""
#: fileread.cc:371
#: fileread.cc:393
#, c-format
msgid "%s: total bytes mapped for read: %llu\n"
msgstr ""
#: fileread.cc:373
#: fileread.cc:395
#, c-format
msgid "%s: maximum bytes mapped for read at one time: %llu\n"
msgstr ""
#: fileread.cc:442
#: fileread.cc:465
#, c-format
msgid "cannot find -l%s"
msgstr ""
#: fileread.cc:469
#: fileread.cc:492
#, c-format
msgid "cannot find %s"
msgstr ""
#: fileread.cc:480
#: fileread.cc:503
#, c-format
msgid "cannot open %s: %s"
msgstr ""
@ -265,12 +265,12 @@ msgstr ""
#. We had some input files, but we weren't able to open any of
#. them.
#: gold.cc:118 gold.cc:165
#: gold.cc:118 gold.cc:166
msgid "no input files"
msgstr ""
#. We print out just the first .so we see; there may be others.
#: gold.cc:180
#: gold.cc:181
#, c-format
msgid "cannot mix -static with dynamic object %s"
msgstr ""
@ -336,50 +336,50 @@ msgid "pthread_cond_broadcast failed: %s"
msgstr ""
#. FIXME: This needs to specify the location somehow.
#: i386.cc:160 i386.cc:1439 x86_64.cc:172 x86_64.cc:1269
#: i386.cc:160 i386.cc:1480 x86_64.cc:172 x86_64.cc:1370
msgid "missing expected TLS relocation"
msgstr ""
#: i386.cc:779 x86_64.cc:732 x86_64.cc:910
#: i386.cc:806 x86_64.cc:761 x86_64.cc:975
#, c-format
msgid "%s: unsupported reloc %u against local symbol"
msgstr ""
#: i386.cc:882 i386.cc:1169 x86_64.cc:851 x86_64.cc:1093
#: i386.cc:913 i386.cc:1209 x86_64.cc:886 x86_64.cc:1157
#, c-format
msgid "%s: unexpected reloc %u in object file"
msgstr ""
#: i386.cc:1018 x86_64.cc:924 x86_64.cc:1152
#: i386.cc:1052 x86_64.cc:989 x86_64.cc:1253
#, c-format
msgid "%s: unsupported reloc %u against global symbol %s"
msgstr ""
#: i386.cc:1322
#: i386.cc:1363
#, c-format
msgid "%s: unsupported RELA reloc section"
msgstr ""
#: i386.cc:1579 x86_64.cc:1467
#: i386.cc:1620 x86_64.cc:1569
#, c-format
msgid "unexpected reloc %u in object file"
msgstr ""
#: i386.cc:1611 i386.cc:1687 i386.cc:1694 i386.cc:1735 i386.cc:1791
#: x86_64.cc:1488 x86_64.cc:1537 x86_64.cc:1548
#: i386.cc:1652 i386.cc:1727 i386.cc:1734 i386.cc:1765 i386.cc:1818
#: x86_64.cc:1590 x86_64.cc:1670 x86_64.cc:1694
#, c-format
msgid "unsupported reloc %u"
msgstr ""
#: i386.cc:1702
#: i386.cc:1742
msgid "both SUN and GNU model TLS relocations"
msgstr ""
#: merge.cc:464
#: merge.cc:472
msgid "mergeable string section length not multiple of character size"
msgstr ""
#: merge.cc:480
#: merge.cc:488
msgid "entry in mergeable string section not null terminated"
msgstr ""
@ -388,7 +388,7 @@ msgstr ""
msgid "%s: unsupported ELF machine number %d"
msgstr ""
#: object.cc:71 script.cc:1226
#: object.cc:71 script.cc:1229
#, c-format
msgid "%s: %s"
msgstr ""
@ -398,117 +398,117 @@ msgstr ""
msgid "section name section has wrong type: %u"
msgstr ""
#: object.cc:308
#: object.cc:311
#, c-format
msgid "invalid symbol table name index: %u"
msgstr ""
#: object.cc:314
#: object.cc:317
#, c-format
msgid "symbol table name section has wrong type: %u"
msgstr ""
#: object.cc:394
#: object.cc:397
#, c-format
msgid "section group %u info %u out of range"
msgstr ""
#: object.cc:412
#: object.cc:415
#, c-format
msgid "symbol %u name offset %u out of range"
msgstr ""
#: object.cc:444
#: object.cc:447
#, c-format
msgid "section %u in section group %u out of range"
msgstr ""
#: object.cc:534 reloc.cc:229 reloc.cc:496
#: object.cc:537 reloc.cc:205 reloc.cc:520
#, c-format
msgid "relocation section %u has bad info %u"
msgstr ""
#: object.cc:706
#: object.cc:709
msgid "size of symbols is not multiple of symbol size"
msgstr ""
#. FIXME: Handle SHN_XINDEX.
#: object.cc:798
#, c-format
msgid "unknown section index %u for local symbol %u"
msgstr ""
#: object.cc:807
#, c-format
msgid "local symbol %u section index %u out of range"
msgstr ""
#: object.cc:839
#: object.cc:808
#, c-format
msgid "local symbol %u section name out of range: %u >= %u"
msgstr ""
#: object.cc:1070
#. FIXME: Handle SHN_XINDEX.
#: object.cc:865
#, c-format
msgid "unknown section index %u for local symbol %u"
msgstr ""
#: object.cc:874
#, c-format
msgid "local symbol %u section index %u out of range"
msgstr ""
#: object.cc:1194
#, c-format
msgid "%s: incompatible target"
msgstr ""
#: object.cc:1226
#: object.cc:1349
#, c-format
msgid "%s: unsupported ELF file type %d"
msgstr ""
#: object.cc:1245 object.cc:1291 object.cc:1325
#: object.cc:1368 object.cc:1414 object.cc:1448
#, c-format
msgid "%s: ELF file too short"
msgstr ""
#: object.cc:1253
#: object.cc:1376
#, c-format
msgid "%s: invalid ELF version 0"
msgstr ""
#: object.cc:1255
#: object.cc:1378
#, c-format
msgid "%s: unsupported ELF version %d"
msgstr ""
#: object.cc:1262
#: object.cc:1385
#, c-format
msgid "%s: invalid ELF class 0"
msgstr ""
#: object.cc:1268
#: object.cc:1391
#, c-format
msgid "%s: unsupported ELF class %d"
msgstr ""
#: object.cc:1275
#: object.cc:1398
#, c-format
msgid "%s: invalid ELF data encoding"
msgstr ""
#: object.cc:1281
#: object.cc:1404
#, c-format
msgid "%s: unsupported ELF data encoding %d"
msgstr ""
#: object.cc:1301
#: object.cc:1424
#, c-format
msgid "%s: not configured to support 32-bit big-endian object"
msgstr ""
#: object.cc:1314
#: object.cc:1437
#, c-format
msgid "%s: not configured to support 32-bit little-endian object"
msgstr ""
#: object.cc:1335
#: object.cc:1458
#, c-format
msgid "%s: not configured to support 64-bit big-endian object"
msgstr ""
#: object.cc:1348
#: object.cc:1471
#, c-format
msgid "%s: not configured to support 64-bit little-endian object"
msgstr ""
@ -832,88 +832,107 @@ msgstr ""
msgid "%s: -%c: %s\n"
msgstr ""
#: options.h:372
#: options.h:331
#, c-format
msgid "Unsupported argument to --compress-debug-symbols: %s"
msgid "invalid optimization level: %s"
msgstr ""
#: options.h:424
#: options.h:377
#, c-format
msgid "%s: invalid argument to -Ttext: %s\n"
msgid "unsupported argument to --compress-debug-sections: %s"
msgstr ""
#: options.h:428
#, c-format
msgid "invalid argument to -Ttext: %s"
msgstr ""
#: options.h:437
#, c-format
msgid "%s: invalid thread count: %s\n"
msgid "invalid thread count: %s"
msgstr ""
#: output.cc:1122
#: options.h:445
msgid "--threads not supported"
msgstr ""
#: output.cc:1467
#, c-format
msgid "invalid alignment %lu for section \"%s\""
msgstr ""
#: output.cc:1941
#: output.cc:2334
#, c-format
msgid "%s: open: %s"
msgstr ""
#: output.cc:1953 output.cc:1986
#: output.cc:2354
#, c-format
msgid "%s: munmap: %s"
msgid "%s: mremap: %s"
msgstr ""
#: output.cc:1967
#: output.cc:2390
#, c-format
msgid "%s: lseek: %s"
msgstr ""
#: output.cc:1970
#: output.cc:2393 output.cc:2430
#, c-format
msgid "%s: write: %s"
msgstr ""
#: output.cc:1976
#: output.cc:2401
#, c-format
msgid "%s: mmap: %s"
msgstr ""
#: output.cc:1990
#: output.cc:2411
#, c-format
msgid "%s: munmap: %s"
msgstr ""
#: output.cc:2428
#, c-format
msgid "%s: write: unexpected 0 return-value"
msgstr ""
#: output.cc:2440
#, c-format
msgid "%s: close: %s"
msgstr ""
#: readsyms.cc:151
#: readsyms.cc:150
#, c-format
msgid "%s: file is empty"
msgstr ""
#: readsyms.cc:186
#: readsyms.cc:185
#, c-format
msgid "%s: ordinary object found in input group"
msgstr ""
#. Here we have to handle any other input file types we need.
#: readsyms.cc:234
#: readsyms.cc:244
#, c-format
msgid "%s: not an object or archive"
msgstr ""
#: reloc.cc:248 reloc.cc:514
#: reloc.cc:224 reloc.cc:538
#, c-format
msgid "relocation section %u uses unexpected symbol table %u"
msgstr ""
#: reloc.cc:263 reloc.cc:532
#: reloc.cc:239 reloc.cc:556
#, c-format
msgid "unexpected entsize for reloc section %u: %lu != %u"
msgstr ""
#: reloc.cc:272 reloc.cc:541
#: reloc.cc:248 reloc.cc:565
#, c-format
msgid "reloc section %u size %lu uneven"
msgstr ""
#: reloc.cc:732
#: reloc.cc:759
#, c-format
msgid "reloc section size %zu is not a multiple of reloc size %d\n"
msgstr ""
@ -948,48 +967,73 @@ msgstr ""
#. There are some options that we could handle here--e.g.,
#. -lLIBRARY. Should we bother?
#: script.cc:1330
#: script.cc:1333
#, c-format
msgid ""
"%s: Ignoring command OPTION; OPTION is only valid for scripts specified via -"
"T"
msgstr ""
#: symtab.cc:597
#: stringpool.cc:535
#, c-format
msgid "%s: %s entries: %zu; buckets: %zu\n"
msgstr ""
#: stringpool.cc:539
#, c-format
msgid "%s: %s entries: %zu\n"
msgstr ""
#: stringpool.cc:542
#, c-format
msgid "%s: %s Stringdata structures: %zu\n"
msgstr ""
#: symtab.cc:595
#, c-format
msgid "bad global symbol name offset %u at %zu"
msgstr ""
#: symtab.cc:675
#: symtab.cc:673
msgid "too few symbol versions"
msgstr ""
#: symtab.cc:704
#: symtab.cc:702
#, c-format
msgid "bad symbol name offset %u at %zu"
msgstr ""
#: symtab.cc:758
#: symtab.cc:756
#, c-format
msgid "versym for symbol %zu out of range: %u"
msgstr ""
#: symtab.cc:766
#: symtab.cc:764
#, c-format
msgid "versym for symbol %zu has no name: %u"
msgstr ""
#: symtab.cc:1484 symtab.cc:1697
#: symtab.cc:1482 symtab.cc:1698
#, c-format
msgid "%s: unsupported symbol section 0x%x"
msgstr ""
#: symtab.cc:1821
#: symtab.cc:1822
#, c-format
msgid "%s: undefined reference to '%s'"
msgstr ""
#: symtab.cc:1962
#: symtab.cc:1907
#, c-format
msgid "%s: symbol table entries: %zu; buckets: %zu\n"
msgstr ""
#: symtab.cc:1910
#, c-format
msgid "%s: symbol table entries: %zu\n"
msgstr ""
#: symtab.cc:1979
#, c-format
msgid ""
"while linking %s: symbol '%s' defined in multiple places (possible ODR "
@ -1024,26 +1068,17 @@ msgid ""
"This program has absolutely no warranty.\n"
msgstr ""
#: workqueue.cc:484
#, c-format
msgid "gold task queue:\n"
msgstr ""
#: workqueue-threads.cc:107
#: workqueue-threads.cc:106
#, c-format
msgid "%s failed: %s"
msgstr ""
#: x86_64.cc:1177
#: x86_64.cc:1278
#, c-format
msgid "%s: unsupported REL reloc section"
msgstr ""
#: x86_64.cc:1513
msgid "TLS reloc but no TLS segment"
msgstr ""
#: x86_64.cc:1576
#: x86_64.cc:1742
#, c-format
msgid "unsupported reloc type %u"
msgstr ""

View File

@ -39,9 +39,9 @@ namespace gold
// If we fail to open the object, then we won't create an Add_symbols
// task. However, we still need to unblock the token, or else the
// link won't proceed to generate more error messages. We can only
// unblock tokens in the main thread, so we need a dummy task to do
// that. The dummy task has to maintain the right sequence of blocks,
// so we need both this_blocker and next_blocker.
// unblock tokens when the workqueue lock is held, so we need a dummy
// task to do that. The dummy task has to maintain the right sequence
// of blocks, so we need both this_blocker and next_blocker.
class Unblock_token : public Task
{
@ -56,17 +56,17 @@ class Unblock_token : public Task
delete this->this_blocker_;
}
Is_runnable_type
is_runnable(Workqueue*)
Task_token*
is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
return this->this_blocker_;
return NULL;
}
Task_locker*
locks(Workqueue* workqueue)
{ return new Task_locker_block(*this->next_blocker_, workqueue); }
void
locks(Task_locker* tl)
{ tl->add(this, this->next_blocker_); }
void
run(Workqueue*)
@ -93,24 +93,23 @@ Read_symbols::~Read_symbols()
// ordinary input file immediately. For an archive specified using
// -l, we have to wait until the search path is complete.
Task::Is_runnable_type
Read_symbols::is_runnable(Workqueue*)
Task_token*
Read_symbols::is_runnable()
{
if (this->input_argument_->is_file()
&& this->input_argument_->file().may_need_search()
&& this->dirpath_.token().is_blocked())
return IS_BLOCKED;
&& this->dirpath_->token()->is_blocked())
return this->dirpath_->token();
return IS_RUNNABLE;
return NULL;
}
// Return a Task_locker for a Read_symbols task. We don't need any
// locks here.
Task_locker*
Read_symbols::locks(Workqueue*)
void
Read_symbols::locks(Task_locker*)
{
return NULL;
}
// Run a Read_symbols task.
@ -139,7 +138,7 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
}
Input_file* input_file = new Input_file(&this->input_argument_->file());
if (!input_file->open(this->options_, this->dirpath_))
if (!input_file->open(this->options_, *this->dirpath_, this))
return false;
// Read enough of the file to pick up the entire ELF header.
@ -190,15 +189,23 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
Read_symbols_data* sd = new Read_symbols_data;
obj->read_symbols(sd);
// Opening the file locked it, so now we need to unlock it.
// We need to unlock it before queuing the Add_symbols task,
// because the workqueue doesn't know about our lock on the
// file. If we queue the Add_symbols task first, it will be
// stuck on the end of the file lock, but since the
// workqueue doesn't know about that lock, it will never
// release the Add_symbols task.
input_file->file().unlock(this);
workqueue->queue_front(new Add_symbols(this->input_objects_,
this->symtab_, this->layout_,
obj, sd,
this->this_blocker_,
this->next_blocker_));
// Opening the file locked it, so now we need to unlock it.
input_file->file().unlock();
return true;
}
}
@ -210,14 +217,15 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
// This is an archive.
Archive* arch = new Archive(this->input_argument_->file().name(),
input_file);
arch->setup();
workqueue->queue(new Add_archive_symbols(this->symtab_,
this->layout_,
this->input_objects_,
arch,
this->input_group_,
this->this_blocker_,
this->next_blocker_));
arch->setup(this);
workqueue->queue_front(new Add_archive_symbols(this->symtab_,
this->layout_,
this->input_objects_,
arch,
this->input_group_,
this->this_blocker_,
this->next_blocker_));
return true;
}
}
@ -251,6 +259,7 @@ Read_symbols::do_group(Workqueue* workqueue)
const Input_file_group* group = this->input_argument_->group();
Task_token* this_blocker = this->this_blocker_;
for (Input_file_group::const_iterator p = group->begin();
p != group->end();
++p)
@ -258,7 +267,7 @@ Read_symbols::do_group(Workqueue* workqueue)
const Input_argument* arg = &*p;
gold_assert(arg->is_file());
Task_token* next_blocker = new Task_token();
Task_token* next_blocker = new Task_token(true);
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(this->options_, this->input_objects_,
this->symtab_, this->layout_,
@ -319,34 +328,21 @@ Add_symbols::~Add_symbols()
// We are blocked by this_blocker_. We block next_blocker_. We also
// lock the file.
Task::Is_runnable_type
Add_symbols::is_runnable(Workqueue*)
Task_token*
Add_symbols::is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
return this->this_blocker_;
if (this->object_->is_locked())
return IS_LOCKED;
return IS_RUNNABLE;
return this->object_->token();
return NULL;
}
class Add_symbols::Add_symbols_locker : public Task_locker
void
Add_symbols::locks(Task_locker* tl)
{
public:
Add_symbols_locker(Task_token& token, Workqueue* workqueue,
Object* object)
: blocker_(token, workqueue), objlock_(*object)
{ }
private:
Task_locker_block blocker_;
Task_locker_obj<Object> objlock_;
};
Task_locker*
Add_symbols::locks(Workqueue* workqueue)
{
return new Add_symbols_locker(*this->next_blocker_, workqueue,
this->object_);
tl->add(this, this->next_blocker_);
tl->add(this, this->object_->token());
}
// Add the symbols in the object to the symbol table.
@ -363,6 +359,7 @@ Add_symbols::run(Workqueue*)
{
this->object_->layout(this->symtab_, this->layout_, this->sd_);
this->object_->add_symbols(this->symtab_, this->sd_);
this->object_->release();
}
delete this->sd_;
this->sd_ = NULL;
@ -380,18 +377,18 @@ Finish_group::~Finish_group()
// We need to wait for THIS_BLOCKER_ and unblock NEXT_BLOCKER_.
Task::Is_runnable_type
Finish_group::is_runnable(Workqueue*)
Task_token*
Finish_group::is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
return this->this_blocker_;
return NULL;
}
Task_locker*
Finish_group::locks(Workqueue* workqueue)
void
Finish_group::locks(Task_locker* tl)
{
return new Task_locker_block(*this->next_blocker_, workqueue);
tl->add(this, this->next_blocker_);
}
// Loop over the archives until there are no new undefined symbols.
@ -408,7 +405,7 @@ Finish_group::run(Workqueue*)
p != this->input_group_->end();
++p)
{
Task_lock_obj<Archive> tl(**p);
Task_lock_obj<Archive> tl(this, *p);
(*p)->add_symbols(this->symtab_, this->layout_,
this->input_objects_);

View File

@ -55,7 +55,7 @@ class Read_symbols : public Task
// NEXT_BLOCKER is used to block the next input file from adding
// symbols.
Read_symbols(const General_options& options, Input_objects* input_objects,
Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath,
Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
const Input_argument* input_argument, Input_group* input_group,
Task_token* this_blocker, Task_token* next_blocker)
: options_(options), input_objects_(input_objects), symtab_(symtab),
@ -68,11 +68,11 @@ class Read_symbols : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -93,7 +93,7 @@ class Read_symbols : public Task
Input_objects* input_objects_;
Symbol_table* symtab_;
Layout* layout_;
const Dirsearch& dirpath_;
Dirsearch* dirpath_;
const Input_argument* input_argument_;
Input_group* input_group_;
Task_token* this_blocker_;
@ -123,11 +123,11 @@ class Add_symbols : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -137,8 +137,6 @@ class Add_symbols : public Task
{ return "Add_symbols " + this->object_->name(); }
private:
class Add_symbols_locker;
Input_objects* input_objects_;
Symbol_table* symtab_;
Layout* layout_;
@ -199,11 +197,11 @@ class Finish_group : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);

View File

@ -37,18 +37,18 @@ namespace gold
// After reading it, the start another task to process the
// information. These tasks requires access to the file.
Task::Is_runnable_type
Read_relocs::is_runnable(Workqueue*)
Task_token*
Read_relocs::is_runnable()
{
return this->object_->is_locked() ? IS_LOCKED : IS_RUNNABLE;
return this->object_->is_locked() ? this->object_->token() : NULL;
}
// Lock the file.
Task_locker*
Read_relocs::locks(Workqueue*)
void
Read_relocs::locks(Task_locker* tl)
{
return new Task_locker_obj<Object>(*this->object_);
tl->add(this, this->object_->token());
}
// Read the relocations and then start a Scan_relocs_task.
@ -58,6 +58,8 @@ Read_relocs::run(Workqueue* workqueue)
{
Read_relocs_data *rd = new Read_relocs_data;
this->object_->read_relocs(rd);
this->object_->release();
workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_,
this->layout_, this->object_, rd,
this->symtab_lock_, this->blocker_));
@ -78,37 +80,25 @@ Read_relocs::get_name() const
// use a lock on the symbol table to keep them from interfering with
// each other.
Task::Is_runnable_type
Scan_relocs::is_runnable(Workqueue*)
Task_token*
Scan_relocs::is_runnable()
{
if (!this->symtab_lock_->is_writable() || this->object_->is_locked())
return IS_LOCKED;
return IS_RUNNABLE;
if (!this->symtab_lock_->is_writable())
return this->symtab_lock_;
if (this->object_->is_locked())
return this->object_->token();
return NULL;
}
// Return the locks we hold: one on the file, one on the symbol table
// and one blocker.
class Scan_relocs::Scan_relocs_locker : public Task_locker
void
Scan_relocs::locks(Task_locker* tl)
{
public:
Scan_relocs_locker(Object* object, Task_token& symtab_lock, Task* task,
Task_token& blocker, Workqueue* workqueue)
: objlock_(*object), symtab_locker_(symtab_lock, task),
blocker_(blocker, workqueue)
{ }
private:
Task_locker_obj<Object> objlock_;
Task_locker_write symtab_locker_;
Task_locker_block blocker_;
};
Task_locker*
Scan_relocs::locks(Workqueue* workqueue)
{
return new Scan_relocs_locker(this->object_, *this->symtab_lock_, this,
*this->blocker_, workqueue);
tl->add(this, this->object_->token());
tl->add(this, this->symtab_lock_);
tl->add(this, this->blocker_);
}
// Scan the relocs.
@ -118,6 +108,7 @@ Scan_relocs::run(Workqueue*)
{
this->object_->scan_relocs(this->options_, this->symtab_, this->layout_,
this->rd_);
this->object_->release();
delete this->rd_;
this->rd_ = NULL;
}
@ -134,46 +125,30 @@ Scan_relocs::get_name() const
// We may have to wait for the output sections to be written.
Task::Is_runnable_type
Relocate_task::is_runnable(Workqueue*)
Task_token*
Relocate_task::is_runnable()
{
if (this->object_->relocs_must_follow_section_writes()
&& this->output_sections_blocker_->is_blocked())
return IS_BLOCKED;
return this->output_sections_blocker_;
if (this->object_->is_locked())
return IS_LOCKED;
return this->object_->token();
return IS_RUNNABLE;
return NULL;
}
// We want to lock the file while we run. We want to unblock
// INPUT_SECTIONS_BLOCKER and FINAL_BLOCKER when we are done.
// INPUT_SECTIONS_BLOCKER may be NULL.
class Relocate_task::Relocate_locker : public Task_locker
void
Relocate_task::locks(Task_locker* tl)
{
public:
Relocate_locker(Task_token& input_sections_blocker,
Task_token& final_blocker, Workqueue* workqueue,
Object* object)
: input_sections_blocker_(input_sections_blocker, workqueue),
final_blocker_(final_blocker, workqueue),
objlock_(*object)
{ }
private:
Task_block_token input_sections_blocker_;
Task_block_token final_blocker_;
Task_locker_obj<Object> objlock_;
};
Task_locker*
Relocate_task::locks(Workqueue* workqueue)
{
return new Relocate_locker(*this->input_sections_blocker_,
*this->final_blocker_,
workqueue,
this->object_);
if (this->input_sections_blocker_ != NULL)
tl->add(this, this->input_sections_blocker_);
tl->add(this, this->final_blocker_);
tl->add(this, this->object_->token());
}
// Run the task.
@ -183,6 +158,7 @@ Relocate_task::run(Workqueue*)
{
this->object_->relocate(this->options_, this->symtab_, this->layout_,
this->of_);
this->object_->release();
}
// Return a debugging name for the task.
@ -401,10 +377,10 @@ template<int size, bool big_endian>
void
Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
Output_file* of,
Views* pviews)
Views* pviews) const
{
unsigned int shnum = this->shnum();
std::vector<Map_to_output>& map_sections(this->map_to_output());
const std::vector<Map_to_output>& map_sections(this->map_to_output());
const unsigned char* p = pshdrs + This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
@ -521,7 +497,7 @@ Sized_relobj<size, big_endian>::relocate_sections(
unsigned int shnum = this->shnum();
Sized_target<size, big_endian>* target = this->sized_target();
std::vector<Map_to_output>& map_sections(this->map_to_output());
const std::vector<Map_to_output>& map_sections(this->map_to_output());
Relocate_info<size, big_endian> relinfo;
relinfo.options = &options;

View File

@ -23,6 +23,7 @@
#ifndef GOLD_RELOC_H
#define GOLD_RELOC_H
#include <vector>
#include <byteswap.h>
#include "elfcpp.h"
@ -69,11 +70,11 @@ class Read_relocs : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -107,11 +108,11 @@ class Scan_relocs : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -120,8 +121,6 @@ class Scan_relocs : public Task
get_name() const;
private:
class Scan_relocs_locker;
const General_options& options_;
Symbol_table* symtab_;
Layout* layout_;
@ -148,11 +147,11 @@ class Relocate_task : public Task
// The standard Task methods.
Is_runnable_type
is_runnable(Workqueue*);
Task_token*
is_runnable();
Task_locker*
locks(Workqueue*);
void
locks(Task_locker*);
void
run(Workqueue*);
@ -161,8 +160,6 @@ class Relocate_task : public Task
get_name() const;
private:
class Relocate_locker;
const General_options& options_;
const Symbol_table* symtab_;
const Layout* layout_;

View File

@ -797,19 +797,17 @@ class Script_unblock : public Task
delete this->this_blocker_;
}
Is_runnable_type
is_runnable(Workqueue*)
Task_token*
is_runnable()
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
return this->this_blocker_;
return NULL;
}
Task_locker*
locks(Workqueue* workqueue)
{
return new Task_locker_block(*this->next_blocker_, workqueue);
}
void
locks(Task_locker* tl)
{ tl->add(this, this->next_blocker_); }
void
run(Workqueue*)
@ -826,8 +824,8 @@ class Script_unblock : public Task
// This class holds data passed through the parser to the lexer and to
// the parser support functions. This avoids global variables. We
// can't use global variables because we need not be called in the
// main thread.
// can't use global variables because we need not be called by a
// singleton thread.
class Parser_closure
{
@ -927,7 +925,7 @@ class Parser_closure
bool
read_input_script(Workqueue* workqueue, const General_options& options,
Symbol_table* symtab, Layout* layout,
const Dirsearch& dirsearch, Input_objects* input_objects,
Dirsearch* dirsearch, Input_objects* input_objects,
Input_group* input_group,
const Input_argument* input_argument,
Input_file* input_file, const unsigned char*, off_t,
@ -956,7 +954,7 @@ read_input_script(Workqueue* workqueue, const General_options& options,
{
// The script did not add any files to read. Note that we are
// not permitted to call NEXT_BLOCKER->unblock() here even if
// THIS_BLOCKER is NULL, as we are not in the main thread.
// THIS_BLOCKER is NULL, as we do not hold the workqueue lock.
workqueue->queue(new Script_unblock(this_blocker, next_blocker));
return true;
}
@ -970,7 +968,7 @@ read_input_script(Workqueue* workqueue, const General_options& options,
nb = next_blocker;
else
{
nb = new Task_token();
nb = new Task_token(true);
nb->add_blocker();
}
workqueue->queue(new Read_symbols(options, input_objects, symtab,
@ -992,17 +990,22 @@ read_commandline_script(const char* filename, Command_line* cmdline)
// using "." + cmdline->options()->search_path() -- not dirsearch.
Dirsearch dirsearch;
// The file locking code wants to record a Task, but we haven't
// started the workqueue yet. This is only for debugging purposes,
// so we invent a fake value.
const Task* task = reinterpret_cast<const Task*>(-1);
Input_file_argument input_argument(filename, false, "",
cmdline->position_dependent_options());
Input_file input_file(&input_argument);
if (!input_file.open(cmdline->options(), dirsearch))
if (!input_file.open(cmdline->options(), dirsearch, task))
return false;
Lex lex(&input_file);
if (lex.tokenize().is_invalid())
{
// Opening the file locked it, so now we need to unlock it.
input_file.file().unlock();
input_file.file().unlock(task);
return false;
}
@ -1014,11 +1017,11 @@ read_commandline_script(const char* filename, Command_line* cmdline)
&lex.tokens());
if (yyparse(&closure) != 0)
{
input_file.file().unlock();
input_file.file().unlock(task);
return false;
}
input_file.file().unlock();
input_file.file().unlock(task);
return true;
}

View File

@ -52,7 +52,7 @@ class Workqueue;
bool
read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
const Dirsearch&, Input_objects*, Input_group*,
Dirsearch*, Input_objects*, Input_group*,
const Input_argument*, Input_file*, const unsigned char* p,
off_t bytes, Task_token* this_blocker,
Task_token* next_blocker);

View File

@ -1392,8 +1392,8 @@ Symbol_table::set_dynsym_indexes(const Target* target,
// OFF. Add their names to POOL. Return the new file offset.
off_t
Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff,
size_t dyn_global_index, size_t dyncount,
Symbol_table::finalize(const Task* task, unsigned int index, off_t off,
off_t dynoff, size_t dyn_global_index, size_t dyncount,
Stringpool* pool)
{
off_t ret;
@ -1426,7 +1426,7 @@ Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff,
// Now that we have the final symbol table, we can reliably note
// which symbols should get warnings.
this->warnings_.note_warnings(this);
this->warnings_.note_warnings(this, task);
return ret;
}
@ -1945,7 +1945,8 @@ struct Odr_violation_compare
// but apparently different definitions (different source-file/line-no).
void
Symbol_table::detect_odr_violations(const char* output_file_name) const
Symbol_table::detect_odr_violations(const Task* task,
const char* output_file_name) const
{
for (Odr_map::const_iterator it = candidate_odr_violations_.begin();
it != candidate_odr_violations_.end();
@ -1961,14 +1962,14 @@ Symbol_table::detect_odr_violations(const char* output_file_name) const
++locs)
{
// We need to lock the object in order to read it. This
// means that we can not run inside a Task. If we want to
// run this in a Task for better performance, we will need
// one Task for object, plus appropriate locking to ensure
// that we don't conflict with other uses of the object.
locs->object->lock();
// means that we have to run in a singleton Task. If we
// want to run this in a general Task for better
// performance, we will need one Task for object, plus
// appropriate locking to ensure that we don't conflict with
// other uses of the object.
Task_lock_obj<Object> tl(task, locs->object);
std::string lineno = Dwarf_line_info::one_addr2line(
locs->object, locs->shndx, locs->offset);
locs->object->unlock();
if (!lineno.empty())
line_nums.insert(lineno);
}
@ -2003,7 +2004,7 @@ Warnings::add_warning(Symbol_table* symtab, const char* name, Object* obj,
// sources for all the symbols.
void
Warnings::note_warnings(Symbol_table* symtab)
Warnings::note_warnings(Symbol_table* symtab, const Task* task)
{
for (Warning_table::iterator p = this->warnings_.begin();
p != this->warnings_.end();
@ -2023,7 +2024,7 @@ Warnings::note_warnings(Symbol_table* symtab)
// the object then, as we might try to issue the same
// warning multiple times simultaneously.
{
Task_locker_obj<Object> tl(*p->second.object);
Task_lock_obj<Object> tl(task, p->second.object);
const unsigned char* c;
off_t len;
c = p->second.object->section_contents(p->second.shndx, &len,

View File

@ -905,7 +905,7 @@ class Warnings
// For each symbol for which we should give a warning, make a note
// on the symbol.
void
note_warnings(Symbol_table* symtab);
note_warnings(Symbol_table* symtab, const Task*);
// Issue a warning for a reference to SYM at RELINFO's location.
template<int size, bool big_endian>
@ -1078,7 +1078,7 @@ class Symbol_table
// Check candidate_odr_violations_ to find symbols with the same name
// but apparently different definitions (different source-file/line-no).
void
detect_odr_violations(const char* output_file_name) const;
detect_odr_violations(const Task*, const char* output_file_name) const;
// SYM is defined using a COPY reloc. Return the dynamic object
// where the original definition was found.
@ -1102,7 +1102,7 @@ class Symbol_table
// symbol, and DYNCOUNT is the number of global dynamic symbols.
// This records the parameters, and returns the new file offset.
off_t
finalize(unsigned int index, off_t off, off_t dynoff,
finalize(const Task*, unsigned int index, off_t off, off_t dynoff,
size_t dyn_global_index, size_t dyncount, Stringpool* pool);
// Write out the global symbols.

View File

@ -39,22 +39,24 @@ bool
Sized_object_test(const unsigned char* test_file, unsigned int test_file_size,
Target* target_test_pointer)
{
Input_file input_file("test.o", test_file, test_file_size);
// We need a pretend Task.
const Task* task = reinterpret_cast<const Task*>(-1);
Input_file input_file(task, "test.o", test_file, test_file_size);
Object* object = make_elf_object("test.o", &input_file, 0,
test_file, test_file_size);
CHECK(object->name() == "test.o");
CHECK(!object->is_dynamic());
CHECK(object->target() == target_test_pointer);
CHECK(object->is_locked());
object->unlock();
object->unlock(task);
CHECK(!object->is_locked());
object->lock();
object->lock(task);
CHECK(object->shnum() == 5);
CHECK(object->section_name(0).empty());
CHECK(object->section_name(1) == ".test");
CHECK(object->section_flags(0) == 0);
CHECK(object->section_flags(1) == elfcpp::SHF_ALLOC);
object->unlock();
object->unlock(task);
return true;
}

316
gold/token.h Normal file
View File

@ -0,0 +1,316 @@
// token.h -- lock tokens 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_TOKEN_H
#define GOLD_TOKEN_H
namespace gold
{
class Condvar;
class Task;
// A list of Tasks, managed through the next_locked_ field in the
// class Task. We define this class here because we need it in
// Task_token.
class Task_list
{
public:
Task_list()
: head_(NULL), tail_(NULL)
{ }
~Task_list()
{ gold_assert(this->head_ == NULL && this->tail_ == NULL); }
// Return whether the list is empty.
bool
empty() const
{ return this->head_ == NULL; }
// Add T to the end of the list.
void
push_back(Task* t);
// Remove the first Task on the list and return it. Return NULL if
// the list is empty.
Task*
pop_front();
private:
// The start of the list. NULL if the list is empty.
Task* head_;
// The end of the list. NULL if the list is empty.
Task* tail_;
};
// We support two basic types of locks, which are both implemented
// using the single class Task_token.
// A write lock may be held by a single Task at a time. This is used
// to control access to a single shared resource such as an Object.
// A blocker is used to indicate that a Task A must be run after some
// set of Tasks B. For each of the Tasks B, we increment the blocker
// when the Task is created, and decrement it when the Task is
// completed. When the count goes to 0, the task A is ready to run.
// There are no shared read locks. We always read and write objects
// in predictable patterns. The purpose of the locks is to permit
// some flexibility for the threading system, for cases where the
// execution order does not matter.
// These tokens are only manipulated when the workqueue lock is held
// or when they are first created. They do not require any locking
// themselves.
class Task_token
{
public:
Task_token(bool is_blocker)
: is_blocker_(is_blocker), blockers_(0), writer_(NULL), waiting_()
{ }
~Task_token()
{
gold_assert(this->blockers_ == 0);
gold_assert(this->writer_ == NULL);
}
// Return whether this is a blocker.
bool
is_blocker() const
{ return this->is_blocker_; }
// A write lock token uses these methods.
// Is the token writable?
bool
is_writable() const
{
gold_assert(!this->is_blocker_);
return this->writer_ == NULL;
}
// Add the task as the token's writer (there may only be one
// writer).
void
add_writer(const Task* t)
{
gold_assert(!this->is_blocker_ && this->writer_ == NULL);
this->writer_ = t;
}
// Remove the task as the token's writer.
void
remove_writer(const Task* t)
{
gold_assert(!this->is_blocker_ && this->writer_ == t);
this->writer_ = NULL;
}
// A blocker token uses these methods.
// Add a blocker to the token.
void
add_blocker()
{
gold_assert(this->is_blocker_);
++this->blockers_;
this->writer_ = NULL;
}
// Remove a blocker from the token. Returns true if block count
// drops to zero.
bool
remove_blocker()
{
gold_assert(this->is_blocker_ && this->blockers_ > 0);
--this->blockers_;
this->writer_ = NULL;
return this->blockers_ == 0;
}
// Is the token currently blocked?
bool
is_blocked() const
{
gold_assert(this->is_blocker_);
return this->blockers_ > 0;
}
// Both blocker and write lock tokens use these methods.
// Add T to the list of tasks waiting for this token to be released.
void
add_waiting(Task* t)
{ this->waiting_.push_back(t); }
// Remove the first Task waiting for this token to be released, and
// return it. Return NULL if no Tasks are waiting.
Task*
remove_first_waiting()
{ return this->waiting_.pop_front(); }
private:
// It makes no sense to copy these.
Task_token(const Task_token&);
Task_token& operator=(const Task_token&);
// Whether this is a blocker token.
bool is_blocker_;
// The number of blockers.
int blockers_;
// The single writer.
const Task* writer_;
// The list of Tasks waiting for this token to be released.
Task_list waiting_;
};
// In order to support tokens more reliably, we provide objects which
// handle them using RAII.
// RAII class to get a write lock on a token. This requires
// specifying the task which is doing the lock.
class Task_write_token
{
public:
Task_write_token(Task_token* token, const Task* task)
: token_(token), task_(task)
{ this->token_->add_writer(this->task_); }
~Task_write_token()
{ this->token_->remove_writer(this->task_); }
private:
Task_write_token(const Task_write_token&);
Task_write_token& operator=(const Task_write_token&);
Task_token* token_;
const Task* task_;
};
// RAII class for a blocker.
class Task_block_token
{
public:
// The blocker count must be incremented when the task is created.
// This object is created when the task is run, so we don't do
// anything in the constructor.
Task_block_token(Task_token* token)
: token_(token)
{ gold_assert(this->token_->is_blocked()); }
~Task_block_token()
{ this->token_->remove_blocker(); }
private:
Task_block_token(const Task_block_token&);
Task_block_token& operator=(const Task_block_token&);
Task_token* token_;
};
// An object which implements an RAII lock for any object which
// supports lock and unlock methods.
template<typename Obj>
class Task_lock_obj
{
public:
Task_lock_obj(const Task* task, Obj* obj)
: task_(task), obj_(obj)
{ this->obj_->lock(task); }
~Task_lock_obj()
{ this->obj_->unlock(this->task_); }
private:
Task_lock_obj(const Task_lock_obj&);
Task_lock_obj& operator=(const Task_lock_obj&);
const Task* task_;
Obj* obj_;
};
// A class which holds the set of Task_tokens which must be locked for
// a Task. No Task requires more than four Task_tokens, so we set
// that as a limit.
class Task_locker
{
public:
static const int max_task_count = 4;
Task_locker()
: count_(0)
{ }
~Task_locker()
{ }
// Clear the locker.
void
clear()
{ this->count_ = 0; }
// Add a token to the locker.
void
add(Task* t, Task_token* token)
{
gold_assert(this->count_ < max_task_count);
this->tokens_[this->count_] = token;
++this->count_;
// A blocker will have been incremented when the task is created.
// A writer we need to lock now.
if (!token->is_blocker())
token->add_writer(t);
}
// Iterate over the tokens.
typedef Task_token** iterator;
iterator
begin()
{ return &this->tokens_[0]; }
iterator
end()
{ return &this->tokens_[this->count_]; }
private:
Task_locker(const Task_locker&);
Task_locker& operator=(const Task_locker&);
// The number of tokens.
int count_;
// The tokens.
Task_token* tokens_[max_task_count];
};
} // End namespace gold.
#endif // !defined(GOLD_TOKEN_H)

View File

@ -24,6 +24,7 @@
#define GOLD_WORKQUEUE_INTERNAL_H
#include <queue>
#include <csignal>
#include "gold-threads.h"
#include "workqueue.h"
@ -36,92 +37,71 @@ 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.
// The Workqueue_threader abstract class. This is the interface used
// by the general workqueue code to manage threads.
class Workqueue_runner
class Workqueue_threader
{
public:
Workqueue_runner(Workqueue* workqueue)
Workqueue_threader(Workqueue* workqueue)
: workqueue_(workqueue)
{ }
virtual ~Workqueue_runner()
virtual ~Workqueue_threader()
{ }
// 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); }
// Return whether to cancel the current thread.
virtual bool
should_cancel_thread() = 0;
Workqueue* get_workqueue() const
protected:
// Get the Workqueue.
Workqueue*
get_workqueue()
{ return this->workqueue_; }
private:
// The Workqueue.
Workqueue* workqueue_;
};
// The threaded instantiation of Workqueue_runner.
// The threaded instantiation of Workqueue_threader.
class Workqueue_runner_threadpool : public Workqueue_runner
class Workqueue_threader_threadpool : public Workqueue_threader
{
public:
Workqueue_runner_threadpool(Workqueue* workqueue);
Workqueue_threader_threadpool(Workqueue*);
~Workqueue_runner_threadpool();
void
run(Task*, Task_locker*);
~Workqueue_threader_threadpool();
// Set the thread count.
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.
// Return whether to cancel a thread.
bool
get_next(Task**, Task_locker**);
should_cancel_thread();
// This is called when the thread completes a task.
// Process all tasks. This keeps running until told to cancel.
void
thread_completed(Task*, Task_locker*);
process(int thread_number)
{ this->get_workqueue()->process(thread_number); }
// The Workqueue_thread class calls functions from this and from the
// parent Workqueue_runner.
friend class Workqueue_thread;
private:
// This is set if we need to check the thread count.
volatile sig_atomic_t check_thread_count_;
// 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 for the remaining members.
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_;
// The number of threads we want to create. This is set to zero
// when all threads should exit.
int desired_thread_count_;
// The number of threads currently running.
int threads_;
};
} // End namespace gold.

View File

@ -44,7 +44,7 @@ namespace gold
class Workqueue_thread
{
public:
Workqueue_thread(Workqueue_runner_threadpool*);
Workqueue_thread(Workqueue_threader_threadpool*, int thread_number);
~Workqueue_thread();
@ -62,20 +62,19 @@ class Workqueue_thread
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_;
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_runner_threadpool* threadpool)
: threadpool_(threadpool)
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);
@ -114,7 +113,8 @@ void*
Workqueue_thread::thread_body(void* arg)
{
Workqueue_thread* pwt = reinterpret_cast<Workqueue_thread*>(arg);
pwt->run();
pwt->threadpool_->process(pwt->thread_number_);
// Delete the thread object as we exit.
delete pwt;
@ -122,144 +122,75 @@ Workqueue_thread::thread_body(void* arg)
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.
// Class Workqueue_threader_threadpool.
// Constructor.
Workqueue_runner_threadpool::Workqueue_runner_threadpool(Workqueue* workqueue)
: Workqueue_runner(workqueue),
desired_thread_count_(0),
Workqueue_threader_threadpool::Workqueue_threader_threadpool(
Workqueue* workqueue)
: Workqueue_threader(workqueue),
check_thread_count_(0),
lock_(),
actual_thread_count_(0),
running_thread_count_(0),
task_queue_(),
task_queue_condvar_(this->lock_)
desired_thread_count_(1),
threads_(1)
{
}
// Destructor.
Workqueue_runner_threadpool::~Workqueue_runner_threadpool()
Workqueue_threader_threadpool::~Workqueue_threader_threadpool()
{
// Tell the threads to exit.
Hold_lock hl(this->lock_);
this->desired_thread_count_ = 0;
this->task_queue_condvar_.broadcast();
this->get_workqueue()->set_thread_count(0);
}
// Run a task. This doesn't actually run the task: it pushes it on
// the queue of tasks to run. This is always called in the main
// thread.
// Set the thread count.
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)
Workqueue_threader_threadpool::set_thread_count(int thread_count)
{
int create;
{
Hold_lock hl(this->lock_);
gold_assert(this->actual_thread_count_ > 0);
gold_assert(this->running_thread_count_ > 0);
--this->running_thread_count_;
this->desired_thread_count_ = thread_count;
create = this->desired_thread_count_ - this->threads_;
if (create < 0)
this->check_thread_count_ = 1;
}
this->completed(t, tl);
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()
{
// Fast exit without taking a lock.
if (!this->check_thread_count_)
return false;
{
Hold_lock hl(this->lock_);
if (this->threads_ > this->desired_thread_count_)
{
--this->threads_;
return true;
}
this->check_thread_count_ = 0;
}
return false;
}
} // End namespace gold.

View File

@ -23,181 +23,99 @@
#include "gold.h"
#include "debug.h"
#include "options.h"
#include "workqueue.h"
#include "workqueue-internal.h"
namespace gold
{
// Task_token methods.
// Class Task_list.
Task_token::Task_token()
: is_blocker_(false), readers_(0), writer_(NULL)
// Add T to the end of the list.
inline void
Task_list::push_back(Task* t)
{
}
Task_token::~Task_token()
{
gold_assert(this->readers_ == 0 && this->writer_ == NULL);
}
bool
Task_token::is_readable() const
{
gold_assert(!this->is_blocker_);
return this->writer_ == NULL;
}
void
Task_token::add_reader()
{
gold_assert(!this->is_blocker_);
gold_assert(this->is_readable());
++this->readers_;
}
void
Task_token::remove_reader()
{
gold_assert(!this->is_blocker_);
gold_assert(this->readers_ > 0);
--this->readers_;
}
bool
Task_token::is_writable() const
{
gold_assert(!this->is_blocker_);
return this->writer_ == NULL && this->readers_ == 0;
}
void
Task_token::add_writer(const Task* t)
{
gold_assert(!this->is_blocker_);
gold_assert(this->is_writable());
this->writer_ = t;
}
void
Task_token::remove_writer(const Task* t)
{
gold_assert(!this->is_blocker_);
gold_assert(this->writer_ == t);
this->writer_ = NULL;
}
bool
Task_token::has_write_lock(const Task* t)
{
gold_assert(!this->is_blocker_);
return this->writer_ == t;
}
// For blockers, we just use the readers_ field.
void
Task_token::add_blocker()
{
if (this->readers_ == 0 && this->writer_ == NULL)
this->is_blocker_ = true;
else
gold_assert(this->is_blocker_);
++this->readers_;
}
bool
Task_token::remove_blocker()
{
gold_assert(this->is_blocker_ && this->readers_ > 0);
--this->readers_;
return this->readers_ == 0;
}
bool
Task_token::is_blocked() const
{
gold_assert(this->is_blocker_
|| (this->readers_ == 0 && this->writer_ == NULL));
return this->readers_ > 0;
}
// The Task_block_token class.
Task_block_token::Task_block_token(Task_token& token, Workqueue* workqueue)
: token_(token), workqueue_(workqueue)
{
// We must increment the block count when the task is created and
// put on the queue. This object is created when the task is run,
// so we don't increment the block count here.
gold_assert(this->token_.is_blocked());
}
Task_block_token::~Task_block_token()
{
if (this->token_.remove_blocker())
gold_assert(t->list_next() == NULL);
if (this->head_ == NULL)
{
// Tell the workqueue that a blocker was cleared. This is
// always called in the main thread, so no locking is required.
this->workqueue_->cleared_blocker();
this->head_ = t;
this->tail_ = t;
}
else
{
this->tail_->set_list_next(t);
this->tail_ = t;
}
}
// The simple single-threaded implementation of Workqueue_runner.
// Remove and return the first Task waiting for this lock to be
// released.
class Workqueue_runner_single : public Workqueue_runner
inline Task*
Task_list::pop_front()
{
Task* ret = this->head_;
if (ret != NULL)
{
if (ret == this->tail_)
{
gold_assert(ret->list_next() == NULL);
this->head_ = NULL;
this->tail_ = NULL;
}
else
{
this->head_ = ret->list_next();
gold_assert(this->head_ != NULL);
ret->clear_list_next();
}
}
return ret;
}
// The simple single-threaded implementation of Workqueue_threader.
class Workqueue_threader_single : public Workqueue_threader
{
public:
Workqueue_runner_single(Workqueue* workqueue)
: Workqueue_runner(workqueue)
Workqueue_threader_single(Workqueue* workqueue)
: Workqueue_threader(workqueue)
{ }
~Workqueue_runner_single()
~Workqueue_threader_single()
{ }
void
run(Task*, Task_locker*);
set_thread_count(int thread_count)
{ gold_assert(thread_count > 0); }
void
set_thread_count(int);
bool
should_cancel_thread()
{ return false; }
};
void
Workqueue_runner_single::run(Task* t, Task_locker* tl)
{
t->run(this->get_workqueue());
this->completed(t, tl);
}
void
Workqueue_runner_single::set_thread_count(int thread_count)
{
gold_assert(thread_count > 0);
}
// Workqueue methods.
Workqueue::Workqueue(const General_options& options)
: tasks_lock_(),
: lock_(),
first_tasks_(),
tasks_(),
completed_lock_(),
completed_(),
running_(0),
queued_(0),
completed_condvar_(this->completed_lock_),
cleared_blockers_(0),
desired_thread_count_(1)
waiting_(0),
condvar_(this->lock_),
threader_(NULL)
{
bool threads = options.threads();
#ifndef ENABLE_THREADS
threads = false;
#endif
if (!threads)
this->runner_ = new Workqueue_runner_single(this);
this->threader_ = new Workqueue_threader_single(this);
else
{
#ifdef ENABLE_THREADS
this->runner_ = new Workqueue_runner_threadpool(this);
this->threader_ = new Workqueue_threader_threadpool(this);
#else
gold_unreachable();
#endif
@ -206,10 +124,28 @@ Workqueue::Workqueue(const General_options& options)
Workqueue::~Workqueue()
{
gold_assert(this->first_tasks_.empty());
gold_assert(this->tasks_.empty());
gold_assert(this->completed_.empty());
gold_assert(this->running_ == 0);
}
// Add a task to the end of a specific queue, or put it on the list
// waiting for a Token.
void
Workqueue::add_to_queue(Task_list* queue, Task* t)
{
Hold_lock hl(this->lock_);
Task_token* token = t->is_runnable();
if (token != NULL)
{
token->add_waiting(t);
++this->waiting_;
}
else
{
queue->push_back(t);
// Tell any waiting thread that there is work to do.
this->condvar_.signal();
}
}
// Add a task to the queue.
@ -217,14 +153,7 @@ Workqueue::~Workqueue()
void
Workqueue::queue(Task* t)
{
{
Hold_lock hl(this->tasks_lock_);
this->tasks_.push_back(t);
}
{
Hold_lock hl(this->completed_lock_);
++this->queued_;
}
this->add_to_queue(&this->tasks_, t);
}
// Add a task to the front of the queue.
@ -232,236 +161,291 @@ Workqueue::queue(Task* t)
void
Workqueue::queue_front(Task* t)
{
{
Hold_lock hl(this->tasks_lock_);
this->first_tasks_.push_front(t);
}
{
Hold_lock hl(this->completed_lock_);
++this->queued_;
}
t->set_should_run_soon();
this->add_to_queue(&this->first_tasks_, t);
}
// Clear the list of completed tasks. Return whether we cleared
// anything. The completed_lock_ must be held when this is called.
// Return whether to cancel the current thread.
bool
Workqueue::clear_completed()
inline bool
Workqueue::should_cancel_thread()
{
if (this->completed_.empty())
return false;
do
{
delete this->completed_.front();
this->completed_.pop_front();
}
while (!this->completed_.empty());
return true;
return this->threader_->should_cancel_thread();
}
// Find a runnable task in TASKS, which is non-empty. Return NULL if
// none could be found. The tasks_lock_ must be held when this is
// called. Sets ALL_BLOCKED if all non-runnable tasks are waiting on
// a blocker.
// Find a runnable task in TASKS. Return NULL if none could be found.
// If we find a Task waiting for a Token, add it to the list for that
// Token. The workqueue lock must be held when this is called.
Task*
Workqueue::find_runnable(Task_list* tasks, bool* all_blocked)
Workqueue::find_runnable_in_list(Task_list* tasks)
{
Task* tlast = tasks->back();
*all_blocked = true;
Task* t;
do
while ((t = tasks->pop_front()) != NULL)
{
t = tasks->front();
tasks->pop_front();
Task_token* token = t->is_runnable();
Task::Is_runnable_type is_runnable = t->is_runnable(this);
if (is_runnable == Task::IS_RUNNABLE)
{
{
Hold_lock hl(this->completed_lock_);
--this->queued_;
}
if (token == NULL)
return t;
return t;
}
if (is_runnable != Task::IS_BLOCKED)
*all_blocked = false;
tasks->push_back(t);
token->add_waiting(t);
++this->waiting_;
}
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
// the linker. Note that as we process tasks, new tasks will be
// added.
// Find a runnable task. Return NULL if none could be found. The
// workqueue lock must be held when this is called.
void
Workqueue::process()
Task*
Workqueue::find_runnable()
{
while (true)
Task* t = this->find_runnable_in_list(&this->first_tasks_);
if (t == NULL)
t = this->find_runnable_in_list(&this->tasks_);
return t;
}
// Find a runnable a task, and wait until we find one. Return NULL if
// we should exit. The workqueue lock must be held when this is
// called.
Task*
Workqueue::find_runnable_or_wait(int thread_number)
{
Task* t = this->find_runnable();
while (t == NULL)
{
Task* t;
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_);
bool first_empty;
bool all_blocked_first;
if (this->first_tasks_.empty())
{
t = NULL;
empty = true;
first_empty = true;
all_blocked_first = false;
}
else
{
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.
// If T == NULL && !empty, then there tasks waiting to be
// run, but they are waiting for something to unlock.
if (t != NULL)
this->run(t);
else if (!empty)
if (this->running_ == 0
&& this->first_tasks_.empty()
&& this->tasks_.empty())
{
// Kick all the threads to make them exit.
this->condvar_.broadcast();
gold_assert(this->waiting_ == 0);
return NULL;
}
if (this->should_cancel_thread())
return NULL;
gold_debug(DEBUG_TASK, "%3d sleeping", thread_number);
this->condvar_.wait();
gold_debug(DEBUG_TASK, "%3d awake", thread_number);
t = this->find_runnable();
}
return t;
}
// Find and run tasks. If we can't find a runnable task, wait for one
// to become available. If we run a task, and it frees up another
// runnable task, then run that one too. This returns true if we
// should look for another task, false if we are cancelling this
// thread.
bool
Workqueue::find_and_run_task(int thread_number)
{
Task* t;
Task_locker tl;
{
Hold_lock hl(this->lock_);
// Find a runnable task.
t = this->find_runnable_or_wait(thread_number);
if (t == NULL)
return false;
// Get the locks for the task. This must be called while we are
// still holding the Workqueue lock.
t->locks(&tl);
++this->running_;
}
while (t != NULL)
{
gold_debug(DEBUG_TASK, "%3d running task %s", thread_number,
t->name().c_str());
t->run(this);
gold_debug(DEBUG_TASK, "%3d completed task %s", thread_number,
t->name().c_str());
Task* next;
{
Hold_lock hl(this->lock_);
--this->running_;
// Release the locks for the task. This must be done with the
// workqueue lock held. Get the next Task to run if any.
next = this->release_locks(t, &tl);
if (next == NULL)
next = this->find_runnable();
// If we have another Task to run, get the Locks. This must
// be called while we are still holding the Workqueue lock.
if (next != NULL)
{
Hold_lock hl(this->completed_lock_);
tl.clear();
next->locks(&tl);
// There must be something for us to wait for, or we won't
// be able to make progress.
gold_assert(this->running_ > 0 || !this->completed_.empty());
if (all_blocked)
{
this->cleared_blockers_ = 0;
int queued = this->queued_;
this->clear_completed();
while (this->cleared_blockers_ == 0
&& queued == this->queued_)
{
if (this->running_ <= 0)
{
this->show_queued_tasks();
gold_unreachable();
}
this->completed_condvar_.wait();
this->clear_completed();
}
}
else
{
if (this->running_ > 0)
{
// Wait for a task to finish.
this->completed_condvar_.wait();
}
this->clear_completed();
}
++this->running_;
}
}
// We are done with this task.
delete t;
t = next;
}
return true;
}
// Handle the return value of release_locks, and get tasks ready to
// run.
// 1) If T is not runnable, queue it on the appropriate token.
// 2) Otherwise, T is runnable. If *PRET is not NULL, then we have
// already decided which Task to run next. Add T to the list of
// runnable tasks, and signal another thread.
// 3) Otherwise, *PRET is NULL. If IS_BLOCKER is false, then T was
// waiting on a write lock. We can grab that lock now, so we run T
// now.
// 4) Otherwise, IS_BLOCKER is true. If we should run T soon, then
// run it now.
// 5) Otherwise, check whether there are other tasks to run. If there
// are, then we generally get a better ordering if we run those tasks
// now, before T. A typical example is tasks waiting on the Dirsearch
// blocker. We don't want to run those tasks right away just because
// the Dirsearch was unblocked.
// 6) Otherwise, there are no other tasks to run, so we might as well
// run this one now.
// This function must be called with the Workqueue lock held.
// Return true if we set *PRET to T, false otherwise.
bool
Workqueue::return_or_queue(Task* t, bool is_blocker, Task** pret)
{
Task_token* token = t->is_runnable();
if (token != NULL)
{
token->add_waiting(t);
++this->waiting_;
return false;
}
bool should_queue = false;
bool should_return = false;
if (*pret != NULL)
should_queue = true;
else if (!is_blocker)
should_return = true;
else if (t->should_run_soon())
should_return = true;
else if (!this->first_tasks_.empty() || !this->tasks_.empty())
should_queue = true;
else
should_return = true;
if (should_return)
{
gold_assert(*pret == NULL);
*pret = t;
return true;
}
else if (should_queue)
{
if (t->should_run_soon())
this->first_tasks_.push_back(t);
else
this->tasks_.push_back(t);
this->condvar_.signal();
return false;
}
gold_unreachable();
}
// Release the locks associated with a Task. Return the first
// runnable Task that we find. If we find more runnable tasks, add
// them to the run queue and signal any other threads. This must be
// called with the Workqueue lock held.
Task*
Workqueue::release_locks(Task* t, Task_locker* tl)
{
Task* ret = NULL;
for (Task_locker::iterator p = tl->begin(); p != tl->end(); ++p)
{
Task_token* token = *p;
if (token->is_blocker())
{
if (token->remove_blocker())
{
// The token has been unblocked. Every waiting Task may
// now be runnable.
Task* t;
while ((t = token->remove_first_waiting()) != NULL)
{
--this->waiting_;
this->return_or_queue(t, true, &ret);
}
}
}
else
{
{
Hold_lock hl(this->completed_lock_);
token->remove_writer(t);
// If there are no running tasks, then we are done.
if (this->running_ == 0)
{
this->clear_completed();
return;
}
// Wait for a task to finish. Then we have to loop around
// again in case it added any new tasks before finishing.
this->completed_condvar_.wait();
this->clear_completed();
}
// One more waiting Task may now be runnable. If we are
// going to run it next, we can stop. Otherwise we need to
// move all the Tasks to the runnable queue, to avoid a
// potential deadlock if the locking status changes before
// we run the next thread.
Task* t;
while ((t = token->remove_first_waiting()) != NULL)
{
--this->waiting_;
if (this->return_or_queue(t, false, &ret))
break;
}
}
}
return ret;
}
// Run a task. This is always called in the main thread.
// Process all the tasks on the workqueue. Keep going until the
// workqueue is empty, or until we have been told to exit. This
// function is called by all threads.
void
Workqueue::run(Task* t)
Workqueue::process(int thread_number)
{
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));
}
// This is called when a task is completed to put the locks on the
// list to be released. We use a list because we only want the locks
// to be released in the main thread.
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);
--this->running_;
this->completed_.push_back(tl);
this->completed_condvar_.signal();
}
delete t;
}
// This is called when the last task for a blocker has completed.
// This is always called in the main thread.
void
Workqueue::cleared_blocker()
{
++this->cleared_blockers_;
while (this->find_and_run_task(thread_number))
;
}
// Set the number of threads to use for the workqueue, if we are using
@ -470,40 +454,11 @@ 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);
}
Hold_lock hl(this->lock_);
// 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);
}
this->threader_->set_thread_count(threads);
// Wake up all the threads, since something has changed.
this->condvar_.broadcast();
}
} // End namespace gold.

View File

@ -24,250 +24,20 @@
// driven from a work queue. This permits us to parallelize the
// linker where possible.
// Task_token
// A simple locking implementation to ensure proper task ordering.
// Task_read_token, Task_write_token
// Lock a Task_token for read or write.
// Task_locker
// Task locking using RAII.
// Task
// An abstract class for jobs to run.
#ifndef GOLD_WORKQUEUE_H
#define GOLD_WORKQUEUE_H
#include <string>
#include "gold-threads.h"
#include "fileread.h"
#include "token.h"
namespace gold
{
class General_options;
class Task;
class Workqueue;
// Some tasks require access to shared data structures, such as the
// symbol table. Some tasks must be executed in a particular order,
// such as reading input file symbol tables--if we see foo.o -llib, we
// have to read the symbols for foo.o before we read the ones for
// -llib. To implement this safely and efficiently, we use tokens.
// Task_tokens support shared read/exclusive write access to some
// resource. Alternatively, they support blockers: blockers implement
// the requirement that some set of tasks must complete before another
// set of tasks can start. In such a case we increment the block
// count when we create the task, and decrement it when the task
// completes. Task_tokens are only manipulated by the main thread, so
// they do not themselves require any locking.
class Task_token
{
public:
Task_token();
~Task_token();
// A read/write token uses these methods.
bool
is_readable() const;
void
add_reader();
void
remove_reader();
bool
is_writable() const;
void
add_writer(const Task*);
void
remove_writer(const Task*);
bool
has_write_lock(const Task*);
// A blocker token uses these methods.
void
add_blocker();
// Returns true if block count drops to zero.
bool
remove_blocker();
bool
is_blocked() const;
private:
// It makes no sense to copy these.
Task_token(const Task_token&);
Task_token& operator=(const Task_token&);
bool is_blocker_;
int readers_;
const Task* writer_;
};
// In order to support tokens more reliably, we provide objects which
// handle them using RAII.
class Task_read_token
{
public:
Task_read_token(Task_token& token)
: token_(token)
{ this->token_.add_reader(); }
~Task_read_token()
{ this->token_.remove_reader(); }
private:
Task_read_token(const Task_read_token&);
Task_read_token& operator=(const Task_read_token&);
Task_token& token_;
};
class Task_write_token
{
public:
Task_write_token(Task_token& token, const Task* task)
: token_(token), task_(task)
{ this->token_.add_writer(this->task_); }
~Task_write_token()
{ this->token_.remove_writer(this->task_); }
private:
Task_write_token(const Task_write_token&);
Task_write_token& operator=(const Task_write_token&);
Task_token& token_;
const Task* task_;
};
class Task_block_token
{
public:
// The blocker count must be incremented when the task is created.
// This object is created when the task is run. When we unblock the
// last task, we notify the workqueue.
Task_block_token(Task_token& token, Workqueue* workqueue);
~Task_block_token();
private:
Task_block_token(const Task_block_token&);
Task_block_token& operator=(const Task_block_token&);
Task_token& token_;
Workqueue* workqueue_;
};
// An object which implements an RAII lock for any object which
// supports lock and unlock methods.
template<typename Obj>
class Task_lock_obj
{
public:
Task_lock_obj(Obj& obj)
: obj_(obj)
{ this->obj_.lock(); }
~Task_lock_obj()
{ this->obj_.unlock(); }
private:
Task_lock_obj(const Task_lock_obj&);
Task_lock_obj& operator=(const Task_lock_obj&);
Obj& obj_;
};
// An abstract class used to lock Task_tokens using RAII. A typical
// implementation would simply have a set of members of type
// Task_read_token, Task_write_token, and Task_block_token.
class Task_locker
{
public:
Task_locker()
{ }
virtual ~Task_locker()
{ }
};
// A version of Task_locker which may be used for a single read lock.
class Task_locker_read : public Task_locker
{
public:
Task_locker_read(Task_token& token)
: read_token_(token)
{ }
private:
Task_locker_read(const Task_locker_read&);
Task_locker_read& operator=(const Task_locker_read&);
Task_read_token read_token_;
};
// A version of Task_locker which may be used for a single write lock.
class Task_locker_write : public Task_locker
{
public:
Task_locker_write(Task_token& token, const Task* task)
: write_token_(token, task)
{ }
private:
Task_locker_write(const Task_locker_write&);
Task_locker_write& operator=(const Task_locker_write&);
Task_write_token write_token_;
};
// A version of Task_locker which may be used for a single blocker
// lock.
class Task_locker_block : public Task_locker
{
public:
Task_locker_block(Task_token& token, Workqueue* workqueue)
: block_token_(token, workqueue)
{ }
private:
Task_locker_block(const Task_locker_block&);
Task_locker_block& operator=(const Task_locker_block&);
Task_block_token block_token_;
};
// A version of Task_locker which may be used to hold a lock on any
// object which supports lock() and unlock() methods.
template<typename Obj>
class Task_locker_obj : public Task_locker
{
public:
Task_locker_obj(Obj& obj)
: obj_lock_(obj)
{ }
private:
Task_locker_obj(const Task_locker_obj&);
Task_locker_obj& operator=(const Task_locker_obj&);
Task_lock_obj<Obj> obj_lock_;
};
// The superclass for tasks to be placed on the workqueue. Each
// specific task class will inherit from this one.
@ -275,39 +45,57 @@ class Task
{
public:
Task()
: name_()
: list_next_(NULL), name_(), should_run_soon_(false)
{ }
virtual ~Task()
{ }
// Type returned by Is_runnable.
enum Is_runnable_type
{
// Task is runnable.
IS_RUNNABLE,
// Task is waiting for a block to clear.
IS_BLOCKED,
// Task is not waiting for a block, but is not runnable--i.e., is
// waiting for a lock.
IS_LOCKED
};
// Check whether the Task can be run now. This method is only
// called with the workqueue lock held. If the Task can run, this
// returns NULL. Otherwise it returns a pointer to a token which
// must be released before the Task can run.
virtual Task_token*
is_runnable() = 0;
// Return whether the task can be run now. This method is only
// called from the main thread.
virtual Is_runnable_type
is_runnable(Workqueue*) = 0;
// Return a pointer to a Task_locker which locks all the resources
// required by the task. We delete the pointer when the task is
// complete. This method can return NULL if no locks are required.
// This method is only called from the main thread.
virtual Task_locker*
locks(Workqueue*) = 0;
// Lock all the resources required by the Task, and store the locks
// in a Task_locker. This method does not need to do anything if no
// locks are required. This method is only called with the
// workqueue lock held.
virtual void
locks(Task_locker*) = 0;
// Run the task.
virtual void
run(Workqueue*) = 0;
// Return whether this task should run soon.
bool
should_run_soon() const
{ return this->should_run_soon_; }
// Note that this task should run soon.
void
set_should_run_soon()
{ this->should_run_soon_ = true; }
// Get the next Task on the list of Tasks. Called by Task_list.
Task*
list_next() const
{ return this->list_next_; }
// Set the next Task on the list of Tasks. Called by Task_list.
void
set_list_next(Task* t)
{
gold_assert(this->list_next_ == NULL);
this->list_next_ = t;
}
// Clear the next Task on the list of Tasks. Called by Task_list.
void
clear_list_next()
{ this->list_next_ = NULL; }
// Return the name of the Task. This is only used for debugging
// purposes.
const std::string&
@ -325,15 +113,24 @@ class Task
get_name() const = 0;
private:
// This task may not be copied.
// Tasks may not be copied.
Task(const Task&);
Task& operator=(const Task&);
// If this Task is on a list, this is a pointer to the next Task on
// the list. We use this simple list structure rather than building
// a container, in order to avoid memory allocation while holding
// the Workqueue lock.
Task* list_next_;
// Task name, for debugging purposes.
std::string name_;
// Whether this Task should be executed soon. This is used for
// Tasks which can be run after some data is read.
bool should_run_soon_;
};
// A simple task which waits for a blocker and then runs a function.
// An interface for Task_function. This is a convenience class to run
// a single function.
class Task_function_runner
{
@ -342,14 +139,16 @@ class Task_function_runner
{ }
virtual void
run(Workqueue*) = 0;
run(Workqueue*, const Task*) = 0;
};
// A simple task which waits for a blocker and then runs a function.
class Task_function : public Task
{
public:
// Both points should be allocated using new, and will be deleted
// after the task runs.
// RUNNER and BLOCKER should be allocated using new, and will be
// deleted after the task runs.
Task_function(Task_function_runner* runner, Task_token* blocker,
const char* name)
: runner_(runner), blocker_(blocker), name_(name)
@ -364,19 +163,19 @@ class Task_function : public Task
// The standard task methods.
// Wait until the task is unblocked.
Is_runnable_type
is_runnable(Workqueue*)
{ return this->blocker_->is_blocked() ? IS_BLOCKED : IS_RUNNABLE; }
Task_token*
is_runnable()
{ return this->blocker_->is_blocked() ? this->blocker_ : NULL; }
// This type of task does not normally hold any locks.
virtual Task_locker*
locks(Workqueue*)
{ return NULL; }
virtual void
locks(Task_locker*)
{ }
// Run the action.
void
run(Workqueue* workqueue)
{ this->runner_->run(workqueue); }
{ this->runner_->run(workqueue, this); }
// The debugging name.
std::string
@ -392,9 +191,9 @@ class Task_function : public Task
const char* name_;
};
// The workqueue
// The workqueue itself.
class Workqueue_runner;
class Workqueue_threader;
class Workqueue
{
@ -411,15 +210,14 @@ class Workqueue
void
queue_front(Task*);
// Process all the tasks on the work queue.
// Process all the tasks on the work queue. This function runs
// until all tasks have completed. The argument is the thread
// number, used only for debugging.
void
process();
process(int);
// A complete set of blocking tasks has completed.
void
cleared_blocker();
// Set the thread count.
// Set the desired thread count--the number of threads we want to
// have running.
void
set_thread_count(int);
@ -428,59 +226,56 @@ class Workqueue
Workqueue(const Workqueue&);
Workqueue& operator=(const Workqueue&);
typedef std::list<Task*> Task_list;
// Run a task.
// Add a task to a queue.
void
run(Task*);
add_to_queue(Task_list* queue, Task* t);
friend class Workqueue_runner;
// Find a runnable task, or wait for one.
Task*
find_runnable_or_wait(int thread_number);
// Find a runnable task.
Task*
find_runnable(Task_list*, bool*);
find_runnable();
// Add a lock to the completed queue.
void
completed(Task*, Task_locker*);
// Find a runnable task in a list.
Task*
find_runnable_in_list(Task_list*);
// Clear the completed queue.
// Find an run a task.
bool
clear_completed();
find_and_run_task(int);
// Print the list of queued tasks.
void
show_queued_tasks();
// Release the locks for a Task. Return the next Task to run.
Task*
release_locks(Task*, Task_locker*);
// How to run a task. Only accessed from main thread.
Workqueue_runner* runner_;
// Store T into *PRET, or queue it as appropriate.
bool
return_or_queue(Task* t, bool is_blocker, Task** pret);
// Lock for access to tasks_ members.
Lock tasks_lock_;
// Return whether to cancel this thread.
bool
should_cancel_thread();
// Master Workqueue lock. This controls access to the following
// member variables.
Lock lock_;
// 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_, 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 tasks waiting for a lock to release.
int waiting_;
// Condition variable associated with lock_. This is signalled when
// there may be a new Task to execute.
Condvar 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_;
// The threading implementation. This is set at construction time
// and not changed thereafter.
Workqueue_threader* threader_;
};
} // End namespace gold.