// workqueue.cc -- the workqueue for gold

// Copyright 2006, 2007 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.

#include "gold.h"

#include "workqueue.h"

namespace gold
{

// Task_token methods.

Task_token::Task_token()
  : is_blocker_(false), readers_(0), writer_(NULL)
{
}

Task_token::~Task_token()
{
  gold_assert(this->readers_ == 0 && this->writer_ == NULL);
}

bool
Task_token::is_readable() const
{
  gold_assert(!this->is_blocker_);
  return this->writer_ == NULL;
}

void
Task_token::add_reader()
{
  gold_assert(!this->is_blocker_);
  gold_assert(this->is_readable());
  ++this->readers_;
}

void
Task_token::remove_reader()
{
  gold_assert(!this->is_blocker_);
  gold_assert(this->readers_ > 0);
  --this->readers_;
}

bool
Task_token::is_writable() const
{
  gold_assert(!this->is_blocker_);
  return this->writer_ == NULL && this->readers_ == 0;
}

void
Task_token::add_writer(const Task* t)
{
  gold_assert(!this->is_blocker_);
  gold_assert(this->is_writable());
  this->writer_ = t;
}

void
Task_token::remove_writer(const Task* t)
{
  gold_assert(!this->is_blocker_);
  gold_assert(this->writer_ == t);
  this->writer_ = NULL;
}

bool
Task_token::has_write_lock(const Task* t)
{
  gold_assert(!this->is_blocker_);
  return this->writer_ == t;
}

// For blockers, we just use the readers_ field.

void
Task_token::add_blocker()
{
  if (this->readers_ == 0 && this->writer_ == NULL)
    this->is_blocker_ = true;
  else
    gold_assert(this->is_blocker_);
  ++this->readers_;
}

bool
Task_token::remove_blocker()
{
  gold_assert(this->is_blocker_ && this->readers_ > 0);
  --this->readers_;
  return this->readers_ == 0;
}

bool
Task_token::is_blocked() const
{
  gold_assert(this->is_blocker_
	      || (this->readers_ == 0 && this->writer_ == NULL));
  return this->readers_ > 0;
}

// The Task_block_token class.

Task_block_token::Task_block_token(Task_token& token, Workqueue* workqueue)
  : token_(token), workqueue_(workqueue)
{
  // We must increment the block count when the task is created and
  // put on the queue.  This object is created when the task is run,
  // so we don't increment the block count here.
  gold_assert(this->token_.is_blocked());
}

Task_block_token::~Task_block_token()
{
  if (this->token_.remove_blocker())
    {
      // Tell the workqueue that a blocker was cleared.  This is
      // always called in the main thread, so no locking is required.
      this->workqueue_->cleared_blocker();
    }
}

// The Workqueue_runner abstract class.

class Workqueue_runner
{
 public:
  Workqueue_runner(Workqueue* workqueue)
    : workqueue_(workqueue)
  { }
  virtual ~Workqueue_runner()
  { }

  // Run a task.  This is always called in the main thread.
  virtual void run(Task*, Task_locker*) = 0;

 protected:
  // This is called by an implementation when a task is completed.
  void completed(Task* t, Task_locker* tl)
  { this->workqueue_->completed(t, tl); }

  Workqueue* get_workqueue() const
  { return this->workqueue_; }

 private:
  Workqueue* workqueue_;
};

// The simple single-threaded implementation of Workqueue_runner.

class Workqueue_runner_single : public Workqueue_runner
{
 public:
  Workqueue_runner_single(Workqueue* workqueue)
    : Workqueue_runner(workqueue)
  { }
  ~Workqueue_runner_single()
  { }

