Back to index

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