summaryrefslogtreecommitdiff
path: root/gold/workqueue.h
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2006-08-04 23:10:59 +0000
committerIan Lance Taylor <iant@google.com>2006-08-04 23:10:59 +0000
commitbae7f79e03d6405f5ceec0e3e24671e6b30f29ed (patch)
tree4b9df8c6433411b45963dd75e3a6dcad9a22518e /gold/workqueue.h
parentc17d87de17351aed016a9d0b0712cdee4cca5f9e (diff)
downloadbinutils-gdb-bae7f79e03d6405f5ceec0e3e24671e6b30f29ed.tar.gz
Initial CVS checkin of gold
Diffstat (limited to 'gold/workqueue.h')
-rw-r--r--gold/workqueue.h336
1 files changed, 336 insertions, 0 deletions
diff --git a/gold/workqueue.h b/gold/workqueue.h
new file mode 100644
index 00000000000..5949cf5f439
--- /dev/null
+++ b/gold/workqueue.h
@@ -0,0 +1,336 @@
+// workqueue.h -- the work queue for gold -*- C++ -*-
+
+// After processing the command line, everything the linker does is
+// driven from a work queue. This permits us to parallelize the
+// linker where possible.
+
+// Task_token
+// A simple locking implementation to ensure proper task ordering.
+// Task_read_token, Task_write_token
+// Lock a Task_token for read or write.
+// Task_locker
+// Task locking using RAII.
+// Task
+// An abstract class for jobs to run.
+
+#ifndef GOLD_WORKQUEUE_H
+#define GOLD_WORKQUEUE_H
+
+#include "gold-threads.h"
+#include "options.h"
+#include "fileread.h"
+
+namespace gold
+{
+
+class Task;
+class Workqueue;
+
+// Some tasks require access to shared data structures, such as the
+// symbol table. Some tasks must be executed in a particular error,
+// such as reading input file symbol tables--if we see foo.o -llib, we
+// have to read the symbols for foo.o before we read the ones for
+// -llib. To implement this safely and efficiently, we use tokens.
+// Task_tokens support shared read/exclusive write access to some
+// resource. Alternatively, they support blockers: blockers implement
+// the requirement that some set of tasks must complete before another
+// set of tasks can start. In such a case we increment the block
+// count when we create the task, and decrement it when the task
+// completes. Task_tokens are only manipulated by the main thread, so
+// they do not themselves require any locking.
+
+class Task_token
+{
+ public:
+ Task_token();
+
+ ~Task_token();
+
+ // A read/write token uses these methods.
+
+ bool
+ is_readable() const;
+
+ void
+ add_reader();
+
+ void
+ remove_reader();
+
+ bool
+ is_writable() const;
+
+ void
+ add_writer(const Task*);
+
+ void
+ remove_writer(const Task*);
+
+ bool
+ has_write_lock(const Task*);
+
+ // A blocker token uses these methods.
+
+ void
+ add_blocker();
+
+ // Returns true if block count drops to zero.
+ bool
+ remove_blocker();
+
+ bool
+ is_blocked() const;
+
+ private:
+ // It makes no sense to copy these.
+ Task_token(const Task_token&);
+ Task_token& operator=(const Task_token&);
+
+ bool is_blocker_;
+ int readers_;
+ const Task* writer_;
+};
+
+// In order to support tokens more reliably, we provide objects which
+// handle them using RAII.
+
+class Task_read_token
+{
+ public:
+ Task_read_token(Task_token& token)
+ : token_(token)
+ { this->token_.add_reader(); }
+
+ ~Task_read_token()
+ { this->token_.remove_reader(); }
+
+ private:
+ Task_read_token(const Task_read_token&);
+ Task_read_token& operator=(const Task_read_token&);
+
+ Task_token& token_;
+};
+
+class Task_write_token
+{
+ public:
+ Task_write_token(Task_token& token, const Task* task)
+ : token_(token), task_(task)
+ { this->token_.add_writer(this->task_); }
+
+ ~Task_write_token()
+ { this->token_.remove_writer(this->task_); }
+
+ private:
+ Task_write_token(const Task_write_token&);
+ Task_write_token& operator=(const Task_write_token&);
+
+ Task_token& token_;
+ const Task* task_;
+};
+
+class Task_block_token
+{
+ public:
+ // The blocker count must be incremented when the task is created.
+ // This object is created when the task is run. When we unblock the
+ // last task, we notify the workqueue.
+ Task_block_token(Task_token& token, Workqueue* workqueue);
+ ~Task_block_token();
+
+ private:
+ Task_block_token(const Task_block_token&);
+ Task_block_token& operator=(const Task_block_token&);
+
+ Task_token& token_;
+ Workqueue* workqueue_;
+};
+
+// An abstract class used to lock Task_tokens using RAII. A typical
+// implementation would simply have a set of members of type
+// Task_read_token, Task_write_token, and Task_block_token.
+
+class Task_locker
+{
+ public:
+ Task_locker()
+ { }
+
+ virtual ~Task_locker()
+ { }
+};
+
+// A version of Task_locker which may be used for a single read lock.
+
+class Task_locker_read : public Task_locker
+{
+ public:
+ Task_locker_read(Task_token& token)
+ : read_token_(token)
+ { }
+
+ private:
+ Task_locker_read(const Task_locker_read&);
+ Task_locker_read& operator=(const Task_locker_read&);
+
+ Task_read_token read_token_;
+};
+
+// A version of Task_locker which may be used for a single write lock.
+
+class Task_locker_write : public Task_locker
+{
+ public:
+ Task_locker_write(Task_token& token, const Task* task)
+ : write_token_(token, task)
+ { }
+
+ private:
+ Task_locker_write(const Task_locker_write&);
+ Task_locker_write& operator=(const Task_locker_write&);
+
+ Task_write_token write_token_;
+};
+
+// A version of Task_locker which may be used for a single blocker
+// lock.
+
+class Task_locker_block : public Task_locker
+{
+ public:
+ Task_locker_block(Task_token& token, Workqueue* workqueue)
+ : block_token_(token, workqueue)
+ { }
+
+ private:
+ Task_locker_block(const Task_locker_block&);
+ Task_locker_block& operator=(const Task_locker_block&);
+
+ Task_block_token block_token_;
+};
+
+// A version of Task_locker which may be used to hold a lock on a
+// File_read.
+
+class Task_locker_file : public Task_locker
+{
+ public:
+ Task_locker_file(File_read& file)
+ : file_lock_(file)
+ { }
+
+ private:
+ Task_locker_file(const Task_locker_file&);
+ Task_locker_file& operator=(const Task_locker_file&);
+
+ File_read_lock file_lock_;
+};
+
+// The superclass for tasks to be placed on the workqueue. Each
+// specific task class will inherit from this one.
+
+class Task
+{
+ public:
+ Task()
+ { }
+ virtual ~Task()
+ { }
+
+ // Type returned by Is_runnable.
+ enum Is_runnable_type
+ {
+ // Task is runnable.
+ IS_RUNNABLE,
+ // Task is waiting for a block to clear.
+ IS_BLOCKED,
+ // Task is not waiting for a block, but is not runnable--i.e., is
+ // waiting for a lock.
+ IS_LOCKED
+ };
+
+ // Return whether the task can be run now. This method is only
+ // called from the main thread.
+ virtual Is_runnable_type
+ is_runnable(Workqueue*) = 0;
+
+ // Return a pointer to a Task_locker which locks all the resources
+ // required by the task. We delete the pointer when the task is
+ // complete. This method can return NULL if no locks are required.
+ // This method is only called from the main thread.
+ virtual Task_locker*
+ locks(Workqueue*) = 0;
+
+ // Run the task.
+ virtual void
+ run(Workqueue*) = 0;
+};
+
+// The workqueue
+
+class Workqueue_runner;
+
+class Workqueue
+{
+ public:
+ Workqueue(const General_options&);
+ ~Workqueue();
+
+ // Add a new task to the work queue.
+ void
+ queue(Task*);
+
+ // Process all the tasks on the work queue.
+ void
+ process();
+
+ // A complete set of blocking tasks has completed.
+ void
+ cleared_blocker();
+
+ private:
+ // This class can not be copied.
+ Workqueue(const Workqueue&);
+ Workqueue& operator=(const Workqueue&);
+
+ typedef std::list<Task*> Task_list;
+
+ // Run a task.
+ void run(Task*);
+
+ friend class Workqueue_runner;
+
+ // Find a runnable task.
+ Task* find_runnable(Task_list&, bool*);
+
+ // Add a lock to the completed queue.
+ void completed(Task*, Task_locker*);
+
+ // Clear the completed queue.
+ bool clear_completed();
+
+ // How to run a task. Only accessed from main thread.
+ Workqueue_runner* runner_;
+
+ // Lock for access to tasks_ members.
+ Lock tasks_lock_;
+ // List of tasks to execute at each link level.
+ Task_list tasks_;
+
+ // Lock for access to completed_ and running_ members.
+ Lock completed_lock_;
+ // List of Task_locker objects for main thread to free.
+ std::list<Task_locker*> completed_;
+ // Number of tasks currently running.
+ int running_;
+ // Condition variable signalled when a new entry is added to completed_.
+ Condvar completed_condvar_;
+
+ // Number of blocker tokens which were fully cleared. Only accessed
+ // from main thread.
+ int cleared_blockers_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_WORKQUEUE_H)