xref: /dflybsd-src/contrib/binutils-2.34/gold/token.h (revision b52ef7118d1621abed722c5bbbd542210290ecef)
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