Chromium Code Reviews| Index: base/at_exit.cc |
| diff --git a/base/at_exit.cc b/base/at_exit.cc |
| index 0fba355698fbebc643fd3225d1323ec64964eb7d..695620a416498e307ed8088d24187d39f5e00da0 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_); |
|
Mark Mentovai
2016/02/08 15:15:45
Maybe this should be a CHECK.
Anand Mistry (off Chromium)
2016/02/08 23:12:25
I didn't want to break release builds, only catch
Mark Mentovai
2016/02/09 15:37:46
Anand Mistry wrote:
|
| g_top_manager->stack_.push(task); |
| } |
| @@ -65,16 +67,28 @@ void AtExitManager::ProcessCallbacksNow() { |
| return; |
| } |
| - AutoLock lock(g_top_manager->lock_); |
| + // Callbacks may add new callbacks, so run them without holding |lock_|. This |
|
Mark Mentovai
2016/02/08 15:15:45
may add→may try to add
Anand Mistry (off Chromium)
2016/02/08 23:12:25
Done.
|
| + // 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; |
| } |