1 /* 2 * Copyright (C) 2005-2018 Team Kodi 3 * This file is part of Kodi - https://kodi.tv 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * See LICENSES/README.md for more information. 7 */ 8 9 #pragma once 10 11 /** 12 * Defining LOG_LIFECYCLE_EVENTS will log all instantiations, deletions 13 * and also reference countings (increments and decrements) that take 14 * place on any Addon* class. 15 * 16 * Comment out (or uncomment out) to change the setting. 17 */ 18 //#define LOG_LIFECYCLE_EVENTS 19 20 /** 21 * Defining XBMC_ADDON_DEBUG_MEMORY will make the Acquire and Release 22 * methods virtual allow the developer to overload them in a sub-class 23 * and set breakpoints to aid in debugging. It will also cause the 24 * reference counting mechanism to never actually delete any AddonClass 25 * instance allowing for the tracking of more references to (supposedly) 26 * deallocated classes. 27 * 28 * Comment out (or uncomment out) to change the setting. 29 */ 30 //#define XBMC_ADDON_DEBUG_MEMORY 31 32 #include "AddonString.h" 33 #include "threads/SingleLock.h" 34 #ifdef XBMC_ADDON_DEBUG_MEMORY 35 #include "utils/log.h" 36 #endif 37 #include "AddonUtils.h" 38 39 #include <atomic> 40 #include <typeindex> 41 42 namespace XBMCAddon 43 { 44 class LanguageHook; 45 46 /** 47 * This class is the superclass for all reference counted classes in the api. 48 * It provides a means for the bindings to handle all api objects generically. 49 * 50 * It also provides some means for debugging "lifecycle" events (see the above 51 * description of LOG_LIFECYCLE_EVENTS). 52 * 53 * If a scripting language bindings require specific handling there is a 54 * hook to add in these language specifics that can be set here. 55 */ 56 class AddonClass : public CCriticalSection 57 { 58 private: 59 mutable std::atomic<long> refs; 60 bool m_isDeallocating = false; 61 62 // no copying 63 inline AddonClass(const AddonClass&) = delete; 64 65 #ifdef XBMC_ADDON_DEBUG_MEMORY 66 bool isDeleted; 67 #endif 68 69 protected: 70 LanguageHook* languageHook; 71 72 /** 73 * This method is meant to be called from the destructor of the 74 * lowest level class. 75 * 76 * It's virtual because it's a convenient place to receive messages that 77 * we're about to go be deleted but prior to any real tear-down. 78 * 79 * Any overloading classes need to remember to pass the call up the chain. 80 */ deallocating()81 virtual void deallocating() 82 { 83 CSingleLock lock(*this); 84 m_isDeallocating = true; 85 } 86 87 /** 88 * This is meant to be called during static initialization and so isn't 89 * synchronized. 90 */ 91 static short getNextClassIndex(); 92 93 public: 94 AddonClass(); 95 virtual ~AddonClass(); 96 GetClassname()97 inline const char* GetClassname() const { return typeid(*this).name(); } GetLanguageHook()98 inline LanguageHook* GetLanguageHook() { return languageHook; } 99 100 /** 101 * This method should be called while holding a Synchronize 102 * on the object. It will prevent the deallocation during 103 * the time it's held. 104 */ isDeallocating()105 bool isDeallocating() { XBMC_TRACE; return m_isDeallocating; } 106 107 static short getNumAddonClasses(); 108 109 #ifdef XBMC_ADDON_DEBUG_MEMORY 110 virtual 111 #else 112 inline 113 #endif Release()114 void Release() const 115 #ifndef XBMC_ADDON_DEBUG_MEMORY 116 { 117 long ct = --refs; 118 #ifdef LOG_LIFECYCLE_EVENTS 119 CLog::Log(LOGDEBUG,"NEWADDON REFCNT decrementing to %ld on %s 0x%lx", ct,GetClassname(), (long)(((void*)this))); 120 #endif 121 if(ct == 0) 122 delete this; 123 } 124 #else 125 ; 126 #endif 127 128 129 #ifdef XBMC_ADDON_DEBUG_MEMORY 130 virtual 131 #else 132 inline 133 #endif Acquire()134 void Acquire() const 135 #ifndef XBMC_ADDON_DEBUG_MEMORY 136 { 137 #ifdef LOG_LIFECYCLE_EVENTS 138 CLog::Log(LOGDEBUG,"NEWADDON REFCNT incrementing to %ld on %s 0x%lx", 139 ++refs, GetClassname(), (long)(((void*)this))); 140 #else 141 ++refs; 142 #endif 143 } 144 #else 145 ; 146 #endif 147 148 #define refcheck 149 /** 150 * This class is a smart pointer for a Referenced class. 151 */ 152 template <class T> class Ref 153 { 154 T * ac; 155 public: Ref()156 inline Ref() : ac(NULL) {} Ref(const T * _ac)157 inline Ref(const T* _ac) : ac(const_cast<T*>(_ac)) { if (ac) ac->Acquire(); refcheck; } 158 159 // copy semantics Ref(Ref<T> const & oref)160 inline Ref(Ref<T> const & oref) : ac(const_cast<T*>(oref.get())) { if (ac) ac->Acquire(); refcheck; } Ref(Ref<O> const & oref)161 template<class O> inline Ref(Ref<O> const & oref) : ac(static_cast<T*>(oref.get())) { if (ac) ac->Acquire(); refcheck; } 162 163 /** 164 * operator= should work with either another smart pointer or a pointer since it will 165 * be able to convert a pointer to a smart pointer using one of the above constructors. 166 * 167 * Note: There is a trick here. The temporary variable is necessary because otherwise the 168 * following code will fail: 169 * 170 * Ref<T> ptr = new T; 171 * ptr = ptr; 172 * 173 * What happens without the tmp is the dereference is called first so the object ends up 174 * deleted and then the reference happens on a deleted object. The order is reversed 175 * in the following. 176 * 177 * Note: Operator= is ambiguous if you define both an operator=(Ref<T>&) and an operator=(T*). I'm 178 * opting for the route the boost took here figuring it has more history behind it. 179 */ 180 inline Ref<T>& operator=(Ref<T> const & oref) 181 { T* tmp = ac; ac = const_cast<T*>(oref.get()); if (ac) ac->Acquire(); if (tmp) tmp->Release(); refcheck; return *this; } 182 183 inline T* operator->() const { refcheck; return ac; } 184 185 /** 186 * This operator doubles as the value in a boolean expression. 187 */ 188 inline operator T*() const { refcheck; return ac; } get()189 inline T* get() const { refcheck; return ac; } getRef()190 inline T& getRef() const { refcheck; return *ac; } 191 ~Ref()192 inline ~Ref() { refcheck; if (ac) ac->Release(); } isNull()193 inline bool isNull() const { refcheck; return ac == NULL; } isNotNull()194 inline bool isNotNull() const { refcheck; return ac != NULL; } isSet()195 inline bool isSet() const { refcheck; return ac != NULL; } 196 inline bool operator!() const { refcheck; return ac == NULL; } 197 inline bool operator==(const AddonClass::Ref<T>& oref) const { refcheck; return ac == oref.ac; } 198 inline bool operator<(const AddonClass::Ref<T>& oref) const { refcheck; return ac < oref.ac; } // std::set semantics 199 200 // This is there only for boost compatibility reset(Ref<O> const & oref)201 template<class O> inline void reset(Ref<O> const & oref) { refcheck; (*this) = static_cast<T*>(oref.get()); refcheck; } reset(O * oref)202 template<class O> inline void reset(O * oref) { refcheck; (*this) = static_cast<T*>(oref); refcheck; } reset()203 inline void reset() { refcheck; if (ac) ac->Release(); ac = NULL; } 204 }; 205 206 }; 207 } 208