summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 17:03:33 +0000
committerjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 17:03:33 +0000
commit445029fbd71d6c7b3fe145c26a76cbdc4c6613d3 (patch)
tree273df6aed06fa05ae69b8834b8c11c43192d8689 /base
parent5183c3fd2be99ea2472112aa924e0ed6d5da8fd9 (diff)
downloadchromium_src-445029fbd71d6c7b3fe145c26a76cbdc4c6613d3.zip
chromium_src-445029fbd71d6c7b3fe145c26a76cbdc4c6613d3.tar.gz
chromium_src-445029fbd71d6c7b3fe145c26a76cbdc4c6613d3.tar.bz2
Avoid any possibility of an Alloc during TLS thread teardown
GIven that TCMalloc might get its thread teardown notifgication ahead of the profiler, we need to be careful to not do an alloc in the teardown handler. If we did, we might bring TCMalloc back to life on this thread... and it might not get a second teardown notification. r=rtenneti BUG=104696 Review URL: http://codereview.chromium.org/8587031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110706 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/tracked_objects.cc80
-rw-r--r--base/tracked_objects.h29
2 files changed, 86 insertions, 23 deletions
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
index 72ac6e8..709477f 100644
--- a/base/tracked_objects.cc
+++ b/base/tracked_objects.cc
@@ -11,6 +11,7 @@
#include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
+#include "base/port.h"
using base::TimeDelta;
@@ -147,22 +148,18 @@ ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
ThreadData::ThreadData(const std::string& suggested_name)
: incarnation_count_for_pool_(-1),
next_(NULL),
- is_a_worker_thread_(false) {
+ worker_thread_number_(0) {
DCHECK_GE(suggested_name.size(), 0u);
thread_name_ = suggested_name;
PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
}
-ThreadData::ThreadData()
+ThreadData::ThreadData(size_t thread_number)
: incarnation_count_for_pool_(-1),
next_(NULL),
- is_a_worker_thread_(true) {
- int thread_number;
- {
- base::AutoLock lock(*list_lock_.Pointer());
- thread_number = ++thread_number_counter_;
- }
- base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
+ worker_thread_number_(thread_number) {
+ CHECK_NE(thread_number, 0u);
+ base::StringAppendF(&thread_name_, "WorkerThread-%"PRIuS, thread_number);
PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
}
@@ -198,19 +195,25 @@ ThreadData* ThreadData::Get() {
// We must be a worker thread, since we didn't pre-register.
ThreadData* worker_thread_data = NULL;
+ size_t thread_number = 0;
{
base::AutoLock lock(*list_lock_.Pointer());
- if (unregistered_thread_data_pool_ &&
- !unregistered_thread_data_pool_->empty()) {
+ if (!unregistered_thread_data_pool_)
+ unregistered_thread_data_pool_ = new ThreadDataPool;
+ if (!unregistered_thread_data_pool_->empty()) {
worker_thread_data =
const_cast<ThreadData*>(unregistered_thread_data_pool_->top());
unregistered_thread_data_pool_->pop();
+ } else {
+ thread_number = ++thread_number_counter_;
+ unregistered_thread_data_pool_->reserve(thread_number);
}
}
// If we can't find a previously used instance, then we have to create one.
if (!worker_thread_data)
- worker_thread_data = new ThreadData();
+ worker_thread_data = new ThreadData(thread_number);
+ DCHECK_GT(worker_thread_data->worker_thread_number_, 0u);
tls_index_.Set(worker_thread_data);
return worker_thread_data;
@@ -226,18 +229,12 @@ void ThreadData::OnThreadTermination(void* thread_data) {
}
void ThreadData::OnThreadTerminationCleanup() const {
- if (!is_a_worker_thread_)
+ if (!worker_thread_number_)
return;
base::AutoLock lock(*list_lock_.Pointer());
if (incarnation_counter_ != incarnation_count_for_pool_)
return; // ThreadData was constructed in an earlier unit test.
-
- // Handle case where we are in unit tests, and have become UNINITIALIZED.
- // In that case, the pool might be NULL. We really should detect this via the
- // incarnation_counter_, but this call is rarely made, so we can afford to
- // code defensively.
- if (!unregistered_thread_data_pool_)
- unregistered_thread_data_pool_ = new ThreadDataPool;
+ // The following will never have to do an allocation.
unregistered_thread_data_pool_->push(this);
}
@@ -562,6 +559,49 @@ void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
}
//------------------------------------------------------------------------------
+// Small partial implementation of a stack that never has to allocate during a
+// push() operation, because it is always prepared to accept the maximum number
+// of ThreadData instances (all the worker thread related instances).
+
+ThreadData::ThreadDataPool::ThreadDataPool() : empty_slot_(0) {};
+ThreadData::ThreadDataPool::~ThreadDataPool() {};
+
+bool ThreadData::ThreadDataPool::empty() const { return empty_slot_ == 0; }
+
+void ThreadData::ThreadDataPool::reserve(size_t largest_worker_pool_number) {
+ // Worker pool numbers start at 1, and exclude 0, so the number is exactly
+ // the least size needed.
+ // Due to asynchronous construction of worker-pool numbers (and associated
+ // ThreadData), we might not hear about the numbers sequentially.
+ if (largest_worker_pool_number > stack_.size())
+ stack_.resize(largest_worker_pool_number);
+}
+
+const ThreadData* ThreadData::ThreadDataPool::top() const {
+ if (empty_slot_ > 0)
+ return stack_[empty_slot_ - 1];
+ NOTREACHED();
+ return NULL;
+}
+
+void ThreadData::ThreadDataPool::push(const ThreadData* thread_data) {
+ if (empty_slot_ < stack_.size()) {
+ stack_[empty_slot_] = thread_data;
+ ++empty_slot_;
+ return;
+ }
+ NOTREACHED();
+}
+
+void ThreadData::ThreadDataPool::pop() {
+ if (empty_slot_ > 0) {
+ --empty_slot_;
+ return;
+ }
+ NOTREACHED();
+}
+
+//------------------------------------------------------------------------------
// Individual 3-tuple of birth (place and thread) along with death thread, and
// the accumulated stats for instances (DeathData).
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
index 90ddc7d..dd1c7c4 100644
--- a/base/tracked_objects.h
+++ b/base/tracked_objects.h
@@ -525,10 +525,31 @@ class BASE_EXPORT ThreadData {
// in production code.
friend class TrackedObjectsTest;
- typedef std::stack<const ThreadData*> ThreadDataPool;
+ // Implment a stack that avoids allocations during a push() by having enough
+ // space ahead of time.
+ class ThreadDataPool {
+ public:
+ ThreadDataPool();
+ ~ThreadDataPool();
+
+ // Make sure the stack is large enough to support the indicated number of
+ // elements.
+ void reserve(size_t largest_worker_pool_number);
+
+ bool empty() const;
+ const ThreadData* top() const;
+ void push(const ThreadData* thread_data);
+ void pop();
+
+ private:
+ std::vector<const ThreadData*> stack_;
+ size_t empty_slot_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadDataPool);
+ };
// Worker thread construction creates a name since there is none.
- ThreadData();
+ explicit ThreadData(size_t thread_number);
+
// Message loop based construction should provide a name.
explicit ThreadData(const std::string& suggested_name);
@@ -616,7 +637,9 @@ class BASE_EXPORT ThreadData {
// Indicate if this is a worker thread, and the ThreadData contexts should be
// stored in the unregistered_thread_data_pool_ when not in use.
- bool is_a_worker_thread_;
+ // Value is zero when it is not a worker thread. Value is a positive integer
+ // corresponding to the created thread name if it is a worker thread.
+ size_t worker_thread_number_;
// A map used on each thread to keep track of Births on this thread.
// This map should only be accessed on the thread it was constructed on.