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