Coverage Report

Created: 2018-09-25 14:53

/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