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

Side by Side Diff: base/tracked_objects.cc

Issue 8587031: Avoid any possibility of an Alloc during TLS thread teardown (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
« base/tracked_objects.h ('K') | « 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"
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 15
15 using base::TimeDelta; 16 using base::TimeDelta;
16 17
17 namespace tracked_objects { 18 namespace tracked_objects {
18 19
19 namespace { 20 namespace {
20 // Flag to compile out almost all of the task tracking code. 21 // Flag to compile out almost all of the task tracking code.
21 static const bool kTrackAllTaskObjects = true; 22 static const bool kTrackAllTaskObjects = true;
22 23
23 // When ThreadData is first initialized, should we start in an ACTIVE state to 24 // When ThreadData is first initialized, should we start in an ACTIVE state to
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 base::LazyInstance<base::Lock, 141 base::LazyInstance<base::Lock,
141 base::LeakyLazyInstanceTraits<base::Lock> > 142 base::LeakyLazyInstanceTraits<base::Lock> >
142 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER; 143 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER;
143 144
144 // static 145 // static
145 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; 146 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
146 147
147 ThreadData::ThreadData(const std::string& suggested_name) 148 ThreadData::ThreadData(const std::string& suggested_name)
148 : incarnation_count_for_pool_(-1), 149 : incarnation_count_for_pool_(-1),
149 next_(NULL), 150 next_(NULL),
150 is_a_worker_thread_(false) { 151 worker_thread_number_(0) {
151 DCHECK_GE(suggested_name.size(), 0u); 152 DCHECK_GE(suggested_name.size(), 0u);
152 thread_name_ = suggested_name; 153 thread_name_ = suggested_name;
153 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. 154 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
154 } 155 }
155 156
156 ThreadData::ThreadData() 157 ThreadData::ThreadData(size_t thread_number)
157 : incarnation_count_for_pool_(-1), 158 : incarnation_count_for_pool_(-1),
158 next_(NULL), 159 next_(NULL),
159 is_a_worker_thread_(true) { 160 worker_thread_number_(thread_number) {
160 int thread_number; 161 base::StringAppendF(&thread_name_, "WorkerThread-%"PRIuS, thread_number);
161 {
162 base::AutoLock lock(*list_lock_.Pointer());
163 thread_number = ++thread_number_counter_;
164 }
165 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
166 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. 162 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
167 } 163 }
168 164
169 ThreadData::~ThreadData() {} 165 ThreadData::~ThreadData() {}
170 166
171 void ThreadData::PushToHeadOfList() { 167 void ThreadData::PushToHeadOfList() {
172 DCHECK(!next_); 168 DCHECK(!next_);
173 base::AutoLock lock(*list_lock_.Pointer()); 169 base::AutoLock lock(*list_lock_.Pointer());
174 incarnation_count_for_pool_ = incarnation_counter_; 170 incarnation_count_for_pool_ = incarnation_counter_;
175 next_ = all_thread_data_list_head_; 171 next_ = all_thread_data_list_head_;
(...skipping 15 matching lines...) Expand all
191 // static 187 // static
192 ThreadData* ThreadData::Get() { 188 ThreadData* ThreadData::Get() {
193 if (!tls_index_.initialized()) 189 if (!tls_index_.initialized())
194 return NULL; // For unittests only. 190 return NULL; // For unittests only.
195 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); 191 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
196 if (registered) 192 if (registered)
197 return registered; 193 return registered;
198 194
199 // We must be a worker thread, since we didn't pre-register. 195 // We must be a worker thread, since we didn't pre-register.
200 ThreadData* worker_thread_data = NULL; 196 ThreadData* worker_thread_data = NULL;
197 size_t thread_number = 0;
201 { 198 {
202 base::AutoLock lock(*list_lock_.Pointer()); 199 base::AutoLock lock(*list_lock_.Pointer());
203 if (unregistered_thread_data_pool_ && 200 if (!unregistered_thread_data_pool_)
204 !unregistered_thread_data_pool_->empty()) { 201 unregistered_thread_data_pool_ = new ThreadDataPool;
202 if (!unregistered_thread_data_pool_->empty()) {
205 worker_thread_data = 203 worker_thread_data =
206 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); 204 const_cast<ThreadData*>(unregistered_thread_data_pool_->top());
207 unregistered_thread_data_pool_->pop(); 205 unregistered_thread_data_pool_->pop();
ramant (doing other things) 2011/11/17 22:59:36 nit: Should we consider adding a DCHECK for worker
jar (doing other things) 2011/11/18 01:06:42 Line 205 would access lower memory if the instance
206 } else {
207 thread_number = ++thread_number_counter_;
208 unregistered_thread_data_pool_->reserve(thread_number);
208 } 209 }
209 } 210 }
210 211
211 // 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.
212 if (!worker_thread_data) 213 if (!worker_thread_data)
213 worker_thread_data = new ThreadData(); 214 worker_thread_data = new ThreadData(thread_number);
ramant (doing other things) 2011/11/17 22:59:36 nit: can thread_number be zero? If not should we c
jar (doing other things) 2011/11/18 01:06:42 Line 215 checks that via the DCHECK(). We're load
215 DCHECK_GT(worker_thread_data->worker_thread_number_, 0u);
214 216
215 tls_index_.Set(worker_thread_data); 217 tls_index_.Set(worker_thread_data);
216 return worker_thread_data; 218 return worker_thread_data;
217 } 219 }
218 220
219 // static 221 // static
220 void ThreadData::OnThreadTermination(void* thread_data) { 222 void ThreadData::OnThreadTermination(void* thread_data) {
221 if (!kTrackAllTaskObjects) 223 if (!kTrackAllTaskObjects)
222 return; // Not compiled in. 224 return; // Not compiled in.
223 if (!thread_data) 225 if (!thread_data)
224 return; 226 return;
225 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); 227 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
226 } 228 }
227 229
228 void ThreadData::OnThreadTerminationCleanup() const { 230 void ThreadData::OnThreadTerminationCleanup() const {
229 if (!is_a_worker_thread_) 231 if (!worker_thread_number_)
230 return; 232 return;
231 base::AutoLock lock(*list_lock_.Pointer()); 233 base::AutoLock lock(*list_lock_.Pointer());
232 if (incarnation_counter_ != incarnation_count_for_pool_) 234 if (incarnation_counter_ != incarnation_count_for_pool_)
233 return; // ThreadData was constructed in an earlier unit test. 235 return; // ThreadData was constructed in an earlier unit test.
234 236 // The following will never have to do an allocation.
235 // Handle case where we are in unit tests, and have become UNINITIALIZED.
236 // In that case, the pool might be NULL. We really should detect this via the
237 // incarnation_counter_, but this call is rarely made, so we can afford to
238 // code defensively.
239 if (!unregistered_thread_data_pool_)
240 unregistered_thread_data_pool_ = new ThreadDataPool;
241 unregistered_thread_data_pool_->push(this); 237 unregistered_thread_data_pool_->push(this);
ramant (doing other things) 2011/11/17 22:59:36 nit: Can we add a DCHECK for unregistered_thread_d
jar (doing other things) 2011/11/18 01:06:42 If unregistered_thread_data_pool_ is null, then we
242 } 238 }
243 239
244 // static 240 // static
245 base::DictionaryValue* ThreadData::ToValue() { 241 base::DictionaryValue* ThreadData::ToValue() {
246 DataCollector collected_data; // Gather data. 242 DataCollector collected_data; // Gather data.
247 collected_data.AddListOfLivingObjects(); // Add births that are still alive. 243 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
248 base::ListValue* list = collected_data.ToValue(); 244 base::ListValue* list = collected_data.ToValue();
249 base::DictionaryValue* dictionary = new base::DictionaryValue(); 245 base::DictionaryValue* dictionary = new base::DictionaryValue();
250 dictionary->Set("list", list); 246 dictionary->Set("list", list);
251 return dictionary; 247 return dictionary;
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
555 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); 551 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
556 next_thread_data->birth_map_.end() != it; ++it) 552 next_thread_data->birth_map_.end() != it; ++it)
557 delete it->second; // Delete the Birth Records. 553 delete it->second; // Delete the Birth Records.
558 next_thread_data->birth_map_.clear(); 554 next_thread_data->birth_map_.clear();
559 next_thread_data->death_map_.clear(); 555 next_thread_data->death_map_.clear();
560 delete next_thread_data; // Includes all Death Records. 556 delete next_thread_data; // Includes all Death Records.
561 } 557 }
562 } 558 }
563 559
564 //------------------------------------------------------------------------------ 560 //------------------------------------------------------------------------------
561 // Small partial implementation of a stack that never has to allocate during a
562 // push() operation, because it is always prepared to accept the maximum number
563 // of ThreadData instances (all the worker thread related instances).
564
565 ThreadData::ThreadDataPool::ThreadDataPool() : empty_slot_(0) {};
566 ThreadData::ThreadDataPool::~ThreadDataPool() {};
567
568 bool ThreadData::ThreadDataPool::empty() const { return empty_slot_ == 0; }
569
570 void ThreadData::ThreadDataPool::reserve(size_t largest_worker_pool_number) {
571 // Worker pool numbers start at 1, and exclude 0, so the number is exactly
572 // the least size needed.
573 // Due to asynchronous construction of worker-pool numbers (and associated
574 // ThreadData), we might not hear about the numbers sequentially.
575 if (largest_worker_pool_number > stack_.size())
576 stack_.resize(largest_worker_pool_number);
577 }
578
579 const ThreadData* ThreadData::ThreadDataPool::top() const {
580 if (empty_slot_ > 0)
581 return stack_[empty_slot_ - 1];
582 NOTREACHED();
583 return NULL;
584 }
585
586 void ThreadData::ThreadDataPool::push(const ThreadData* thread_data) {
587 if (empty_slot_ < stack_.size()) {
ramant (doing other things) 2011/11/17 22:59:36 nit: is there a value to check if thread_data->wor
jar (doing other things) 2011/11/18 01:06:42 If that violation takes place, then I'll fall thro
588 stack_[empty_slot_] = thread_data;
589 ++empty_slot_;
590 return;
591 }
592 NOTREACHED();
593 }
594
595 void ThreadData::ThreadDataPool::pop() {
596 if (empty_slot_ > 0) {
597 --empty_slot_;
598 return;
599 }
600 NOTREACHED();
601 }
602
603 //------------------------------------------------------------------------------
565 // Individual 3-tuple of birth (place and thread) along with death thread, and 604 // Individual 3-tuple of birth (place and thread) along with death thread, and
566 // the accumulated stats for instances (DeathData). 605 // the accumulated stats for instances (DeathData).
567 606
568 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, 607 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
569 const ThreadData& death_thread, 608 const ThreadData& death_thread,
570 const DeathData& death_data) 609 const DeathData& death_data)
571 : birth_(&birth_on_thread), 610 : birth_(&birth_on_thread),
572 death_thread_(&death_thread), 611 death_thread_(&death_thread),
573 death_data_(death_data) { 612 death_data_(death_data) {
574 } 613 }
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 694
656 base::ListValue* DataCollector::ToValue() const { 695 base::ListValue* DataCollector::ToValue() const {
657 base::ListValue* list = new base::ListValue; 696 base::ListValue* list = new base::ListValue;
658 for (size_t i = 0; i < collection_.size(); ++i) { 697 for (size_t i = 0; i < collection_.size(); ++i) {
659 list->Append(collection_[i].ToValue()); 698 list->Append(collection_[i].ToValue());
660 } 699 }
661 return list; 700 return list;
662 } 701 }
663 702
664 } // namespace tracked_objects 703 } // namespace tracked_objects
OLDNEW
« base/tracked_objects.h ('K') | « base/tracked_objects.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698