Home | History | Annotate | Download | only in base
      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     : processing_callbacks_(false), 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::Bind(func, param));
     53 }
     54 
     55 // static
     56 void AtExitManager::RegisterTask(base::Closure 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   DCHECK(!g_top_manager->processing_callbacks_);
     64   g_top_manager->stack_.push(std::move(task));
     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 
     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.
     77   std::stack<base::Closure> tasks;
     78   {
     79     AutoLock lock(g_top_manager->lock_);
     80     tasks.swap(g_top_manager->stack_);
     81     g_top_manager->processing_callbacks_ = true;
     82   }
     83 
     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 
     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());
     96 }
     97 
     98 void AtExitManager::DisableAllAtExitManagers() {
     99   AutoLock lock(g_top_manager->lock_);
    100   g_disable_managers = true;
    101 }
    102 
    103 AtExitManager::AtExitManager(bool shadow)
    104     : processing_callbacks_(false), next_manager_(g_top_manager) {
    105   DCHECK(shadow || !g_top_manager);
    106   g_top_manager = this;
    107 }
    108 
    109 }  // namespace base
    110