Commit | Line | Data |
---|---|---|
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 | ||
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: | |
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 | // 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 | ||
206 | class 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 | ||
226 | class 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 | ||
249 | template<typename Obj> | |
250 | class 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 | ||
272 | class 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) |