/src/mozilla-central/security/sandbox/chromium/base/at_exit.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
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" |
6 | | |
7 | | #include <stddef.h> |
8 | | #include <ostream> |
9 | | #include <utility> |
10 | | |
11 | | #include "base/bind.h" |
12 | | #include "base/callback.h" |
13 | | #include "base/logging.h" |
14 | | |
15 | | namespace base { |
16 | | |
17 | | // Keep a stack of registered AtExitManagers. We always operate on the most |
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. |
23 | | static AtExitManager* g_top_manager = NULL; |
24 | | |
25 | | static bool g_disable_managers = false; |
26 | | |
27 | | AtExitManager::AtExitManager() |
28 | 0 | : processing_callbacks_(false), next_manager_(g_top_manager) { |
29 | 0 | // If multiple modules instantiate AtExitManagers they'll end up living in this |
30 | 0 | // module... they have to coexist. |
31 | 0 | #if !defined(COMPONENT_BUILD) |
32 | 0 | DCHECK(!g_top_manager); |
33 | 0 | #endif |
34 | 0 | g_top_manager = this; |
35 | 0 | } |
36 | | |
37 | 0 | AtExitManager::~AtExitManager() { |
38 | 0 | if (!g_top_manager) { |
39 | 0 | NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; |
40 | 0 | return; |
41 | 0 | } |
42 | 0 | DCHECK_EQ(this, g_top_manager); |
43 | 0 |
|
44 | 0 | if (!g_disable_managers) |
45 | 0 | ProcessCallbacksNow(); |
46 | 0 | g_top_manager = next_manager_; |
47 | 0 | } |
48 | | |
49 | | // static |
50 | 0 | void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { |
51 | 0 | DCHECK(func); |
52 | 0 | RegisterTask(base::Bind(func, param)); |
53 | 0 | } |
54 | | |
55 | | // static |
56 | 0 | void AtExitManager::RegisterTask(base::Closure task) { |
57 | 0 | if (!g_top_manager) { |
58 | 0 | NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; |
59 | 0 | return; |
60 | 0 | } |
61 | 0 |
|
62 | 0 | AutoLock lock(g_top_manager->lock_); |
63 | 0 | DCHECK(!g_top_manager->processing_callbacks_); |
64 | 0 | g_top_manager->stack_.push(std::move(task)); |
65 | 0 | } |
66 | | |
67 | | // static |
68 | 0 | void AtExitManager::ProcessCallbacksNow() { |
69 | 0 | if (!g_top_manager) { |
70 | 0 | NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; |
71 | 0 | return; |
72 | 0 | } |
73 | 0 |
|
74 | 0 | // Callbacks may try to add new callbacks, so run them without holding |
75 | 0 | // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but |
76 | 0 | // handle it gracefully in release builds so we don't deadlock. |
77 | 0 | std::stack<base::Closure> tasks; |
78 | 0 | { |
79 | 0 | AutoLock lock(g_top_manager->lock_); |
80 | 0 | tasks.swap(g_top_manager->stack_); |
81 | 0 | g_top_manager->processing_callbacks_ = true; |
82 | 0 | } |
83 | 0 |
|
84 | 0 | // Relax the cross-thread access restriction to non-thread-safe RefCount. |
85 | 0 | // It's safe since all other threads should be terminated at this point. |
86 | 0 | ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access; |
87 | 0 |
|
88 | 0 | while (!tasks.empty()) { |
89 | 0 | base::Closure task = tasks.top(); |
90 | 0 | task.Run(); |
91 | 0 | tasks.pop(); |
92 | 0 | } |
93 | 0 |
|
94 | 0 | // Expect that all callbacks have been run. |
95 | 0 | DCHECK(g_top_manager->stack_.empty()); |
96 | 0 | } |
97 | | |
98 | 0 | void AtExitManager::DisableAllAtExitManagers() { |
99 | 0 | AutoLock lock(g_top_manager->lock_); |
100 | 0 | g_disable_managers = true; |
101 | 0 | } |
102 | | |
103 | | AtExitManager::AtExitManager(bool shadow) |
104 | 0 | : processing_callbacks_(false), next_manager_(g_top_manager) { |
105 | 0 | DCHECK(shadow || !g_top_manager); |
106 | 0 | g_top_manager = this; |
107 | 0 | } |
108 | | |
109 | | } // namespace base |