]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gold/token.h
PR binutils/10924
[thirdparty/binutils-gdb.git] / gold / token.h
CommitLineData
17a1d0a9
ILT
1// token.h -- lock tokens for gold -*- C++ -*-
2
ebdbb458 3// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
17a1d0a9
ILT
4// Written by Ian Lance Taylor <iant@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
26namespace gold
27{
28
29class Condvar;
30class 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
36class 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
da769d56
ILT
51 // Add T to the head of the list.
52 void
53 push_front(Task* t);
54
17a1d0a9
ILT
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
91class Task_token
92{
93 public:
91d6fa6a
NC
94 Task_token(bool tis_blocker)
95 : is_blocker_(tis_blocker), blockers_(0), writer_(NULL), waiting_()
17a1d0a9
ILT
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 // Remove a blocker from the token. Returns true if block count
148 // drops to zero.
149 bool
150 remove_blocker()
151 {
152 gold_assert(this->is_blocker_ && this->blockers_ > 0);
153 --this->blockers_;
154 this->writer_ = NULL;
155 return this->blockers_ == 0;
156 }
157
158 // Is the token currently blocked?
159 bool
160 is_blocked() const
161 {
162 gold_assert(this->is_blocker_);
163 return this->blockers_ > 0;
164 }
165
166 // Both blocker and write lock tokens use these methods.
167
168 // Add T to the list of tasks waiting for this token to be released.
169 void
170 add_waiting(Task* t)
171 { this->waiting_.push_back(t); }
172
da769d56
ILT
173 // Add T to the front of the list of tasks waiting for this token to
174 // be released.
175 void
176 add_waiting_front(Task* t)
177 { this->waiting_.push_front(t); }
178
17a1d0a9
ILT
179 // Remove the first Task waiting for this token to be released, and
180 // return it. Return NULL if no Tasks are waiting.
181 Task*
182 remove_first_waiting()
183 { return this->waiting_.pop_front(); }
184
185 private:
186 // It makes no sense to copy these.
187 Task_token(const Task_token&);
188 Task_token& operator=(const Task_token&);
189
190 // Whether this is a blocker token.
191 bool is_blocker_;
192 // The number of blockers.
193 int blockers_;
194 // The single writer.
195 const Task* writer_;
196 // The list of Tasks waiting for this token to be released.
197 Task_list waiting_;
198};
199
200// In order to support tokens more reliably, we provide objects which
201// handle them using RAII.
202
203// RAII class to get a write lock on a token. This requires
204// specifying the task which is doing the lock.
205
206class Task_write_token
207{
208 public:
209 Task_write_token(Task_token* token, const Task* task)
210 : token_(token), task_(task)
211 { this->token_->add_writer(this->task_); }
212
213 ~Task_write_token()
214 { this->token_->remove_writer(this->task_); }
215
216 private:
217 Task_write_token(const Task_write_token&);
218 Task_write_token& operator=(const Task_write_token&);
219
220 Task_token* token_;
221 const Task* task_;
222};
223
224// RAII class for a blocker.
225
226class Task_block_token
227{
228 public:
229 // The blocker count must be incremented when the task is created.
230 // This object is created when the task is run, so we don't do
231 // anything in the constructor.
232 Task_block_token(Task_token* token)
233 : token_(token)
234 { gold_assert(this->token_->is_blocked()); }
235
236 ~Task_block_token()
237 { this->token_->remove_blocker(); }
238
239 private:
240 Task_block_token(const Task_block_token&);
241 Task_block_token& operator=(const Task_block_token&);
242
243 Task_token* token_;
244};
245
246// An object which implements an RAII lock for any object which
247// supports lock and unlock methods.
248
249template<typename Obj>
250class Task_lock_obj
251{
252 public:
253 Task_lock_obj(const Task* task, Obj* obj)
254 : task_(task), obj_(obj)
255 { this->obj_->lock(task); }
256
257 ~Task_lock_obj()
258 { this->obj_->unlock(this->task_); }
259
260 private:
261 Task_lock_obj(const Task_lock_obj&);
262 Task_lock_obj& operator=(const Task_lock_obj&);
263
264 const Task* task_;
265 Obj* obj_;
266};
267
268// A class which holds the set of Task_tokens which must be locked for
269// a Task. No Task requires more than four Task_tokens, so we set
270// that as a limit.
271
272class Task_locker
273{
274 public:
275 static const int max_task_count = 4;
276
277 Task_locker()
278 : count_(0)
279 { }
280
281 ~Task_locker()
282 { }
283
284 // Clear the locker.
285 void
286 clear()
287 { this->count_ = 0; }
288
289 // Add a token to the locker.
290 void
291 add(Task* t, Task_token* token)
292 {
293 gold_assert(this->count_ < max_task_count);
294 this->tokens_[this->count_] = token;
295 ++this->count_;
296 // A blocker will have been incremented when the task is created.
297 // A writer we need to lock now.
298 if (!token->is_blocker())
299 token->add_writer(t);
300 }
301
302 // Iterate over the tokens.
303
304 typedef Task_token** iterator;
305
306 iterator
307 begin()
308 { return &this->tokens_[0]; }
309
310 iterator
311 end()
312 { return &this->tokens_[this->count_]; }
313
314 private:
315 Task_locker(const Task_locker&);
316 Task_locker& operator=(const Task_locker&);
317
318 // The number of tokens.
319 int count_;
320 // The tokens.
321 Task_token* tokens_[max_task_count];
322};
323
324} // End namespace gold.
325
326#endif // !defined(GOLD_TOKEN_H)