// workqueue.h -- the work queue for gold -*- C++ -*- // Copyright (C) 2006-2014 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) { gold_assert(blocker != NULL); } ~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(int thread_number); // 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)