1*fae548d3Szrj // token.h -- lock tokens for gold -*- C++ -*- 2*fae548d3Szrj 3*fae548d3Szrj // Copyright (C) 2006-2020 Free Software Foundation, Inc. 4*fae548d3Szrj // Written by Ian Lance Taylor <iant@google.com>. 5*fae548d3Szrj 6*fae548d3Szrj // This file is part of gold. 7*fae548d3Szrj 8*fae548d3Szrj // This program is free software; you can redistribute it and/or modify 9*fae548d3Szrj // it under the terms of the GNU General Public License as published by 10*fae548d3Szrj // the Free Software Foundation; either version 3 of the License, or 11*fae548d3Szrj // (at your option) any later version. 12*fae548d3Szrj 13*fae548d3Szrj // This program is distributed in the hope that it will be useful, 14*fae548d3Szrj // but WITHOUT ANY WARRANTY; without even the implied warranty of 15*fae548d3Szrj // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*fae548d3Szrj // GNU General Public License for more details. 17*fae548d3Szrj 18*fae548d3Szrj // You should have received a copy of the GNU General Public License 19*fae548d3Szrj // along with this program; if not, write to the Free Software 20*fae548d3Szrj // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 21*fae548d3Szrj // MA 02110-1301, USA. 22*fae548d3Szrj 23*fae548d3Szrj #ifndef GOLD_TOKEN_H 24*fae548d3Szrj #define GOLD_TOKEN_H 25*fae548d3Szrj 26*fae548d3Szrj namespace gold 27*fae548d3Szrj { 28*fae548d3Szrj 29*fae548d3Szrj class Condvar; 30*fae548d3Szrj class Task; 31*fae548d3Szrj 32*fae548d3Szrj // A list of Tasks, managed through the next_locked_ field in the 33*fae548d3Szrj // class Task. We define this class here because we need it in 34*fae548d3Szrj // Task_token. 35*fae548d3Szrj 36*fae548d3Szrj class Task_list 37*fae548d3Szrj { 38*fae548d3Szrj public: Task_list()39*fae548d3Szrj Task_list() 40*fae548d3Szrj : head_(NULL), tail_(NULL) 41*fae548d3Szrj { } 42*fae548d3Szrj ~Task_list()43*fae548d3Szrj ~Task_list() 44*fae548d3Szrj { gold_assert(this->head_ == NULL && this->tail_ == NULL); } 45*fae548d3Szrj 46*fae548d3Szrj // Return whether the list is empty. 47*fae548d3Szrj bool empty()48*fae548d3Szrj empty() const 49*fae548d3Szrj { return this->head_ == NULL; } 50*fae548d3Szrj 51*fae548d3Szrj // Add T to the head of the list. 52*fae548d3Szrj void 53*fae548d3Szrj push_front(Task* t); 54*fae548d3Szrj 55*fae548d3Szrj // Add T to the end of the list. 56*fae548d3Szrj void 57*fae548d3Szrj push_back(Task* t); 58*fae548d3Szrj 59*fae548d3Szrj // Remove the first Task on the list and return it. Return NULL if 60*fae548d3Szrj // the list is empty. 61*fae548d3Szrj Task* 62*fae548d3Szrj pop_front(); 63*fae548d3Szrj 64*fae548d3Szrj private: 65*fae548d3Szrj // The start of the list. NULL if the list is empty. 66*fae548d3Szrj Task* head_; 67*fae548d3Szrj // The end of the list. NULL if the list is empty. 68*fae548d3Szrj Task* tail_; 69*fae548d3Szrj }; 70*fae548d3Szrj 71*fae548d3Szrj // We support two basic types of locks, which are both implemented 72*fae548d3Szrj // using the single class Task_token. 73*fae548d3Szrj 74*fae548d3Szrj // A write lock may be held by a single Task at a time. This is used 75*fae548d3Szrj // to control access to a single shared resource such as an Object. 76*fae548d3Szrj 77*fae548d3Szrj // A blocker is used to indicate that a Task A must be run after some 78*fae548d3Szrj // set of Tasks B. For each of the Tasks B, we increment the blocker 79*fae548d3Szrj // when the Task is created, and decrement it when the Task is 80*fae548d3Szrj // completed. When the count goes to 0, the task A is ready to run. 81*fae548d3Szrj 82*fae548d3Szrj // There are no shared read locks. We always read and write objects 83*fae548d3Szrj // in predictable patterns. The purpose of the locks is to permit 84*fae548d3Szrj // some flexibility for the threading system, for cases where the 85*fae548d3Szrj // execution order does not matter. 86*fae548d3Szrj 87*fae548d3Szrj // These tokens are only manipulated when the workqueue lock is held 88*fae548d3Szrj // or when they are first created. They do not require any locking 89*fae548d3Szrj // themselves. 90*fae548d3Szrj 91*fae548d3Szrj class Task_token 92*fae548d3Szrj { 93*fae548d3Szrj public: Task_token(bool is_blocker)94*fae548d3Szrj Task_token(bool is_blocker) 95*fae548d3Szrj : is_blocker_(is_blocker), blockers_(0), writer_(NULL), waiting_() 96*fae548d3Szrj { } 97*fae548d3Szrj ~Task_token()98*fae548d3Szrj ~Task_token() 99*fae548d3Szrj { 100*fae548d3Szrj gold_assert(this->blockers_ == 0); 101*fae548d3Szrj gold_assert(this->writer_ == NULL); 102*fae548d3Szrj } 103*fae548d3Szrj 104*fae548d3Szrj // Return whether this is a blocker. 105*fae548d3Szrj bool is_blocker()106*fae548d3Szrj is_blocker() const 107*fae548d3Szrj { return this->is_blocker_; } 108*fae548d3Szrj 109*fae548d3Szrj // A write lock token uses these methods. 110*fae548d3Szrj 111*fae548d3Szrj // Is the token writable? 112*fae548d3Szrj bool is_writable()113*fae548d3Szrj is_writable() const 114*fae548d3Szrj { 115*fae548d3Szrj gold_assert(!this->is_blocker_); 116*fae548d3Szrj return this->writer_ == NULL; 117*fae548d3Szrj } 118*fae548d3Szrj 119*fae548d3Szrj // Add the task as the token's writer (there may only be one 120*fae548d3Szrj // writer). 121*fae548d3Szrj void add_writer(const Task * t)122*fae548d3Szrj add_writer(const Task* t) 123*fae548d3Szrj { 124*fae548d3Szrj gold_assert(!this->is_blocker_ && this->writer_ == NULL); 125*fae548d3Szrj this->writer_ = t; 126*fae548d3Szrj } 127*fae548d3Szrj 128*fae548d3Szrj // Remove the task as the token's writer. 129*fae548d3Szrj void remove_writer(const Task * t)130*fae548d3Szrj remove_writer(const Task* t) 131*fae548d3Szrj { 132*fae548d3Szrj gold_assert(!this->is_blocker_ && this->writer_ == t); 133*fae548d3Szrj this->writer_ = NULL; 134*fae548d3Szrj } 135*fae548d3Szrj 136*fae548d3Szrj // A blocker token uses these methods. 137*fae548d3Szrj 138*fae548d3Szrj // Add a blocker to the token. 139*fae548d3Szrj void add_blocker()140*fae548d3Szrj add_blocker() 141*fae548d3Szrj { 142*fae548d3Szrj gold_assert(this->is_blocker_); 143*fae548d3Szrj ++this->blockers_; 144*fae548d3Szrj this->writer_ = NULL; 145*fae548d3Szrj } 146*fae548d3Szrj 147*fae548d3Szrj // Add some number of blockers to the token. 148*fae548d3Szrj void add_blockers(int c)149*fae548d3Szrj add_blockers(int c) 150*fae548d3Szrj { 151*fae548d3Szrj gold_assert(this->is_blocker_); 152*fae548d3Szrj this->blockers_ += c; 153*fae548d3Szrj this->writer_ = NULL; 154*fae548d3Szrj } 155*fae548d3Szrj 156*fae548d3Szrj // Remove a blocker from the token. Returns true if block count 157*fae548d3Szrj // drops to zero. 158*fae548d3Szrj bool remove_blocker()159*fae548d3Szrj remove_blocker() 160*fae548d3Szrj { 161*fae548d3Szrj gold_assert(this->is_blocker_ && this->blockers_ > 0); 162*fae548d3Szrj --this->blockers_; 163*fae548d3Szrj this->writer_ = NULL; 164*fae548d3Szrj return this->blockers_ == 0; 165*fae548d3Szrj } 166*fae548d3Szrj 167*fae548d3Szrj // Is the token currently blocked? 168*fae548d3Szrj bool is_blocked()169*fae548d3Szrj is_blocked() const 170*fae548d3Szrj { 171*fae548d3Szrj gold_assert(this->is_blocker_); 172*fae548d3Szrj return this->blockers_ > 0; 173*fae548d3Szrj } 174*fae548d3Szrj 175*fae548d3Szrj // Both blocker and write lock tokens use these methods. 176*fae548d3Szrj 177*fae548d3Szrj // Add T to the list of tasks waiting for this token to be released. 178*fae548d3Szrj void add_waiting(Task * t)179*fae548d3Szrj add_waiting(Task* t) 180*fae548d3Szrj { this->waiting_.push_back(t); } 181*fae548d3Szrj 182*fae548d3Szrj // Add T to the front of the list of tasks waiting for this token to 183*fae548d3Szrj // be released. 184*fae548d3Szrj void add_waiting_front(Task * t)185*fae548d3Szrj add_waiting_front(Task* t) 186*fae548d3Szrj { this->waiting_.push_front(t); } 187*fae548d3Szrj 188*fae548d3Szrj // Remove the first Task waiting for this token to be released, and 189*fae548d3Szrj // return it. Return NULL if no Tasks are waiting. 190*fae548d3Szrj Task* remove_first_waiting()191*fae548d3Szrj remove_first_waiting() 192*fae548d3Szrj { return this->waiting_.pop_front(); } 193*fae548d3Szrj 194*fae548d3Szrj private: 195*fae548d3Szrj // It makes no sense to copy these. 196*fae548d3Szrj Task_token(const Task_token&); 197*fae548d3Szrj Task_token& operator=(const Task_token&); 198*fae548d3Szrj 199*fae548d3Szrj // Whether this is a blocker token. 200*fae548d3Szrj bool is_blocker_; 201*fae548d3Szrj // The number of blockers. 202*fae548d3Szrj int blockers_; 203*fae548d3Szrj // The single writer. 204*fae548d3Szrj const Task* writer_; 205*fae548d3Szrj // The list of Tasks waiting for this token to be released. 206*fae548d3Szrj Task_list waiting_; 207*fae548d3Szrj }; 208*fae548d3Szrj 209*fae548d3Szrj // In order to support tokens more reliably, we provide objects which 210*fae548d3Szrj // handle them using RAII. 211*fae548d3Szrj 212*fae548d3Szrj // RAII class to get a write lock on a token. This requires 213*fae548d3Szrj // specifying the task which is doing the lock. 214*fae548d3Szrj 215*fae548d3Szrj class Task_write_token 216*fae548d3Szrj { 217*fae548d3Szrj public: Task_write_token(Task_token * token,const Task * task)218*fae548d3Szrj Task_write_token(Task_token* token, const Task* task) 219*fae548d3Szrj : token_(token), task_(task) 220*fae548d3Szrj { this->token_->add_writer(this->task_); } 221*fae548d3Szrj ~Task_write_token()222*fae548d3Szrj ~Task_write_token() 223*fae548d3Szrj { this->token_->remove_writer(this->task_); } 224*fae548d3Szrj 225*fae548d3Szrj private: 226*fae548d3Szrj Task_write_token(const Task_write_token&); 227*fae548d3Szrj Task_write_token& operator=(const Task_write_token&); 228*fae548d3Szrj 229*fae548d3Szrj Task_token* token_; 230*fae548d3Szrj const Task* task_; 231*fae548d3Szrj }; 232*fae548d3Szrj 233*fae548d3Szrj // RAII class for a blocker. 234*fae548d3Szrj 235*fae548d3Szrj class Task_block_token 236*fae548d3Szrj { 237*fae548d3Szrj public: 238*fae548d3Szrj // The blocker count must be incremented when the task is created. 239*fae548d3Szrj // This object is created when the task is run, so we don't do 240*fae548d3Szrj // anything in the constructor. Task_block_token(Task_token * token)241*fae548d3Szrj Task_block_token(Task_token* token) 242*fae548d3Szrj : token_(token) 243*fae548d3Szrj { gold_assert(this->token_->is_blocked()); } 244*fae548d3Szrj ~Task_block_token()245*fae548d3Szrj ~Task_block_token() 246*fae548d3Szrj { this->token_->remove_blocker(); } 247*fae548d3Szrj 248*fae548d3Szrj private: 249*fae548d3Szrj Task_block_token(const Task_block_token&); 250*fae548d3Szrj Task_block_token& operator=(const Task_block_token&); 251*fae548d3Szrj 252*fae548d3Szrj Task_token* token_; 253*fae548d3Szrj }; 254*fae548d3Szrj 255*fae548d3Szrj // An object which implements an RAII lock for any object which 256*fae548d3Szrj // supports lock and unlock methods. 257*fae548d3Szrj 258*fae548d3Szrj template<typename Obj> 259*fae548d3Szrj class Task_lock_obj 260*fae548d3Szrj { 261*fae548d3Szrj public: Task_lock_obj(const Task * task,Obj * obj)262*fae548d3Szrj Task_lock_obj(const Task* task, Obj* obj) 263*fae548d3Szrj : task_(task), obj_(obj) 264*fae548d3Szrj { this->obj_->lock(task); } 265*fae548d3Szrj ~Task_lock_obj()266*fae548d3Szrj ~Task_lock_obj() 267*fae548d3Szrj { this->obj_->unlock(this->task_); } 268*fae548d3Szrj 269*fae548d3Szrj private: 270*fae548d3Szrj Task_lock_obj(const Task_lock_obj&); 271*fae548d3Szrj Task_lock_obj& operator=(const Task_lock_obj&); 272*fae548d3Szrj 273*fae548d3Szrj const Task* task_; 274*fae548d3Szrj Obj* obj_; 275*fae548d3Szrj }; 276*fae548d3Szrj 277*fae548d3Szrj // A class which holds the set of Task_tokens which must be locked for 278*fae548d3Szrj // a Task. No Task requires more than four Task_tokens, so we set 279*fae548d3Szrj // that as a limit. 280*fae548d3Szrj 281*fae548d3Szrj class Task_locker 282*fae548d3Szrj { 283*fae548d3Szrj public: 284*fae548d3Szrj static const int max_task_count = 4; 285*fae548d3Szrj Task_locker()286*fae548d3Szrj Task_locker() 287*fae548d3Szrj : count_(0) 288*fae548d3Szrj { } 289*fae548d3Szrj ~Task_locker()290*fae548d3Szrj ~Task_locker() 291*fae548d3Szrj { } 292*fae548d3Szrj 293*fae548d3Szrj // Clear the locker. 294*fae548d3Szrj void clear()295*fae548d3Szrj clear() 296*fae548d3Szrj { this->count_ = 0; } 297*fae548d3Szrj 298*fae548d3Szrj // Add a token to the locker. 299*fae548d3Szrj void add(Task * t,Task_token * token)300*fae548d3Szrj add(Task* t, Task_token* token) 301*fae548d3Szrj { 302*fae548d3Szrj gold_assert(this->count_ < max_task_count); 303*fae548d3Szrj this->tokens_[this->count_] = token; 304*fae548d3Szrj ++this->count_; 305*fae548d3Szrj // A blocker will have been incremented when the task is created. 306*fae548d3Szrj // A writer we need to lock now. 307*fae548d3Szrj if (!token->is_blocker()) 308*fae548d3Szrj token->add_writer(t); 309*fae548d3Szrj } 310*fae548d3Szrj 311*fae548d3Szrj // Iterate over the tokens. 312*fae548d3Szrj 313*fae548d3Szrj typedef Task_token** iterator; 314*fae548d3Szrj 315*fae548d3Szrj iterator begin()316*fae548d3Szrj begin() 317*fae548d3Szrj { return &this->tokens_[0]; } 318*fae548d3Szrj 319*fae548d3Szrj iterator end()320*fae548d3Szrj end() 321*fae548d3Szrj { return &this->tokens_[this->count_]; } 322*fae548d3Szrj 323*fae548d3Szrj private: 324*fae548d3Szrj Task_locker(const Task_locker&); 325*fae548d3Szrj Task_locker& operator=(const Task_locker&); 326*fae548d3Szrj 327*fae548d3Szrj // The number of tokens. 328*fae548d3Szrj int count_; 329*fae548d3Szrj // The tokens. 330*fae548d3Szrj Task_token* tokens_[max_task_count]; 331*fae548d3Szrj }; 332*fae548d3Szrj 333*fae548d3Szrj } // End namespace gold. 334*fae548d3Szrj 335*fae548d3Szrj #endif // !defined(GOLD_TOKEN_H) 336