Index: base/at_exit.cc |
diff --git a/base/at_exit.cc b/base/at_exit.cc |
index 0fba355698fbebc643fd3225d1323ec64964eb7d..f9aa2d1094e9c65aaf60df9c7681c606c7ec4e27 100644 |
--- a/base/at_exit.cc |
+++ b/base/at_exit.cc |
@@ -21,7 +21,8 @@ namespace base { |
// this for thread-safe access, since it will only be modified in testing. |
static AtExitManager* g_top_manager = NULL; |
-AtExitManager::AtExitManager() : next_manager_(g_top_manager) { |
+AtExitManager::AtExitManager() |
+ : processing_callbacks_(false), next_manager_(g_top_manager) { |
// If multiple modules instantiate AtExitManagers they'll end up living in this |
// module... they have to coexist. |
#if !defined(COMPONENT_BUILD) |
@@ -55,6 +56,7 @@ void AtExitManager::RegisterTask(base::Closure task) { |
} |
AutoLock lock(g_top_manager->lock_); |
+ DCHECK(!g_top_manager->processing_callbacks_); |
g_top_manager->stack_.push(task); |
} |
@@ -65,16 +67,28 @@ void AtExitManager::ProcessCallbacksNow() { |
return; |
} |
- AutoLock lock(g_top_manager->lock_); |
+ // Callbacks may try to add new callbacks, so run them without holding |
+ // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but |
+ // handle it gracefully in release builds so we don't deadlock. |
+ std::stack<base::Closure> tasks; |
+ { |
+ AutoLock lock(g_top_manager->lock_); |
+ tasks.swap(g_top_manager->stack_); |
+ g_top_manager->processing_callbacks_ = true; |
+ } |
- while (!g_top_manager->stack_.empty()) { |
- base::Closure task = g_top_manager->stack_.top(); |
+ while (!tasks.empty()) { |
+ base::Closure task = tasks.top(); |
task.Run(); |
- g_top_manager->stack_.pop(); |
+ tasks.pop(); |
} |
+ |
+ // Expect that all callbacks have been run. |
+ DCHECK(g_top_manager->stack_.empty()); |
} |
-AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { |
+AtExitManager::AtExitManager(bool shadow) |
+ : processing_callbacks_(false), next_manager_(g_top_manager) { |
DCHECK(shadow || !g_top_manager); |
g_top_manager = this; |
} |