// workqueue.h -- the work queue for gold   -*- C++ -*-

// Copyright (C) 2006-2024 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <[email protected]>.

// 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)