scottbyer@chromium.org | 189aa71 | 2012-07-26 05:36:33 +0900 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/at_exit.h" |
jhawkins@chromium.org | 8e73d06 | 2011-04-05 03:04:37 +0900 | [diff] [blame] | 6 | |
| 7 | #include <stddef.h> |
| 8 | #include <ostream> |
dcheng | 7dc9137 | 2016-07-06 12:59:26 +0900 | [diff] [blame] | 9 | #include <utility> |
jhawkins@chromium.org | 8e73d06 | 2011-04-05 03:04:37 +0900 | [diff] [blame] | 10 | |
apatrick@chromium.org | 035f4af | 2011-09-07 08:14:47 +0900 | [diff] [blame] | 11 | #include "base/bind.h" |
ajwong@chromium.org | e4f3dc3 | 2012-01-07 07:12:28 +0900 | [diff] [blame] | 12 | #include "base/callback.h" |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 13 | #include "base/logging.h" |
| 14 | |
| 15 | namespace base { |
| 16 | |
| 17 | // Keep a stack of registered AtExitManagers. We always operate on the most |
rvargas@google.com | 3e37cca | 2011-04-23 07:09:35 +0900 | [diff] [blame] | 18 | // recent, and we should never have more than one outside of testing (for a |
| 19 | // statically linked version of this library). Testing may use the shadow |
| 20 | // version of the constructor, and if we are building a dynamic library we may |
| 21 | // end up with multiple AtExitManagers on the same process. We don't protect |
| 22 | // this for thread-safe access, since it will only be modified in testing. |
Ivan Kotenkov | e88f346 | 2017-11-08 21:37:33 +0900 | [diff] [blame] | 23 | static AtExitManager* g_top_manager = nullptr; |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 24 | |
haraken | df522bd | 2017-01-12 16:14:04 +0900 | [diff] [blame] | 25 | static bool g_disable_managers = false; |
| 26 | |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 27 | AtExitManager::AtExitManager() |
| 28 | : processing_callbacks_(false), next_manager_(g_top_manager) { |
rvargas@google.com | 3e37cca | 2011-04-23 07:09:35 +0900 | [diff] [blame] | 29 | // If multiple modules instantiate AtExitManagers they'll end up living in this |
| 30 | // module... they have to coexist. |
darin@chromium.org | 86d9f94 | 2011-07-14 05:41:28 +0900 | [diff] [blame] | 31 | #if !defined(COMPONENT_BUILD) |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 32 | DCHECK(!g_top_manager); |
rvargas@google.com | 3e37cca | 2011-04-23 07:09:35 +0900 | [diff] [blame] | 33 | #endif |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 34 | g_top_manager = this; |
| 35 | } |
| 36 | |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 37 | AtExitManager::~AtExitManager() { |
| 38 | if (!g_top_manager) { |
| 39 | NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; |
| 40 | return; |
| 41 | } |
kushi.p@gmail.com | 90594e3 | 2011-04-29 03:20:09 +0900 | [diff] [blame] | 42 | DCHECK_EQ(this, g_top_manager); |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 43 | |
haraken | df522bd | 2017-01-12 16:14:04 +0900 | [diff] [blame] | 44 | if (!g_disable_managers) |
| 45 | ProcessCallbacksNow(); |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 46 | g_top_manager = next_manager_; |
| 47 | } |
| 48 | |
| 49 | // static |
deanm@google.com | f6299e7 | 2008-09-08 18:06:51 +0900 | [diff] [blame] | 50 | void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { |
apatrick@chromium.org | 035f4af | 2011-09-07 08:14:47 +0900 | [diff] [blame] | 51 | DCHECK(func); |
| 52 | RegisterTask(base::Bind(func, param)); |
| 53 | } |
| 54 | |
| 55 | // static |
| 56 | void AtExitManager::RegisterTask(base::Closure task) { |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 57 | if (!g_top_manager) { |
| 58 | NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; |
| 59 | return; |
| 60 | } |
| 61 | |
| 62 | AutoLock lock(g_top_manager->lock_); |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 63 | DCHECK(!g_top_manager->processing_callbacks_); |
dcheng | 7dc9137 | 2016-07-06 12:59:26 +0900 | [diff] [blame] | 64 | g_top_manager->stack_.push(std::move(task)); |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | // static |
| 68 | void AtExitManager::ProcessCallbacksNow() { |
| 69 | if (!g_top_manager) { |
| 70 | NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; |
| 71 | return; |
| 72 | } |
| 73 | |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 74 | // Callbacks may try to add new callbacks, so run them without holding |
| 75 | // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but |
| 76 | // handle it gracefully in release builds so we don't deadlock. |
Brett Wilson | 19fd2bb | 2017-10-03 03:55:28 +0900 | [diff] [blame] | 77 | base::stack<base::Closure> tasks; |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 78 | { |
| 79 | AutoLock lock(g_top_manager->lock_); |
| 80 | tasks.swap(g_top_manager->stack_); |
| 81 | g_top_manager->processing_callbacks_ = true; |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 82 | } |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 83 | |
tzik | 8a08348 | 2017-04-01 06:31:24 +0900 | [diff] [blame] | 84 | // Relax the cross-thread access restriction to non-thread-safe RefCount. |
| 85 | // It's safe since all other threads should be terminated at this point. |
| 86 | ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access; |
| 87 | |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 88 | while (!tasks.empty()) { |
| 89 | base::Closure task = tasks.top(); |
| 90 | task.Run(); |
| 91 | tasks.pop(); |
| 92 | } |
| 93 | |
| 94 | // Expect that all callbacks have been run. |
| 95 | DCHECK(g_top_manager->stack_.empty()); |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 96 | } |
| 97 | |
haraken | df522bd | 2017-01-12 16:14:04 +0900 | [diff] [blame] | 98 | void AtExitManager::DisableAllAtExitManagers() { |
| 99 | AutoLock lock(g_top_manager->lock_); |
| 100 | g_disable_managers = true; |
| 101 | } |
| 102 | |
amistry | 11fd640 | 2016-02-10 11:18:33 +0900 | [diff] [blame] | 103 | AtExitManager::AtExitManager(bool shadow) |
| 104 | : processing_callbacks_(false), next_manager_(g_top_manager) { |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 105 | DCHECK(shadow || !g_top_manager); |
| 106 | g_top_manager = this; |
| 107 | } |
| 108 | |
mmentovai@google.com | aa13be6 | 2008-09-03 03:20:34 +0900 | [diff] [blame] | 109 | } // namespace base |