Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : * Copyright Contributors to the OpenSDN project 4 : */ 5 : 6 : #include "base/lifetime.h" 7 : 8 : #include <boost/bind/bind.hpp> 9 : #include "base/backtrace.h" 10 : #include "base/time_util.h" 11 : 12 : using namespace boost::placeholders; 13 : 14 1224282 : LifetimeRefBase::LifetimeRefBase(LifetimeActor *actor) 15 1224282 : : ref_(this, actor) { 16 1224352 : } 17 : 18 1224832 : LifetimeRefBase::~LifetimeRefBase() { 19 1224832 : } 20 : 21 1035212 : LifetimeActor::LifetimeActor(LifetimeManager *manager) 22 1035074 : : manager_(manager), refcount_(0), shutdown_invoked_(false), 23 1035074 : delete_paused_(false), 24 1035074 : create_time_stamp_usecs_(UTCTimestampUsec()), 25 2070615 : delete_time_stamp_usecs_(0) { 26 1035176 : deleted_ = false; 27 1035533 : } 28 : 29 1035560 : LifetimeActor::~LifetimeActor() { 30 1035560 : assert(refcount_ == 0); 31 1035560 : assert(dependents_.empty()); 32 1035560 : } 33 : 34 : // 35 : // Concurrency: called in the context of any Task or the main thread. 36 : // 37 : // Used to trigger delete for managed object and it's dependents. Enqueue 38 : // this actor to the Lifetime Manager. Propagation of the delete operation 39 : // to dependents happens in the context of the LifetimeManager's Task. 40 : // 41 1058382 : void LifetimeActor::Delete() { 42 1058382 : std::scoped_lock lock(mutex_); 43 1058382 : if (deleted_.exchange(true)) { 44 24372 : return; 45 : } 46 1034010 : delete_time_stamp_usecs_ = UTCTimestampUsec(); 47 1034010 : refcount_++; 48 1034010 : manager_->EnqueueNoIncrement(this); 49 1058382 : } 50 : 51 : // 52 : // Concurrency: called in the context of the LifetimeManager's Task. 53 : // 54 : // Used to propagate delete for a managed object to it's dependents. 55 : // 56 : // Walk the list of dependent LifetimeRefs and propagate the delete. A mutex is 57 : // used to ensure that the dependent list does not change while we are walking 58 : // through it. 59 : // 60 1034010 : void LifetimeActor::PropagateDelete() { 61 1034010 : assert(deleted_); 62 1034010 : std::scoped_lock lock(mutex_); 63 1034010 : for (Dependents::iterator iter = dependents_.begin(); 64 2166604 : iter != dependents_.end(); ++iter) { 65 1132594 : iter->Delete(); 66 : } 67 1034010 : } 68 : 69 : // 70 : // Concurrency: called in the context of any Task or the main thread. 71 : // 72 : // Enqueue a delete event for this actor to the LifetimeManager. 73 : // 74 450080 : void LifetimeActor::RetryDelete() { 75 450080 : assert(deleted_); 76 450079 : manager_->Enqueue(this); 77 450093 : } 78 : 79 : // 80 : // Concurrency: called in the context of the LifetimeManager's Task. 81 : // 82 : // Called immediately before the object is destroyed. 83 : // 84 1034008 : void LifetimeActor::DeleteComplete() { 85 1034008 : } 86 : 87 : // 88 : // Concurrency: called in the context of the LifetimeManager's Task. 89 : // 90 : // Can be called multiple times - when the managed object is initially deleted 91 : // and whenever a delete event is enqueued to the Lifetime Manager. The latter 92 : // happens when the list of dependents becomes empty or the refcount of the 93 : // lightweight dependents goes to 0. 94 : // 95 171796 : void LifetimeActor::Shutdown() { 96 171796 : } 97 : 98 : // 99 : // Concurrency: called in the context of main thread. 100 : // TaskScheduler should be stopped prior to invoking this method. 101 : // 102 : // Prevent object from getting destroyed - testing only. 103 : // 104 2617 : void LifetimeActor::PauseDelete() { 105 2617 : std::scoped_lock lock(mutex_); 106 2617 : assert(!deleted_); 107 2617 : delete_paused_ = true; 108 2617 : } 109 : 110 : // 111 : // Concurrency: called in the context of main thread. 112 : // TaskScheduler should be stopped prior to invoking this method. 113 : // 114 : // Allow object to get destroyed - testing only. 115 : // 116 2617 : void LifetimeActor::ResumeDelete() { 117 2617 : std::scoped_lock lock(mutex_); 118 2617 : assert(deleted_); 119 2617 : delete_paused_ = false; 120 2617 : refcount_++; 121 2617 : manager_->EnqueueNoIncrement(this); 122 2617 : } 123 : 124 : // 125 : // Concurrency: called in the context of any Task. 126 : // 127 : // Add the LifetimeRef as a dependent for this Actor. 128 : // 129 1212166 : void LifetimeActor::DependencyAdd( 130 : DependencyRef<LifetimeRefBase, LifetimeActor> *node) { 131 1212166 : std::scoped_lock lock(mutex_); 132 1212286 : assert(!deleted_); 133 1212162 : dependents_.Add(node); 134 1211948 : } 135 : 136 : // 137 : // Concurrency: called in the context of the LifetimeManager's Task. 138 : // 139 : // Remove the LifetimeRef as a dependent of this Actor. Note that this can 140 : // happen when the dependent object itself is being deleted i.e. this actor 141 : // itself need not be marked deleted. 142 : // 143 1212382 : void LifetimeActor::DependencyRemove( 144 : DependencyRef<LifetimeRefBase, LifetimeActor> *node) { 145 1212382 : std::scoped_lock lock(mutex_); 146 1212389 : dependents_.Remove(node); 147 1212378 : if (deleted_ && dependents_.empty()) { 148 358964 : refcount_++; 149 358964 : manager_->EnqueueNoIncrement(this); 150 : } 151 1212378 : } 152 : 153 : // When the actor is placed in the queue the caller must still hold an 154 : // "lock" on the object in the form of either a dependency or an 155 : // explicit test performed by the derived class MayDelete() method. 156 450079 : void LifetimeActor::ReferenceIncrement() { 157 450079 : std::scoped_lock lock(mutex_); 158 450088 : refcount_++; 159 450088 : } 160 : 161 1845684 : bool LifetimeActor::ReferenceDecrementAndTest() { 162 1845684 : std::scoped_lock lock(mutex_); 163 1845684 : refcount_--; 164 2911729 : return (refcount_ == 0 && dependents_.empty() && !delete_paused_ && 165 2911729 : MayDelete()); 166 1845684 : } 167 : 168 17443 : LifetimeManager::LifetimeManager(int task_id) 169 17443 : : defer_count_(0), 170 17443 : queue_(task_id, 0, 171 17443 : boost::bind(&LifetimeManager::DeleteExecutor, this, _1)) { 172 17443 : queue_.set_name("LifetimeManager"); 173 17443 : } 174 : 175 22952 : LifetimeManager::~LifetimeManager() { 176 17428 : queue_.Shutdown(); 177 22952 : } 178 : 179 : // 180 : // Disable/Enable the WorkQueue - testing only. 181 : // 182 96 : void LifetimeManager::SetQueueDisable(bool disabled) { 183 96 : queue_.set_disable(disabled); 184 96 : } 185 : 186 : // 187 : // Concurrency: called in the context of any Task or the main thread. 188 : // 189 : // Enqueue a delete event for the actor. 190 : // 191 450079 : void LifetimeManager::Enqueue(LifetimeActor *actor) { 192 : LifetimeActorRef actor_ref; 193 450079 : actor->ReferenceIncrement(); 194 450090 : actor_ref.actor = actor; 195 450090 : queue_.Enqueue(actor_ref); 196 450093 : } 197 : 198 1938545 : void LifetimeManager::EnqueueNoIncrement(LifetimeActor *actor) { 199 : LifetimeActorRef actor_ref; 200 1938545 : actor_ref.actor = actor; 201 1938545 : queue_.Enqueue(actor_ref); 202 1938545 : } 203 : 204 : // 205 : // Concurrency: called in the context of the LifetimeManager's Task. 206 : // 207 : // If this is the first time that the delete actor is being processed, we 208 : // propagate the delete to it's dependents and ask the managed object to 209 : // shut itself i.e. take care of cleaning up any state not represented as 210 : // an explicit LifetimeRef dependent. 211 : // 212 : // If global conditions for object destruction are not satisfied, enqueue 213 : // the delete actor again and defer processing of the queue. Do not bump 214 : // up the refcount in this case. 215 : // Else go ahead and destroy the object if all conditions are satisfied. 216 : // 217 2388638 : bool LifetimeManager::DeleteExecutor(LifetimeActorRef actor_ref) { 218 2388638 : LifetimeActor *actor = actor_ref.actor; 219 2388638 : assert(actor->IsDeleted()); 220 2388638 : if (!actor->shutdown_invoked()) { 221 1034010 : actor->PropagateDelete(); 222 1034010 : actor->Shutdown(); 223 1034010 : actor->set_shutdown_invoked(); 224 : } 225 2388638 : if (!MayDestroy()) { 226 542954 : EnqueueNoIncrement(actor); 227 542954 : defer_count_++; 228 542954 : return false; 229 : } 230 1845684 : if (actor->ReferenceDecrementAndTest()) { 231 1034008 : actor->DeleteComplete(); 232 1034008 : actor->Destroy(); 233 : } 234 1845684 : return true; 235 : }