Commit | Line | Data |
---|---|---|
17a1d0a9 ILT |
1 | // token.h -- lock tokens for gold -*- C++ -*- |
2 | ||
82704155 | 3 | // Copyright (C) 2006-2019 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 | ||
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 | ||
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 | ||
91 | class Task_token | |
92 | { | |
93 | public: | |
2ea97941 ILT |
94 | Task_token(bool is_blocker) |
95 | : is_blocker_(is_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; | |
fa17a3f4 ILT |
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; | |
17a1d0a9 ILT |
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 | ||
da769d56 ILT |
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 | ||
17a1d0a9 ILT |
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) |