// token.h -- lock tokens for gold -*- C++ -*-// Copyright 2006, 2007, 2008 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.#ifndef GOLD_TOKEN_H#define GOLD_TOKEN_Hnamespace gold{class Condvar;class Task;// A list of Tasks, managed through the next_locked_ field in the// class Task. We define this class here because we need it in// Task_token.class Task_list{public:Task_list(): head_(NULL), tail_(NULL){ }~Task_list(){ gold_assert(this->head_ == NULL && this->tail_ == NULL); }// Return whether the list is empty.boolempty() const{ return this->head_ == NULL; }// Add T to the head of the list.voidpush_front(Task* t);// Add T to the end of the list.voidpush_back(Task* t);// Remove the first Task on the list and return it. Return NULL if// the list is empty.Task*pop_front();private:// The start of the list. NULL if the list is empty.Task* head_;// The end of the list. NULL if the list is empty.Task* tail_;};// We support two basic types of locks, which are both implemented// using the single class Task_token.// A write lock may be held by a single Task at a time. This is used// to control access to a single shared resource such as an Object.// A blocker is used to indicate that a Task A must be run after some// set of Tasks B. For each of the Tasks B, we increment the blocker// when the Task is created, and decrement it when the Task is// completed. When the count goes to 0, the task A is ready to run.// There are no shared read locks. We always read and write objects// in predictable patterns. The purpose of the locks is to permit// some flexibility for the threading system, for cases where the// execution order does not matter.// These tokens are only manipulated when the workqueue lock is held// or when they are first created. They do not require any locking// themselves.class Task_token{public:Task_token(bool is_blocker): is_blocker_(is_blocker), blockers_(0), writer_(NULL), waiting_(){ }~Task_token(){gold_assert(this->blockers_ == 0);gold_assert(this->writer_ == NULL);}// Return whether this is a blocker.boolis_blocker() const{ return this->is_blocker_; }// A write lock token uses these methods.// Is the token writable?boolis_writable() const{gold_assert(!this->is_blocker_);return this->writer_ == NULL;}// Add the task as the token's writer (there may only be one// writer).voidadd_writer(const Task* t){gold_assert(!this->is_blocker_ && this->writer_ == NULL);this->writer_ = t;}// Remove the task as the token's writer.voidremove_writer(const Task* t){gold_assert(!this->is_blocker_ && this->writer_ == t);this->writer_ = NULL;}// A blocker token uses these methods.// Add a blocker to the token.voidadd_blocker(){gold_assert(this->is_blocker_);++this->blockers_;this->writer_ = NULL;}// Remove a blocker from the token. Returns true if block count// drops to zero.boolremove_blocker(){gold_assert(this->is_blocker_ && this->blockers_ > 0);--this->blockers_;this->writer_ = NULL;return this->blockers_ == 0;}// Is the token currently blocked?boolis_blocked() const{gold_assert(this->is_blocker_);return this->blockers_ > 0;}// Both blocker and write lock tokens use these methods.// Add T to the list of tasks waiting for this token to be released.voidadd_waiting(Task* t){ this->waiting_.push_back(t); }// Add T to the front of the list of tasks waiting for this token to// be released.voidadd_waiting_front(Task* t){ this->waiting_.push_front(t); }// Remove the first Task waiting for this token to be released, and// return it. Return NULL if no Tasks are waiting.Task*remove_first_waiting(){ return this->waiting_.pop_front(); }private:// It makes no sense to copy these.Task_token(const Task_token&);Task_token& operator=(const Task_token&);// Whether this is a blocker token.bool is_blocker_;// The number of blockers.int blockers_;// The single writer.const Task* writer_;// The list of Tasks waiting for this token to be released.Task_list waiting_;};// In order to support tokens more reliably, we provide objects which// handle them using RAII.// RAII class to get a write lock on a token. This requires// specifying the task which is doing the lock.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_;};// RAII class for a blocker.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, so we don't do// anything in the constructor.Task_block_token(Task_token* token): token_(token){ gold_assert(this->token_->is_blocked()); }~Task_block_token(){ this->token_->remove_blocker(); }private:Task_block_token(const Task_block_token&);Task_block_token& operator=(const Task_block_token&);Task_token* token_;};// An object which implements an RAII lock for any object which// supports lock and unlock methods.template<typename Obj>class Task_lock_obj{public:Task_lock_obj(const Task* task, Obj* obj): task_(task), obj_(obj){ this->obj_->lock(task); }~Task_lock_obj(){ this->obj_->unlock(this->task_); }private:Task_lock_obj(const Task_lock_obj&);Task_lock_obj& operator=(const Task_lock_obj&);const Task* task_;Obj* obj_;};// A class which holds the set of Task_tokens which must be locked for// a Task. No Task requires more than four Task_tokens, so we set// that as a limit.class Task_locker{public:static const int max_task_count = 4;Task_locker(): count_(0){ }~Task_locker(){ }// Clear the locker.voidclear(){ this->count_ = 0; }// Add a token to the locker.voidadd(Task* t, Task_token* token){gold_assert(this->count_ < max_task_count);this->tokens_[this->count_] = token;++this->count_;// A blocker will have been incremented when the task is created.// A writer we need to lock now.if (!token->is_blocker())token->add_writer(t);}// Iterate over the tokens.typedef Task_token** iterator;iteratorbegin(){ return &this->tokens_[0]; }iteratorend(){ return &this->tokens_[this->count_]; }private:Task_locker(const Task_locker&);Task_locker& operator=(const Task_locker&);// The number of tokens.int count_;// The tokens.Task_token* tokens_[max_task_count];};} // End namespace gold.#endif // !defined(GOLD_TOKEN_H)