nux-1.14.0
Object.cpp
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 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends