blob: a71988fc446c93a26f947bf90e46b1fd7c3ad22d [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2011 The Chromium Authors
mmentovai@google.comb2e97292008-09-02 18:20:342// 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.org2edc2862011-04-04 18:04:376
7#include <stddef.h>
8#include <ostream>
dchengd02ce9c2016-07-06 03:59:269#include <utility>
jhawkins@chromium.org2edc2862011-04-04 18:04:3710
Hans Wennborgc3cffa62020-04-27 10:09:1211#include "base/check_op.h"
Avi Drissman63e1f992023-01-13 18:54:4312#include "base/functional/bind.h"
13#include "base/functional/callback.h"
Hans Wennborgc3cffa62020-04-27 10:09:1214#include "base/notreached.h"
mmentovai@google.comb2e97292008-09-02 18:20:3415
16namespace base {
17
18// Keep a stack of registered AtExitManagers. We always operate on the most
rvargas@google.com5ceb1b02011-04-22 22:09:3519// recent, and we should never have more than one outside of testing (for a
20// statically linked version of this library). Testing may use the shadow
21// version of the constructor, and if we are building a dynamic library we may
22// end up with multiple AtExitManagers on the same process. We don't protect
23// this for thread-safe access, since it will only be modified in testing.
Ivan Kotenkova16212a52017-11-08 12:37:3324static AtExitManager* g_top_manager = nullptr;
mmentovai@google.comb2e97292008-09-02 18:20:3425
harakenbbfdd9f02017-01-12 07:14:0426static bool g_disable_managers = false;
27
Gabriel Charette24e83392018-10-30 23:09:0328AtExitManager::AtExitManager() : next_manager_(g_top_manager) {
rvargas@google.com5ceb1b02011-04-22 22:09:3529// If multiple modules instantiate AtExitManagers they'll end up living in this
30// module... they have to coexist.
darin@chromium.org63e39a282011-07-13 20:41:2831#if !defined(COMPONENT_BUILD)
mmentovai@google.comb2e97292008-09-02 18:20:3432 DCHECK(!g_top_manager);
rvargas@google.com5ceb1b02011-04-22 22:09:3533#endif
mmentovai@google.comb2e97292008-09-02 18:20:3434 g_top_manager = this;
35}
36
mmentovai@google.comb2e97292008-09-02 18:20:3437AtExitManager::~AtExitManager() {
38 if (!g_top_manager) {
39 NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
40 return;
41 }
kushi.p@gmail.com5e0be642011-04-28 18:20:0942 DCHECK_EQ(this, g_top_manager);
mmentovai@google.comb2e97292008-09-02 18:20:3443
harakenbbfdd9f02017-01-12 07:14:0444 if (!g_disable_managers)
45 ProcessCallbacksNow();
mmentovai@google.comb2e97292008-09-02 18:20:3446 g_top_manager = next_manager_;
47}
48
49// static
deanm@google.com9795ec12008-09-08 09:06:5150void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
apatrick@chromium.org762de912011-09-06 23:14:4751 DCHECK(func);
kylecharb2695fc2019-04-24 14:51:2052 RegisterTask(base::BindOnce(func, param));
apatrick@chromium.org762de912011-09-06 23:14:4753}
54
55// static
kylecharb2695fc2019-04-24 14:51:2056void AtExitManager::RegisterTask(base::OnceClosure task) {
mmentovai@google.comb2e97292008-09-02 18:20:3457 if (!g_top_manager) {
58 NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
59 return;
60 }
61
62 AutoLock lock(g_top_manager->lock_);
Gabriel Charette24e83392018-10-30 23:09:0363#if DCHECK_IS_ON()
amistryf35088d2016-02-10 02:18:3364 DCHECK(!g_top_manager->processing_callbacks_);
Gabriel Charette24e83392018-10-30 23:09:0365#endif
dchengd02ce9c2016-07-06 03:59:2666 g_top_manager->stack_.push(std::move(task));
mmentovai@google.comb2e97292008-09-02 18:20:3467}
68
69// static
70void AtExitManager::ProcessCallbacksNow() {
71 if (!g_top_manager) {
72 NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
73 return;
74 }
75
amistryf35088d2016-02-10 02:18:3376 // Callbacks may try to add new callbacks, so run them without holding
77 // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
78 // handle it gracefully in release builds so we don't deadlock.
kylecharb2695fc2019-04-24 14:51:2079 base::stack<base::OnceClosure> tasks;
amistryf35088d2016-02-10 02:18:3380 {
81 AutoLock lock(g_top_manager->lock_);
82 tasks.swap(g_top_manager->stack_);
Gabriel Charette24e83392018-10-30 23:09:0383#if DCHECK_IS_ON()
amistryf35088d2016-02-10 02:18:3384 g_top_manager->processing_callbacks_ = true;
Gabriel Charette24e83392018-10-30 23:09:0385#endif
mmentovai@google.comb2e97292008-09-02 18:20:3486 }
amistryf35088d2016-02-10 02:18:3387
tzik3f4231f2017-03-31 21:31:2488 // Relax the cross-thread access restriction to non-thread-safe RefCount.
89 // It's safe since all other threads should be terminated at this point.
90 ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
91
amistryf35088d2016-02-10 02:18:3392 while (!tasks.empty()) {
kylecharb2695fc2019-04-24 14:51:2093 std::move(tasks.top()).Run();
amistryf35088d2016-02-10 02:18:3394 tasks.pop();
95 }
96
Gabriel Charette24e83392018-10-30 23:09:0397#if DCHECK_IS_ON()
98 AutoLock lock(g_top_manager->lock_);
amistryf35088d2016-02-10 02:18:3399 // Expect that all callbacks have been run.
100 DCHECK(g_top_manager->stack_.empty());
Gabriel Charette24e83392018-10-30 23:09:03101 g_top_manager->processing_callbacks_ = false;
102#endif
mmentovai@google.comb2e97292008-09-02 18:20:34103}
104
harakenbbfdd9f02017-01-12 07:14:04105void AtExitManager::DisableAllAtExitManagers() {
106 AutoLock lock(g_top_manager->lock_);
107 g_disable_managers = true;
108}
109
Gabriel Charette24e83392018-10-30 23:09:03110AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
erg@google.comeae9c062011-01-11 00:50:59111 DCHECK(shadow || !g_top_manager);
112 g_top_manager = this;
113}
114
mmentovai@google.comb2e97292008-09-02 18:20:34115} // namespace base