X-Git-Url: https://oss.titaniummirror.com/gitweb?p=msp430-binutils.git;a=blobdiff_plain;f=gold%2Fworkqueue.h;fp=gold%2Fworkqueue.h;h=75452241d80515f3d3dc6f2eff810c4ad25cf91c;hp=0000000000000000000000000000000000000000;hb=88750007d7869f178f0ba528f41efd3b74c424cf;hpb=6df9443a374e2b81278c61b8afc0a1eef7db280b diff --git a/gold/workqueue.h b/gold/workqueue.h new file mode 100644 index 0000000..7545224 --- /dev/null +++ b/gold/workqueue.h @@ -0,0 +1,295 @@ +// workqueue.h -- the work queue for gold -*- C++ -*- + +// Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// 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. + +// After processing the command line, everything the linker does is +// driven from a work queue. This permits us to parallelize the +// linker where possible. + +#ifndef GOLD_WORKQUEUE_H +#define GOLD_WORKQUEUE_H + +#include + +#include "gold-threads.h" +#include "token.h" + +namespace gold +{ + +class General_options; +class Workqueue; + +// The superclass for tasks to be placed on the workqueue. Each +// specific task class will inherit from this one. + +class Task +{ + public: + Task() + : list_next_(NULL), name_(), should_run_soon_(false) + { } + virtual ~Task() + { } + + // 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; + + // 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& + name() + { + if (this->name_.empty()) + this->name_ = this->get_name(); + return this->name_; + } + + protected: + // Get the name of the task. This must be implemented by the child + // class. + virtual std::string + get_name() const = 0; + + private: + // 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_; +}; + +// An interface for Task_function. This is a convenience class to run +// a single function. + +class Task_function_runner +{ + public: + virtual ~Task_function_runner() + { } + + virtual void + run(Workqueue*, const Task*) = 0; +}; + +// A simple task which waits for a blocker and then runs a function. + +class Task_function : public Task +{ + public: + // 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) + { } + + ~Task_function() + { + delete this->runner_; + delete this->blocker_; + } + + // The standard task methods. + + // Wait until the task is unblocked. + Task_token* + is_runnable() + { return this->blocker_->is_blocked() ? this->blocker_ : NULL; } + + // This type of task does not normally hold any locks. + virtual void + locks(Task_locker*) + { } + + // Run the action. + void + run(Workqueue* workqueue) + { this->runner_->run(workqueue, this); } + + // The debugging name. + std::string + get_name() const + { return this->name_; } + + private: + Task_function(const Task_function&); + Task_function& operator=(const Task_function&); + + Task_function_runner* runner_; + Task_token* blocker_; + const char* name_; +}; + +// The workqueue itself. + +class Workqueue_threader; + +class Workqueue +{ + public: + Workqueue(const General_options&); + ~Workqueue(); + + // Add a new task to the work queue. + void + queue(Task*); + + // Add a new task to the work queue which should run soon. If the + // task is ready, it will be run before any tasks added using + // queue(). + void + queue_soon(Task*); + + // Add a new task to the work queue which should run next if it is + // ready. + void + queue_next(Task*); + + // 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(int); + + // Set the desired thread count--the number of threads we want to + // have running. + void + set_thread_count(int); + + // Add a new blocker to an existing Task_token. This must be done + // with the workqueue lock held. This should not be done routinely, + // only in special circumstances. + void + add_blocker(Task_token*); + + private: + // This class can not be copied. + Workqueue(const Workqueue&); + Workqueue& operator=(const Workqueue&); + + // Add a task to a queue. + void + add_to_queue(Task_list* queue, Task* t, bool front); + + // Find a runnable task, or wait for one. + Task* + find_runnable_or_wait(int thread_number); + + // Find a runnable task. + Task* + find_runnable(); + + // Find a runnable task in a list. + Task* + find_runnable_in_list(Task_list*); + + // Find an run a task. + bool + find_and_run_task(int); + + // Release the locks for a Task. Return the next Task to run. + Task* + release_locks(Task*, Task_locker*); + + // Store T into *PRET, or queue it as appropriate. + bool + return_or_queue(Task* t, bool is_blocker, Task** pret); + + // 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_; + // Number of tasks currently running. + int running_; + // 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_; + + // The threading implementation. This is set at construction time + // and not changed thereafter. + Workqueue_threader* threader_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_WORKQUEUE_H)