diff options
author | Ian Lance Taylor <iant@google.com> | 2006-08-04 23:10:59 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2006-08-04 23:10:59 +0000 |
commit | bae7f79e03d6405f5ceec0e3e24671e6b30f29ed (patch) | |
tree | 4b9df8c6433411b45963dd75e3a6dcad9a22518e /gold/workqueue.h | |
parent | c17d87de17351aed016a9d0b0712cdee4cca5f9e (diff) | |
download | gdb-bae7f79e03d6405f5ceec0e3e24671e6b30f29ed.zip gdb-bae7f79e03d6405f5ceec0e3e24671e6b30f29ed.tar.gz gdb-bae7f79e03d6405f5ceec0e3e24671e6b30f29ed.tar.bz2 |
Initial CVS checkin of gold
Diffstat (limited to 'gold/workqueue.h')
-rw-r--r-- | gold/workqueue.h | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/gold/workqueue.h b/gold/workqueue.h new file mode 100644 index 0000000..5949cf5 --- /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) |