| // workqueue.h -- the work queue for gold -*- C++ -*- |
| |
| // Copyright (C) 2006-2024 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. |
| |
| // 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 <string> |
| |
| #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) |