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