tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

at_exit.cc (3352B)


      1 // Copyright 2011 The Chromium Authors
      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/check_op.h"
     12 #include "base/functional/bind.h"
     13 #include "base/functional/callback.h"
     14 #include "base/notreached.h"
     15 
     16 namespace base {
     17 
     18 // Keep a stack of registered AtExitManagers.  We always operate on the most
     19 // 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.
     24 static AtExitManager* g_top_manager = nullptr;
     25 
     26 static bool g_disable_managers = false;
     27 
     28 AtExitManager::AtExitManager() : next_manager_(g_top_manager) {
     29 // If multiple modules instantiate AtExitManagers they'll end up living in this
     30 // module... they have to coexist.
     31 #if !defined(COMPONENT_BUILD)
     32  DCHECK(!g_top_manager);
     33 #endif
     34  g_top_manager = this;
     35 }
     36 
     37 AtExitManager::~AtExitManager() {
     38  if (!g_top_manager) {
     39    NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
     40    return;
     41  }
     42  DCHECK_EQ(this, g_top_manager);
     43 
     44  if (!g_disable_managers)
     45    ProcessCallbacksNow();
     46  g_top_manager = next_manager_;
     47 }
     48 
     49 // static
     50 void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
     51  DCHECK(func);
     52  RegisterTask(base::BindOnce(func, param));
     53 }
     54 
     55 // static
     56 void AtExitManager::RegisterTask(base::OnceClosure task) {
     57  if (!g_top_manager) {
     58    NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
     59    return;
     60  }
     61 
     62  AutoLock lock(g_top_manager->lock_);
     63 #if DCHECK_IS_ON()
     64  DCHECK(!g_top_manager->processing_callbacks_);
     65 #endif
     66  g_top_manager->stack_.push(std::move(task));
     67 }
     68 
     69 // static
     70 void AtExitManager::ProcessCallbacksNow() {
     71  if (!g_top_manager) {
     72    NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
     73    return;
     74  }
     75 
     76  // 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.
     79  base::stack<base::OnceClosure> tasks;
     80  {
     81    AutoLock lock(g_top_manager->lock_);
     82    tasks.swap(g_top_manager->stack_);
     83 #if DCHECK_IS_ON()
     84    g_top_manager->processing_callbacks_ = true;
     85 #endif
     86  }
     87 
     88  // 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 
     92  while (!tasks.empty()) {
     93    std::move(tasks.top()).Run();
     94    tasks.pop();
     95  }
     96 
     97 #if DCHECK_IS_ON()
     98  AutoLock lock(g_top_manager->lock_);
     99  // Expect that all callbacks have been run.
    100  DCHECK(g_top_manager->stack_.empty());
    101  g_top_manager->processing_callbacks_ = false;
    102 #endif
    103 }
    104 
    105 void AtExitManager::DisableAllAtExitManagers() {
    106  AutoLock lock(g_top_manager->lock_);
    107  g_disable_managers = true;
    108 }
    109 
    110 AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
    111  DCHECK(shadow || !g_top_manager);
    112  g_top_manager = this;
    113 }
    114 
    115 }  // namespace base