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 AtExitManager::AtExitManager()
     26     : processing_callbacks_(false), next_manager_(g_top_manager) {
     27 // If multiple modules instantiate AtExitManagers they'll end up living in this
     28 // module... they have to coexist.
     29 #if !defined(COMPONENT_BUILD)
     30   DCHECK(!g_top_manager);
     31 #endif
     32   g_top_manager = this;
     33 }
     34 
     35 AtExitManager::~AtExitManager() {
     36   if (!g_top_manager) {
     37     NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
     38     return;
     39   }
     40   DCHECK_EQ(this, g_top_manager);
     41 
     42   ProcessCallbacksNow();
     43   g_top_manager = next_manager_;
     44 }
     45 
     46 // static
     47 void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
     48   DCHECK(func);
     49   RegisterTask(base::Bind(func, param));
     50 }
     51 
     52 // static
     53 void AtExitManager::RegisterTask(base::Closure task) {
     54   if (!g_top_manager) {
     55     NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
     56     return;
     57   }
     58 
     59   AutoLock lock(g_top_manager->lock_);
     60   DCHECK(!g_top_manager->processing_callbacks_);
     61   g_top_manager->stack_.push(std::move(task));
     62 }
     63 
     64 // static
     65 void AtExitManager::ProcessCallbacksNow() {
     66   if (!g_top_manager) {
     67     NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
     68     return;
     69   }
     70 
     71   // Callbacks may try to add new callbacks, so run them without holding
     72   // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
     73   // handle it gracefully in release builds so we don't deadlock.
     74   std::stack<base::Closure> tasks;
     75   {
     76     AutoLock lock(g_top_manager->lock_);
     77     tasks.swap(g_top_manager->stack_);
     78     g_top_manager->processing_callbacks_ = true;
     79   }
     80 
     81   while (!tasks.empty()) {
     82     base::Closure task = tasks.top();
     83     task.Run();
     84     tasks.pop();
     85   }
     86 
     87   // Expect that all callbacks have been run.
     88   DCHECK(g_top_manager->stack_.empty());
     89 }
     90 
     91 AtExitManager::AtExitManager(bool shadow)
     92     : processing_callbacks_(false), next_manager_(g_top_manager) {
     93   DCHECK(shadow || !g_top_manager);
     94   g_top_manager = this;
     95 }
     96 
     97 }  // namespace base
     98