nux-1.14.0
|
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- 00002 /* 00003 * Copyright 2010-2011 Inalogic® Inc. 00004 * 00005 * This program is free software: you can redistribute it and/or modify it 00006 * under the terms of the GNU Lesser General Public License, as 00007 * published by the Free Software Foundation; either version 2.1 or 3.0 00008 * of the License. 00009 * 00010 * This program is distributed in the hope that it will be useful, but 00011 * WITHOUT ANY WARRANTY; without even the implied warranties of 00012 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 00013 * PURPOSE. See the applicable version of the GNU Lesser General Public 00014 * License for more details. 00015 * 00016 * You should have received a copy of both the GNU Lesser General Public 00017 * License along with this program. If not, see <http://www.gnu.org/licenses/> 00018 * 00019 * Authored by: Jay Taoko <jaytaoko@inalogic.com> 00020 * 00021 */ 00022 00023 #include <iostream> 00024 #include <sstream> 00025 00026 #include "NuxCore.h" 00027 #include "Object.h" 00028 #include "ObjectPtr.h" 00029 #include "Logger.h" 00030 00031 namespace nux 00032 { 00033 namespace 00034 { 00035 logging::Logger logger("nux.core.object"); 00036 } 00037 00038 NUX_IMPLEMENT_ROOT_OBJECT_TYPE (Trackable); 00039 NUX_IMPLEMENT_OBJECT_TYPE (Object); 00040 NUX_IMPLEMENT_GLOBAL_OBJECT (ObjectStats); 00041 00042 void ObjectStats::Constructor() 00043 { 00044 _total_allocated_size= 0; 00045 _number_of_objects = 0; 00046 } 00047 00048 void ObjectStats::Destructor() 00049 { 00050 #if defined(NUX_DEBUG) 00051 if (_number_of_objects) 00052 { 00053 std::cerr << "[ObjectStats::Destructor] " 00054 << _number_of_objects << " undeleted objects.\n\t" 00055 << _allocation_list.size() << " items in allocation list.\n"; 00056 } 00057 00058 int index = 0; 00059 00060 #if defined(NUX_OS_WINDOWS) 00061 // Visual Studio does not support range based for loops. 00062 for (AllocationList::iterator ptr = _allocation_list.begin(); ptr != _allocation_list.end(); ++ptr) 00063 { 00064 Object* obj = static_cast<Object*>(*ptr); 00065 std::cerr << "\t" << ++index << " Undeleted object: Type " 00066 << obj->Type().name << ", " 00067 << obj->GetAllocationLoation() << "\n"; 00068 } 00069 00070 #else 00071 for (auto ptr : _allocation_list) 00072 { 00073 Object* obj = static_cast<Object*>(ptr); 00074 std::cerr << "\t" << ++index << " Undeleted object: Type " 00075 << obj->Type().name << ", " 00076 << obj->GetAllocationLoation() << "\n"; 00077 } 00078 #endif 00079 #endif 00080 } 00081 00082 std::new_handler Trackable::_new_current_handler = 0; 00083 00084 Trackable::Trackable() 00085 { 00086 _heap_allocated = -1; 00087 00088 _owns_the_reference = false; 00089 } 00090 00091 Trackable::~Trackable() 00092 { 00093 } 00094 00095 bool Trackable::Reference() 00096 { 00097 return false; 00098 } 00099 00100 bool Trackable::UnReference() 00101 { 00102 return false; 00103 } 00104 00105 bool Trackable::SinkReference() 00106 { 00107 return false; 00108 } 00109 00110 bool Trackable::Dispose() 00111 { 00112 return false; 00113 } 00114 00115 bool Trackable::OwnsTheReference() 00116 { 00117 return _owns_the_reference; 00118 } 00119 00120 void Trackable::SetOwnedReference (bool b) 00121 { 00122 if (_owns_the_reference == true) 00123 { 00124 LOG_DEBUG(logger) << "Do not change the ownership if is already set to true!"; 00125 return; 00126 } 00127 00128 _owns_the_reference = b; 00129 } 00130 00131 std::new_handler Trackable::set_new_handler (std::new_handler handler) 00132 { 00133 std::new_handler old_handler = _new_current_handler; 00134 _new_current_handler = handler; 00135 return old_handler; 00136 } 00137 00138 void* Trackable::operator new (size_t size) 00139 { 00140 // Set the new_handler for this call 00141 std::new_handler global_handler = std::set_new_handler (_new_current_handler); 00142 00143 // If allocation fails _new_current_handler is called, if specified, 00144 // otherwise the global new_handler is called. 00145 void *ptr; 00146 00147 try 00148 { 00149 ptr = ::operator new (size); 00150 00151 GObjectStats._allocation_list.push_front (ptr); 00152 NUX_STATIC_CAST (Trackable *, ptr)->_size_of_this_object = size; 00153 GObjectStats._total_allocated_size += size; 00154 ++GObjectStats._number_of_objects; 00155 } 00156 catch (std::bad_alloc &) 00157 { 00158 std::set_new_handler (global_handler); 00159 throw; 00160 } 00161 00162 // Reset gloabal new_handler 00163 std::set_new_handler (global_handler); 00164 return ptr; 00165 } 00166 00167 #if (__GNUC__ < 4 && __GNUC_MINOR__ < 4) 00168 00169 void *Trackable::operator new (size_t size, void *ptr) 00170 { 00171 return ::operator new (size, ptr); 00172 } 00173 00174 #endif 00175 00176 void Trackable::operator delete (void *ptr) 00177 { 00178 ObjectStats::AllocationList::iterator i = std::find (GObjectStats._allocation_list.begin(), GObjectStats._allocation_list.end(), ptr); 00179 00180 if (i != GObjectStats._allocation_list.end() ) 00181 { 00182 GObjectStats._total_allocated_size -= NUX_STATIC_CAST (Trackable *, ptr)->_size_of_this_object; 00183 --GObjectStats._number_of_objects; 00184 GObjectStats._allocation_list.erase (i); 00185 ::operator delete (ptr); 00186 } 00187 } 00188 00189 bool Trackable::IsHeapAllocated() 00190 { 00191 if (_heap_allocated == -1) 00192 { 00193 _heap_allocated = (IsDynamic () ? 1 : 0); 00194 } 00195 00196 return (_heap_allocated == 1 ? true : false); 00197 } 00198 00199 bool Trackable::IsDynamic() const 00200 { 00201 // Get pointer to beginning of the memory occupied by this. 00202 const void *ptr = dynamic_cast<const void *> (this); 00203 00204 // Search for ptr in allocation_list 00205 #if defined(NUX_OS_WINDOWS) && !defined(NUX_VISUAL_STUDIO_2010) 00206 std::list<void*>::iterator i = std::find(GObjectStats._allocation_list.begin(), 00207 GObjectStats._allocation_list.end(), ptr); 00208 #else 00209 auto i = std::find(GObjectStats._allocation_list.begin(), 00210 GObjectStats._allocation_list.end(), ptr); 00211 #endif 00212 return i != GObjectStats._allocation_list.end(); 00213 } 00214 00215 int Trackable::GetObjectSize () 00216 { 00217 return _size_of_this_object; 00218 } 00219 00221 00222 Object::Object(bool OwnTheReference, NUX_FILE_LINE_DECL) 00223 : allocation_file_name_(__Nux_FileName__) 00224 , allocation_line_number_(__Nux_LineNumber__) 00225 , reference_count_(new NThreadSafeCounter()) 00226 , objectptr_count_(new NThreadSafeCounter()) 00227 { 00228 reference_count_->Set(1); 00229 SetOwnedReference(OwnTheReference); 00230 } 00231 00232 Object::~Object() 00233 { 00234 if (IsHeapAllocated()) 00235 { 00236 // If the object has properly been UnReference, it should have gone 00237 // through Destroy(). if that is the case then _reference_count should 00238 // be NULL or its value (returned by GetValue ()) should be equal to 0; 00239 // We can use this to detect when delete is called directly on an 00240 // object. 00241 if (reference_count_->GetValue() > 0) 00242 { 00243 LOG_WARN(logger) << "Invalid object destruction, still has " 00244 << reference_count_->GetValue() << " references." 00245 << "\nObject allocated at: " << GetAllocationLoation(); 00246 } 00247 } 00248 delete reference_count_; 00249 delete objectptr_count_; 00250 } 00251 00252 bool Object::Reference() 00253 { 00254 if (!IsHeapAllocated()) 00255 { 00256 LOG_WARN(logger) << "Trying to reference an object that was not heap allocated." 00257 << "\nObject allocated at: " << GetAllocationLoation(); 00258 return false; 00259 } 00260 00261 if (!OwnsTheReference()) 00262 { 00263 SetOwnedReference(true); 00264 // The ref count remains at 1. Exit the method. 00265 return true; 00266 } 00267 00268 reference_count_->Increment(); 00269 return true; 00270 } 00271 00272 bool Object::UnReference() 00273 { 00274 if (!IsHeapAllocated()) 00275 { 00276 LOG_WARN(logger) << "Trying to un-reference an object that was not heap allocated." 00277 << "\nObject allocated at: " << GetAllocationLoation(); 00278 return false; 00279 } 00280 00281 if (objectptr_count_->GetValue() == reference_count_->GetValue()) 00282 { 00283 // There are ObjectPtr's hosting this object. Release all of them to 00284 // destroy this object. This prevent from calling UnReference () many 00285 // times and destroying the object when there are ObjectPtr's hosting 00286 // it. This method should not be called directly in that case. 00287 LOG_WARN(logger) << "There are ObjectPtr hosting this object. " 00288 << "Release all of them to destroy this object. " 00289 << "\nObject allocated at: " << GetAllocationLoation(); 00290 return false; 00291 } 00292 00293 reference_count_->Decrement(); 00294 00295 if (reference_count_->GetValue() == 0) 00296 { 00297 Destroy(); 00298 return true; 00299 } 00300 00301 return false; 00302 } 00303 00304 bool Object::SinkReference() 00305 { 00306 if (!IsHeapAllocated()) 00307 { 00308 LOG_WARN(logger) << "Trying to sink an object that was not heap allocated." 00309 << "\nObject allocated at: " << GetAllocationLoation(); 00310 return false; 00311 } 00312 00313 if (!OwnsTheReference()) 00314 { 00315 SetOwnedReference(true); 00316 // The ref count remains at 1. Exit the method. 00317 return true; 00318 } 00319 00320 return false; 00321 } 00322 00323 bool Object::Dispose() 00324 { 00325 // The intent of the Dispose call is to destroy objects with a float 00326 // reference (reference count is equal to 1 and the '_owns_the_reference' 00327 // flag is set to false). In Nux, only widgets object can have a floating 00328 // reference. And widgets are only visible if added to the widget tree. 00329 // When an object with a floating reference is added to the widget tree, 00330 // it becomes "owned'. It looses it floating reference status but it still 00331 // has a reference count number of 1. In practice, since widgets must be 00332 // added to the widget tree, there should never be a need to call Dispose 00333 // (except in a few cases). 00334 00335 // Dispose() was designed to only destroy objects with floating 00336 // references, while UnReference() destroys objects that are "owned". 00337 // That is now relaxed. Dispose() calls UnReference(). 00338 return UnReference(); 00339 } 00340 00341 void Object::Destroy() 00342 { 00343 static int delete_depth = 0; 00344 ++delete_depth; 00345 const char* obj_type = this->Type().name; 00346 LOG_TRACE(logger) << "Depth: " << delete_depth << ", about to delete " 00347 << obj_type << " allocated at " << GetAllocationLoation(); 00348 // Weak smart pointers will clear their pointers when they get this signal. 00349 object_destroyed.emit(this); 00350 delete this; 00351 LOG_TRACE(logger) << "Depth: " << delete_depth << ", delete successful for " << obj_type; 00352 --delete_depth; 00353 } 00354 00355 int Object::GetReferenceCount() const 00356 { 00357 return reference_count_->GetValue(); 00358 } 00359 00360 std::string Object::GetAllocationLoation() const 00361 { 00362 std::ostringstream sout; 00363 sout << allocation_file_name_ << ":" << allocation_line_number_; 00364 return sout.str(); 00365 } 00366 00367 } 00368