Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: base/tracked_objects.cc

Issue 8597017: Switch to a simple linked-list for worker thread pool (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/tracked_objects.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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"
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 // static 128 // static
129 int ThreadData::thread_number_counter_ = 0; 129 int ThreadData::thread_number_counter_ = 0;
130 130
131 // static 131 // static
132 int ThreadData::incarnation_counter_ = 0; 132 int ThreadData::incarnation_counter_ = 0;
133 133
134 // static 134 // static
135 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; 135 ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
136 136
137 // static 137 // static
138 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; 138 ThreadData* ThreadData::first_retired_worker_ = NULL;
139 139
140 // static 140 // static
141 base::LazyInstance<base::Lock, 141 base::LazyInstance<base::Lock,
142 base::LeakyLazyInstanceTraits<base::Lock> > 142 base::LeakyLazyInstanceTraits<base::Lock> >
143 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; 143 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER;
144 144
145 // static 145 // static
146 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; 146 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
147 147
148 ThreadData::ThreadData(const std::string& suggested_name) 148 ThreadData::ThreadData(const std::string& suggested_name)
149 : incarnation_count_for_pool_(-1), 149 : incarnation_count_for_pool_(-1),
150 next_(NULL), 150 next_(NULL),
151 next_retired_worker_(NULL),
151 worker_thread_number_(0) { 152 worker_thread_number_(0) {
152 DCHECK_GE(suggested_name.size(), 0u); 153 DCHECK_GE(suggested_name.size(), 0u);
153 thread_name_ = suggested_name; 154 thread_name_ = suggested_name;
154 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. 155 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
155 } 156 }
156 157
157 ThreadData::ThreadData(size_t thread_number) 158 ThreadData::ThreadData(int thread_number)
158 : incarnation_count_for_pool_(-1), 159 : incarnation_count_for_pool_(-1),
159 next_(NULL), 160 next_(NULL),
161 next_retired_worker_(NULL),
160 worker_thread_number_(thread_number) { 162 worker_thread_number_(thread_number) {
161 CHECK_NE(thread_number, 0u); 163 CHECK_GT(thread_number, 0);
162 base::StringAppendF(&thread_name_, "WorkerThread-%"PRIuS, thread_number); 164 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
163 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. 165 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
164 } 166 }
165 167
166 ThreadData::~ThreadData() {} 168 ThreadData::~ThreadData() {}
167 169
168 void ThreadData::PushToHeadOfList() { 170 void ThreadData::PushToHeadOfList() {
169 DCHECK(!next_); 171 DCHECK(!next_);
170 base::AutoLock lock(*list_lock_.Pointer()); 172 base::AutoLock lock(*list_lock_.Pointer());
171 incarnation_count_for_pool_ = incarnation_counter_; 173 incarnation_count_for_pool_ = incarnation_counter_;
172 next_ = all_thread_data_list_head_; 174 next_ = all_thread_data_list_head_;
(...skipping 15 matching lines...) Expand all
188 // static 190 // static
189 ThreadData* ThreadData::Get() { 191 ThreadData* ThreadData::Get() {
190 if (!tls_index_.initialized()) 192 if (!tls_index_.initialized())
191 return NULL; // For unittests only. 193 return NULL; // For unittests only.
192 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); 194 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
193 if (registered) 195 if (registered)
194 return registered; 196 return registered;
195 197
196 // We must be a worker thread, since we didn't pre-register. 198 // We must be a worker thread, since we didn't pre-register.
197 ThreadData* worker_thread_data = NULL; 199 ThreadData* worker_thread_data = NULL;
198 size_t thread_number = 0; 200 int thread_number = 0;
199 { 201 {
200 base::AutoLock lock(*list_lock_.Pointer()); 202 base::AutoLock lock(*list_lock_.Pointer());
201 if (!unregistered_thread_data_pool_) 203 if (first_retired_worker_) {
202 unregistered_thread_data_pool_ = new ThreadDataPool; 204 worker_thread_data = first_retired_worker_;
203 if (!unregistered_thread_data_pool_->empty()) { 205 first_retired_worker_ = first_retired_worker_->next_retired_worker_;
204 worker_thread_data = 206 worker_thread_data->next_retired_worker_ = NULL;
205 const_cast<ThreadData*>(unregistered_thread_data_pool_->top());
206 unregistered_thread_data_pool_->pop();
207 } else { 207 } else {
208 thread_number = ++thread_number_counter_; 208 thread_number = ++thread_number_counter_;
209 unregistered_thread_data_pool_->reserve(thread_number);
210 } 209 }
211 } 210 }
212 211
213 // If we can't find a previously used instance, then we have to create one. 212 // If we can't find a previously used instance, then we have to create one.
214 if (!worker_thread_data) 213 if (!worker_thread_data)
215 worker_thread_data = new ThreadData(thread_number); 214 worker_thread_data = new ThreadData(thread_number);
216 DCHECK_GT(worker_thread_data->worker_thread_number_, 0u); 215 DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
217 216
218 tls_index_.Set(worker_thread_data); 217 tls_index_.Set(worker_thread_data);
219 return worker_thread_data; 218 return worker_thread_data;
220 } 219 }
221 220
222 // static 221 // static
223 void ThreadData::OnThreadTermination(void* thread_data) { 222 void ThreadData::OnThreadTermination(void* thread_data) {
224 if (!kTrackAllTaskObjects) 223 if (!kTrackAllTaskObjects)
225 return; // Not compiled in. 224 return; // Not compiled in.
226 if (!thread_data) 225 if (!thread_data)
227 return; 226 return;
228 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); 227 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
229 } 228 }
230 229
231 void ThreadData::OnThreadTerminationCleanup() const { 230 void ThreadData::OnThreadTerminationCleanup() {
232 if (!worker_thread_number_) 231 if (!worker_thread_number_)
233 return; 232 return;
234 base::AutoLock lock(*list_lock_.Pointer()); 233 base::AutoLock lock(*list_lock_.Pointer());
235 if (incarnation_counter_ != incarnation_count_for_pool_) 234 if (incarnation_counter_ != incarnation_count_for_pool_)
236 return; // ThreadData was constructed in an earlier unit test. 235 return; // ThreadData was constructed in an earlier unit test.
237 // The following will never have to do an allocation. 236 // We must NOT do any allocations during this callback.
238 unregistered_thread_data_pool_->push(this); 237 // Using the simple linked lists avoids all allocations.
238 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
239 this->next_retired_worker_ = first_retired_worker_;
240 first_retired_worker_ = this;
239 } 241 }
240 242
241 // static 243 // static
242 base::DictionaryValue* ThreadData::ToValue() { 244 base::DictionaryValue* ThreadData::ToValue() {
243 DataCollector collected_data; // Gather data. 245 DataCollector collected_data; // Gather data.
244 collected_data.AddListOfLivingObjects(); // Add births that are still alive. 246 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
245 base::ListValue* list = collected_data.ToValue(); 247 base::ListValue* list = collected_data.ToValue();
246 base::DictionaryValue* dictionary = new base::DictionaryValue(); 248 base::DictionaryValue* dictionary = new base::DictionaryValue();
247 dictionary->Set("list", list); 249 dictionary->Set("list", list);
248 return dictionary; 250 return dictionary;
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after
505 } 507 }
506 508
507 // static 509 // static
508 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { 510 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
509 // This is only called from test code, where we need to cleanup so that 511 // This is only called from test code, where we need to cleanup so that
510 // additional tests can be run. 512 // additional tests can be run.
511 // We must be single threaded... but be careful anyway. 513 // We must be single threaded... but be careful anyway.
512 if (!InitializeAndSetTrackingStatus(false)) 514 if (!InitializeAndSetTrackingStatus(false))
513 return; 515 return;
514 ThreadData* thread_data_list; 516 ThreadData* thread_data_list;
515 ThreadDataPool* final_pool;
516 { 517 {
517 base::AutoLock lock(*list_lock_.Pointer()); 518 base::AutoLock lock(*list_lock_.Pointer());
518 thread_data_list = all_thread_data_list_head_; 519 thread_data_list = all_thread_data_list_head_;
519 all_thread_data_list_head_ = NULL; 520 all_thread_data_list_head_ = NULL;
520 final_pool = unregistered_thread_data_pool_;
521 unregistered_thread_data_pool_ = NULL;
522 ++incarnation_counter_; 521 ++incarnation_counter_;
522 // To be clean, break apart the retired worker list (though we leak them).
523 while(first_retired_worker_) {
524 ThreadData* worker = first_retired_worker_;
525 CHECK_GT(worker->worker_thread_number_, 0);
526 first_retired_worker_ = worker->next_retired_worker_;
527 worker->next_retired_worker_ = NULL;
528 }
523 } 529 }
524 530
525 // Put most global static back in pristine shape. 531 // Put most global static back in pristine shape.
526 thread_number_counter_ = 0; 532 thread_number_counter_ = 0;
527 tls_index_.Set(NULL); 533 tls_index_.Set(NULL);
528 status_ = UNINITIALIZED; 534 status_ = UNINITIALIZED;
529 535
530 // To avoid any chance of racing in unit tests, which is the only place we 536 // To avoid any chance of racing in unit tests, which is the only place we
531 // call this function, we may sometimes leak all the data structures we 537 // call this function, we may sometimes leak all the data structures we
532 // recovered, as they may still be in use on threads from prior tests! 538 // recovered, as they may still be in use on threads from prior tests!
533 if (leak) 539 if (leak)
534 return; 540 return;
535 541
536 // When we want to cleanup (on a single thread), here is what we do. 542 // When we want to cleanup (on a single thread), here is what we do.
537 543
538 if (final_pool) {
539 // The thread_data_list contains *all* the instances, and we'll use it to
540 // delete them. This pool has pointers to some instances, and we just
541 // have to drop those pointers (and not do the deletes here).
542 while (!final_pool->empty())
543 final_pool->pop();
544 delete final_pool;
545 }
546
547 // Do actual recursive delete in all ThreadData instances. 544 // Do actual recursive delete in all ThreadData instances.
548 while (thread_data_list) { 545 while (thread_data_list) {
549 ThreadData* next_thread_data = thread_data_list; 546 ThreadData* next_thread_data = thread_data_list;
550 thread_data_list = thread_data_list->next(); 547 thread_data_list = thread_data_list->next();
551 548
552 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); 549 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
553 next_thread_data->birth_map_.end() != it; ++it) 550 next_thread_data->birth_map_.end() != it; ++it)
554 delete it->second; // Delete the Birth Records. 551 delete it->second; // Delete the Birth Records.
555 next_thread_data->birth_map_.clear(); 552 next_thread_data->birth_map_.clear();
556 next_thread_data->death_map_.clear(); 553 next_thread_data->death_map_.clear();
557 delete next_thread_data; // Includes all Death Records. 554 delete next_thread_data; // Includes all Death Records.
558 } 555 }
559 } 556 }
560 557
561 //------------------------------------------------------------------------------ 558 //------------------------------------------------------------------------------
562 // Small partial implementation of a stack that never has to allocate during a
563 // push() operation, because it is always prepared to accept the maximum number
564 // of ThreadData instances (all the worker thread related instances).
565
566 ThreadData::ThreadDataPool::ThreadDataPool() : empty_slot_(0) {};
567 ThreadData::ThreadDataPool::~ThreadDataPool() {};
568
569 bool ThreadData::ThreadDataPool::empty() const { return empty_slot_ == 0; }
570
571 void ThreadData::ThreadDataPool::reserve(size_t largest_worker_pool_number) {
572 // Worker pool numbers start at 1, and exclude 0, so the number is exactly
573 // the least size needed.
574 // Due to asynchronous construction of worker-pool numbers (and associated
575 // ThreadData), we might not hear about the numbers sequentially.
576 if (largest_worker_pool_number > stack_.size())
577 stack_.resize(largest_worker_pool_number);
578 }
579
580 const ThreadData* ThreadData::ThreadDataPool::top() const {
581 if (empty_slot_ > 0)
582 return stack_[empty_slot_ - 1];
583 NOTREACHED();
584 return NULL;
585 }
586
587 void ThreadData::ThreadDataPool::push(const ThreadData* thread_data) {
588 if (empty_slot_ < stack_.size()) {
589 stack_[empty_slot_] = thread_data;
590 ++empty_slot_;
591 return;
592 }
593 NOTREACHED();
594 }
595
596 void ThreadData::ThreadDataPool::pop() {
597 if (empty_slot_ > 0) {
598 --empty_slot_;
599 return;
600 }
601 NOTREACHED();
602 }
603
604 //------------------------------------------------------------------------------
605 // Individual 3-tuple of birth (place and thread) along with death thread, and 559 // Individual 3-tuple of birth (place and thread) along with death thread, and
606 // the accumulated stats for instances (DeathData). 560 // the accumulated stats for instances (DeathData).
607 561
608 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, 562 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
609 const ThreadData& death_thread, 563 const ThreadData& death_thread,
610 const DeathData& death_data) 564 const DeathData& death_data)
611 : birth_(&birth_on_thread), 565 : birth_(&birth_on_thread),
612 death_thread_(&death_thread), 566 death_thread_(&death_thread),
613 death_data_(death_data) { 567 death_data_(death_data) {
614 } 568 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 649
696 base::ListValue* DataCollector::ToValue() const { 650 base::ListValue* DataCollector::ToValue() const {
697 base::ListValue* list = new base::ListValue; 651 base::ListValue* list = new base::ListValue;
698 for (size_t i = 0; i < collection_.size(); ++i) { 652 for (size_t i = 0; i < collection_.size(); ++i) {
699 list->Append(collection_[i].ToValue()); 653 list->Append(collection_[i].ToValue());
700 } 654 }
701 return list; 655 return list;
702 } 656 }
703 657
704 } // namespace tracked_objects 658 } // namespace tracked_objects
OLDNEW
« no previous file with comments | « base/tracked_objects.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698