  void run(Task*, Task_locker*);
};

void
Workqueue_runner_single::run(Task* t, Task_locker* tl)
{
  t->run(this->get_workqueue());
  this->completed(t, tl);
}

// Workqueue methods.

Workqueue::Workqueue(const General_options&)
  : tasks_lock_(),
    tasks_(),
    completed_lock_(),
    completed_(),
    running_(0),
    completed_condvar_(this->completed_lock_),
    cleared_blockers_(0)
{
  // At some point we will select the specific implementation of
  // Workqueue_runner to use based on the command line options.
  this->runner_ = new Workqueue_runner_single(this);
}

Workqueue::~Workqueue()
{
  gold_assert(this->tasks_.empty());
  gold_assert(this->completed_.empty());
  gold_assert(this->running_ == 0);
}

// Add a task to the queue.

void
Workqueue::queue(Task* t)
{
  Hold_lock hl(this->tasks_lock_);
  this->tasks_.push_back(t);
}

// Add a task to the front of the queue.

void
Workqueue::queue_front(Task* t)
{
  Hold_lock hl(this->tasks_lock_);
  this->tasks_.push_front(t);
}

// Clear the list of completed tasks.  Return whether we cleared
// anything.  The completed_lock_ must be held when this is called.

bool
Workqueue::clear_completed()
{
  if (this->completed_.empty())
    return false;
  do
    {
      delete this->completed_.front();
      this->completed_.pop_front();
    }
  while (!this->completed_.empty());
  return true;
}

// Find a runnable task in TASKS, which is non-empty.  Return NULL if
// none could be found.  The tasks_lock_ must be held when this is
// called.  Sets ALL_BLOCKED if all non-runnable tasks are waiting on
// a blocker.

Task*
Workqueue::find_runnable(Task_list& tasks, bool* all_blocked)
{
  Task* tlast = tasks.back();
  *all_blocked = true;
  while (true)
    {
      Task* t = tasks.front();
      tasks.pop_front();

      Task::Is_runnable_type is_runnable = t->is_runnable(this);
      if (is_runnable == Task::IS_RUNNABLE)
	return t;

      if (is_runnable != Task::IS_BLOCKED)
	*all_blocked = false;

      tasks.push_back(t);

      if (t == tlast)
	{
	  // We couldn't find any runnable task.  If there are any
	  // completed tasks, free their locks and try again.

	  {
	    Hold_lock hl2(this->completed_lock_);

	    if (!this->clear_completed())
	      {
		// There had better be some tasks running, or we will
		// never find a runnable task.
		gold_assert(this->running_ > 0);

		// We couldn't find any runnable tasks, and we
		// couldn't release any locks.
		return NULL;
	      }
	  }

	  // We're going around again, so recompute ALL_BLOCKED.
	  *all_blocked = true;
	}
    }
}

// Process all the tasks on the workqueue.  This is the main loop in
// the linker.  Note that as we process tasks, new tasks will be
// added.

void
Workqueue::process()
{
  while (true)
    {
      Task* t;
      bool empty;
      bool all_blocked;

      {
	Hold_lock hl(this->tasks_lock_);

	if (this->tasks_.empty())
	  {
	    t = NULL;
	    empty = true;
	    all_blocked = false;
	  }
	else
	  {
	    t = this->find_runnable(this->tasks_, &all_blocked);
	    empty = false;
	  }
      }

      // If T != NULL, it is a task we can run.
      // If T == NULL && empty, then there are no tasks waiting to
      // be run at this level.
      // If T == NULL && !empty, then there tasks waiting to be
      // run at this level, but they are waiting for something to
      // unlock.

      if (t != NULL)
	this->run(t);
      else if (!empty)
	{
	  {
	    Hold_lock hl(this->completed_lock_);

	    // There must be something for us to wait for, or we won't
	    // be able to make progress.
	    gold_assert(this->running_ > 0 || !this->completed_.empty());

	    if (all_blocked)
	      {
		this->cleared_blockers_ = 0;
		this->clear_completed();
		while (this->cleared_blockers_ == 0)
		  {
		    gold_assert(this->running_ > 0);
		    this->completed_condvar_.wait();
		    this->clear_completed();
		  }
	      }
	    else
	      {
		if (this->running_ > 0)
		  {
		    // Wait for a task to finish.
		    this->completed_condvar_.wait();
		  }
		this->clear_completed();
	      }
	  }
	}
      else
	{
	  {
	    Hold_lock hl(this->completed_lock_);

	    // If there are no running tasks, then we are done.
	    if (this->running_ == 0)
	      {
		this->clear_completed();
		return;
	      }

	    // Wait for a task to finish.  Then we have to loop around
	    // again in case it added any new tasks before finishing.
	    this->completed_condvar_.wait();
	    this->clear_completed();
	  }
	}
    }
}

// Run a task.  This is always called in the main thread.

void
Workqueue::run(Task* t)
{
  ++this->running_;
  this->runner_->run(t, t->locks(this));
}

// This is called when a task is completed to put the locks on the
// list to be released.  We use a list because we only want the locks
// to be released in the main thread.

void
Workqueue::completed(Task* t, Task_locker* tl)
{
  {
    Hold_lock hl(this->completed_lock_);
    gold_assert(this->running_ > 0);
    --this->running_;
    this->completed_.push_back(tl);
    this->completed_condvar_.signal();
  }
  delete t;
}

// This is called when the last task for a blocker has completed.
// This is always called in the main thread.

void
Workqueue::cleared_blocker()
{
  ++this->cleared_blockers_;
}

} // End namespace gold.