OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/tracked_objects.h" | 5 #include "base/tracked_objects.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
12 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
13 #include "build/build_config.h" | 13 #include "build/build_config.h" |
14 #include "base/port.h" | 14 #include "base/port.h" |
15 | 15 |
16 #include "base/win/dllmain.cc" | |
rvargas (doing something else)
2011/11/24 00:09:21
definitely not cool.
| |
17 | |
16 using base::TimeDelta; | 18 using base::TimeDelta; |
17 | 19 |
18 namespace tracked_objects { | 20 namespace tracked_objects { |
19 | 21 |
20 namespace { | 22 namespace { |
21 // Flag to compile out almost all of the task tracking code. | 23 // Flag to compile out almost all of the task tracking code. |
22 static const bool kTrackAllTaskObjects = true; | 24 static const bool kTrackAllTaskObjects = true; |
23 | 25 |
24 // When ThreadData is first initialized, should we start in an ACTIVE state to | 26 // When ThreadData is first initialized, should we start in an ACTIVE state to |
25 // record all of the startup-time tasks, or should we start up DEACTIVATED, so | 27 // record all of the startup-time tasks, or should we start up DEACTIVATED, so |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 // TODO(jar): We should pull all these static vars together, into a struct, and | 119 // TODO(jar): We should pull all these static vars together, into a struct, and |
118 // optimize layout so that we benefit from locality of reference during accesses | 120 // optimize layout so that we benefit from locality of reference during accesses |
119 // to them. | 121 // to them. |
120 | 122 |
121 // A TLS slot which points to the ThreadData instance for the current thread. We | 123 // A TLS slot which points to the ThreadData instance for the current thread. We |
122 // do a fake initialization here (zeroing out data), and then the real in-place | 124 // do a fake initialization here (zeroing out data), and then the real in-place |
123 // construction happens when we call tls_index_.Initialize(). | 125 // construction happens when we call tls_index_.Initialize(). |
124 // static | 126 // static |
125 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | 127 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
126 | 128 |
127 // A lock-protected counter to assign sequence number to threads. | |
128 // static | 129 // static |
129 int ThreadData::thread_number_counter_ = 0; | 130 int ThreadData::worker_thread_data_creation_count_ = 0; |
131 | |
132 // static | |
133 int ThreadData::cleanup_count_ = 0; | |
130 | 134 |
131 // static | 135 // static |
132 int ThreadData::incarnation_counter_ = 0; | 136 int ThreadData::incarnation_counter_ = 0; |
133 | 137 |
134 // static | 138 // static |
135 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; | 139 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; |
136 | 140 |
137 // static | 141 // static |
138 ThreadData* ThreadData::first_retired_worker_ = NULL; | 142 ThreadData* ThreadData::first_retired_worker_ = NULL; |
139 | 143 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
190 // static | 194 // static |
191 ThreadData* ThreadData::Get() { | 195 ThreadData* ThreadData::Get() { |
192 if (!tls_index_.initialized()) | 196 if (!tls_index_.initialized()) |
193 return NULL; // For unittests only. | 197 return NULL; // For unittests only. |
194 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); | 198 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); |
195 if (registered) | 199 if (registered) |
196 return registered; | 200 return registered; |
197 | 201 |
198 // We must be a worker thread, since we didn't pre-register. | 202 // We must be a worker thread, since we didn't pre-register. |
199 ThreadData* worker_thread_data = NULL; | 203 ThreadData* worker_thread_data = NULL; |
200 int thread_number = 0; | 204 int worker_thread_number = 0; |
201 { | 205 { |
202 base::AutoLock lock(*list_lock_.Pointer()); | 206 base::AutoLock lock(*list_lock_.Pointer()); |
203 if (first_retired_worker_) { | 207 if (first_retired_worker_) { |
204 worker_thread_data = first_retired_worker_; | 208 worker_thread_data = first_retired_worker_; |
205 first_retired_worker_ = first_retired_worker_->next_retired_worker_; | 209 first_retired_worker_ = first_retired_worker_->next_retired_worker_; |
206 worker_thread_data->next_retired_worker_ = NULL; | 210 worker_thread_data->next_retired_worker_ = NULL; |
207 } else { | 211 } else { |
208 thread_number = ++thread_number_counter_; | 212 worker_thread_number = ++worker_thread_data_creation_count_; |
209 } | 213 } |
210 } | 214 } |
211 | 215 |
212 // If we can't find a previously used instance, then we have to create one. | 216 // If we can't find a previously used instance, then we have to create one. |
213 if (!worker_thread_data) | 217 if (!worker_thread_data) { |
214 worker_thread_data = new ThreadData(thread_number); | 218 DCHECK_GT(worker_thread_number, 0); |
219 worker_thread_data = new ThreadData(worker_thread_number); | |
220 } | |
215 DCHECK_GT(worker_thread_data->worker_thread_number_, 0); | 221 DCHECK_GT(worker_thread_data->worker_thread_number_, 0); |
216 | 222 |
217 tls_index_.Set(worker_thread_data); | 223 tls_index_.Set(worker_thread_data); |
218 return worker_thread_data; | 224 return worker_thread_data; |
219 } | 225 } |
220 | 226 |
221 // static | 227 // static |
222 void ThreadData::OnThreadTermination(void* thread_data) { | 228 void ThreadData::OnThreadTermination(void* thread_data) { |
229 // We must NOT do any allocations during this callback. There is a chance | |
230 // that the allocator is no longer active on this thread. | |
223 if (!kTrackAllTaskObjects) | 231 if (!kTrackAllTaskObjects) |
224 return; // Not compiled in. | 232 return; // Not compiled in. |
225 if (!thread_data) | 233 if (!thread_data) |
226 return; | 234 return; |
227 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); | 235 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); |
228 } | 236 } |
229 | 237 |
230 void ThreadData::OnThreadTerminationCleanup() { | 238 void ThreadData::OnThreadTerminationCleanup() { |
231 if (!worker_thread_number_) | 239 // The list_lock_ was created when we registered the callback, so it won't be |
232 return; | 240 // allocated here despite the lazy reference. |
233 base::AutoLock lock(*list_lock_.Pointer()); | 241 base::AutoLock lock(*list_lock_.Pointer()); |
234 if (incarnation_counter_ != incarnation_count_for_pool_) | 242 if (incarnation_counter_ != incarnation_count_for_pool_) |
235 return; // ThreadData was constructed in an earlier unit test. | 243 return; // ThreadData was constructed in an earlier unit test. |
244 ++cleanup_count_; | |
245 // Only worker threads need to be retired and reused. | |
246 if (!worker_thread_number_) { | |
247 return; | |
248 } | |
236 // We must NOT do any allocations during this callback. | 249 // We must NOT do any allocations during this callback. |
237 // Using the simple linked lists avoids all allocations. | 250 // Using the simple linked lists avoids all allocations. |
238 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL)); | 251 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL)); |
239 this->next_retired_worker_ = first_retired_worker_; | 252 this->next_retired_worker_ = first_retired_worker_; |
240 first_retired_worker_ = this; | 253 first_retired_worker_ = this; |
241 } | 254 } |
242 | 255 |
243 // static | 256 // static |
244 base::DictionaryValue* ThreadData::ToValue() { | 257 base::DictionaryValue* ThreadData::ToValue() { |
245 DataCollector collected_data; // Gather data. | 258 DataCollector collected_data; // Gather data. |
246 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 259 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
247 base::ListValue* list = collected_data.ToValue(); | 260 base::ListValue* list = collected_data.ToValue(); |
248 base::DictionaryValue* dictionary = new base::DictionaryValue(); | 261 base::DictionaryValue* dictionary = new base::DictionaryValue(); |
249 dictionary->Set("list", list); | 262 dictionary->Set("list", list); |
250 return dictionary; | 263 return dictionary; |
251 } | 264 } |
252 | 265 |
253 Births* ThreadData::TallyABirth(const Location& location) { | 266 Births* ThreadData::TallyABirth(const Location& location) { |
254 BirthMap::iterator it = birth_map_.find(location); | 267 BirthMap::iterator it = birth_map_.find(location); |
255 if (it != birth_map_.end()) { | 268 if (it != birth_map_.end()) { |
256 it->second->RecordBirth(); | 269 it->second->RecordBirth(); |
257 return it->second; | 270 return it->second; |
258 } | 271 } |
259 | 272 |
260 Births* tracker = new Births(location, *this); | 273 Births* tracker = new Births(location, *this); |
261 // Lock since the map may get relocated now, and other threads sometimes | 274 // Lock since the map may get relocated now, and other threads sometimes |
262 // snapshot it (but they lock before copying it). | 275 // snapshot it (but they lock before copying it). |
263 base::AutoLock lock(lock_); | 276 base::AutoLock lock(map_lock_); |
264 birth_map_[location] = tracker; | 277 birth_map_[location] = tracker; |
265 return tracker; | 278 return tracker; |
266 } | 279 } |
267 | 280 |
268 void ThreadData::TallyADeath(const Births& birth, | 281 void ThreadData::TallyADeath(const Births& birth, |
269 DurationInt queue_duration, | 282 DurationInt queue_duration, |
270 DurationInt run_duration) { | 283 DurationInt run_duration) { |
271 DeathMap::iterator it = death_map_.find(&birth); | 284 DeathMap::iterator it = death_map_.find(&birth); |
272 DeathData* death_data; | 285 DeathData* death_data; |
273 if (it != death_map_.end()) { | 286 if (it != death_map_.end()) { |
274 death_data = &it->second; | 287 death_data = &it->second; |
275 } else { | 288 } else { |
276 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 289 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. |
277 death_data = &death_map_[&birth]; | 290 death_data = &death_map_[&birth]; |
278 } // Release lock ASAP. | 291 } // Release lock ASAP. |
279 death_data->RecordDeath(queue_duration, run_duration); | 292 death_data->RecordDeath(queue_duration, run_duration); |
280 } | 293 } |
281 | 294 |
282 // static | 295 // static |
283 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 296 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
284 if (!kTrackAllTaskObjects) | 297 if (!kTrackAllTaskObjects) |
285 return NULL; // Not compiled in. | 298 return NULL; // Not compiled in. |
286 | 299 |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
399 } | 412 } |
400 | 413 |
401 // static | 414 // static |
402 ThreadData* ThreadData::first() { | 415 ThreadData* ThreadData::first() { |
403 base::AutoLock lock(*list_lock_.Pointer()); | 416 base::AutoLock lock(*list_lock_.Pointer()); |
404 return all_thread_data_list_head_; | 417 return all_thread_data_list_head_; |
405 } | 418 } |
406 | 419 |
407 // This may be called from another thread. | 420 // This may be called from another thread. |
408 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 421 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
409 base::AutoLock lock(lock_); | 422 base::AutoLock lock(map_lock_); |
410 for (BirthMap::const_iterator it = birth_map_.begin(); | 423 for (BirthMap::const_iterator it = birth_map_.begin(); |
411 it != birth_map_.end(); ++it) | 424 it != birth_map_.end(); ++it) |
412 (*output)[it->first] = it->second; | 425 (*output)[it->first] = it->second; |
413 } | 426 } |
414 | 427 |
415 // This may be called from another thread. | 428 // This may be called from another thread. |
416 void ThreadData::SnapshotDeathMap(DeathMap *output) const { | 429 void ThreadData::SnapshotDeathMap(DeathMap *output) const { |
417 base::AutoLock lock(lock_); | 430 base::AutoLock lock(map_lock_); |
418 for (DeathMap::const_iterator it = death_map_.begin(); | 431 for (DeathMap::const_iterator it = death_map_.begin(); |
419 it != death_map_.end(); ++it) | 432 it != death_map_.end(); ++it) |
420 (*output)[it->first] = it->second; | 433 (*output)[it->first] = it->second; |
421 } | 434 } |
422 | 435 |
423 // static | 436 // static |
424 void ThreadData::ResetAllThreadData() { | 437 void ThreadData::ResetAllThreadData() { |
425 ThreadData* my_list = first(); | 438 ThreadData* my_list = first(); |
426 | 439 |
427 for (ThreadData* thread_data = my_list; | 440 for (ThreadData* thread_data = my_list; |
428 thread_data; | 441 thread_data; |
429 thread_data = thread_data->next()) | 442 thread_data = thread_data->next()) |
430 thread_data->Reset(); | 443 thread_data->Reset(); |
431 } | 444 } |
432 | 445 |
433 void ThreadData::Reset() { | 446 void ThreadData::Reset() { |
434 base::AutoLock lock(lock_); | 447 base::AutoLock lock(map_lock_); |
435 for (DeathMap::iterator it = death_map_.begin(); | 448 for (DeathMap::iterator it = death_map_.begin(); |
436 it != death_map_.end(); ++it) | 449 it != death_map_.end(); ++it) |
437 it->second.Clear(); | 450 it->second.Clear(); |
438 for (BirthMap::iterator it = birth_map_.begin(); | 451 for (BirthMap::iterator it = birth_map_.begin(); |
439 it != birth_map_.end(); ++it) | 452 it != birth_map_.end(); ++it) |
440 it->second->Clear(); | 453 it->second->Clear(); |
441 } | 454 } |
442 | 455 |
443 bool ThreadData::Initialize() { | 456 bool ThreadData::Initialize() { |
444 if (!kTrackAllTaskObjects) | 457 if (!kTrackAllTaskObjects) |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
500 } | 513 } |
501 | 514 |
502 // static | 515 // static |
503 TrackedTime ThreadData::Now() { | 516 TrackedTime ThreadData::Now() { |
504 if (kTrackAllTaskObjects && tracking_status()) | 517 if (kTrackAllTaskObjects && tracking_status()) |
505 return TrackedTime::Now(); | 518 return TrackedTime::Now(); |
506 return TrackedTime(); // Super fast when disabled, or not compiled. | 519 return TrackedTime(); // Super fast when disabled, or not compiled. |
507 } | 520 } |
508 | 521 |
509 // static | 522 // static |
523 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { | |
524 base::AutoLock lock(*list_lock_.Pointer()); | |
525 if (worker_thread_data_creation_count_ == 0) | |
526 return; // We haven't really run much, and couldn't have leaked. | |
527 // Verify that we've at least shutdown/cleanup the major namesd threads. The | |
528 // caller should tell us how many thread shutdowns should have taken place by | |
529 // now. | |
530 return; // TODO(jar): until this is working on XP, don't run the real test. | |
531 CHECK_GT(cleanup_count_, major_threads_shutdown_count); | |
532 } | |
533 | |
534 // static | |
510 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { | 535 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { |
511 // This is only called from test code, where we need to cleanup so that | 536 // This is only called from test code, where we need to cleanup so that |
512 // additional tests can be run. | 537 // additional tests can be run. |
513 // We must be single threaded... but be careful anyway. | 538 // We must be single threaded... but be careful anyway. |
514 if (!InitializeAndSetTrackingStatus(false)) | 539 if (!InitializeAndSetTrackingStatus(false)) |
515 return; | 540 return; |
516 ThreadData* thread_data_list; | 541 ThreadData* thread_data_list; |
517 { | 542 { |
518 base::AutoLock lock(*list_lock_.Pointer()); | 543 base::AutoLock lock(*list_lock_.Pointer()); |
519 thread_data_list = all_thread_data_list_head_; | 544 thread_data_list = all_thread_data_list_head_; |
520 all_thread_data_list_head_ = NULL; | 545 all_thread_data_list_head_ = NULL; |
521 ++incarnation_counter_; | 546 ++incarnation_counter_; |
522 // To be clean, break apart the retired worker list (though we leak them). | 547 // To be clean, break apart the retired worker list (though we leak them). |
523 while(first_retired_worker_) { | 548 while(first_retired_worker_) { |
524 ThreadData* worker = first_retired_worker_; | 549 ThreadData* worker = first_retired_worker_; |
525 CHECK_GT(worker->worker_thread_number_, 0); | 550 CHECK_GT(worker->worker_thread_number_, 0); |
526 first_retired_worker_ = worker->next_retired_worker_; | 551 first_retired_worker_ = worker->next_retired_worker_; |
527 worker->next_retired_worker_ = NULL; | 552 worker->next_retired_worker_ = NULL; |
528 } | 553 } |
529 } | 554 } |
530 | 555 |
531 // Put most global static back in pristine shape. | 556 // Put most global static back in pristine shape. |
532 thread_number_counter_ = 0; | 557 worker_thread_data_creation_count_ = 0; |
558 cleanup_count_ = 0; | |
533 tls_index_.Set(NULL); | 559 tls_index_.Set(NULL); |
534 status_ = UNINITIALIZED; | 560 status_ = UNINITIALIZED; |
535 | 561 |
536 // To avoid any chance of racing in unit tests, which is the only place we | 562 // To avoid any chance of racing in unit tests, which is the only place we |
537 // call this function, we may sometimes leak all the data structures we | 563 // call this function, we may sometimes leak all the data structures we |
538 // recovered, as they may still be in use on threads from prior tests! | 564 // recovered, as they may still be in use on threads from prior tests! |
539 if (leak) | 565 if (leak) |
540 return; | 566 return; |
541 | 567 |
542 // When we want to cleanup (on a single thread), here is what we do. | 568 // When we want to cleanup (on a single thread), here is what we do. |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
649 | 675 |
650 base::ListValue* DataCollector::ToValue() const { | 676 base::ListValue* DataCollector::ToValue() const { |
651 base::ListValue* list = new base::ListValue; | 677 base::ListValue* list = new base::ListValue; |
652 for (size_t i = 0; i < collection_.size(); ++i) { | 678 for (size_t i = 0; i < collection_.size(); ++i) { |
653 list->Append(collection_[i].ToValue()); | 679 list->Append(collection_[i].ToValue()); |
654 } | 680 } |
655 return list; | 681 return list; |
656 } | 682 } |
657 | 683 |
658 } // namespace tracked_objects | 684 } // namespace tracked_objects |
OLD | NEW |