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