/[svn]/linuxsampler/trunk/src/common/Ref.h
ViewVC logotype

Annotation of /linuxsampler/trunk/src/common/Ref.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3769 - (hide annotations) (download) (as text)
Fri May 15 20:16:39 2020 UTC (4 years ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 17419 byte(s)
Ref<> class: Use C++ atomics instead of C atomics (as some compilers seem
to be unable to mix C atomics with C++ code, and the generated result
should be binary identical anyway).

1 schoenebeck 2581 /*
2 schoenebeck 3767 * Copyright (c) 2014 - 2020 Christian Schoenebeck
3 schoenebeck 2581 *
4     * http://www.linuxsampler.org
5     *
6     * This file is part of LinuxSampler and released under the same terms.
7     * See README file for details.
8     */
9    
10     #ifndef LS_REF_H
11     #define LS_REF_H
12    
13 schoenebeck 3768 #include "global.h"
14    
15 schoenebeck 2581 #include <set>
16     #include <stdio.h>
17 schoenebeck 3767 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
18     # include <type_traits> // for std::enable_if and std::is_same
19     #endif
20 schoenebeck 2581
21     // You may enable this while developing or at least when you encounter any kind
22     // of crashes or other misbehaviors in conjunction with Ref class guarded code.
23     // Enabling the following macro will add a bunch of sanity checks for easing
24     // debugging of such issues, however it comes with the cost that everything will
25     // be much slower.
26 schoenebeck 2619 #define LS_REF_ASSERT_MODE 0
27 schoenebeck 2581
28 schoenebeck 3768 // Enable this for VERY verbose debug messages for debugging deep issues with
29 schoenebeck 2581 // Ref class.
30     #define LS_REF_VERBOSE_DEBUG_MSG 0
31    
32     #if LS_REF_ASSERT_MODE
33 schoenebeck 3768 # warning LS_REF_ASSERT_MODE is enabled which will decrease runtime efficiency!
34 schoenebeck 2581 # include <assert.h>
35     #endif
36    
37 schoenebeck 3768 // Whether the Ref<> class shall be thread safe. Performance penalty should not
38     // be measurable in practice due to non blocking, lock-free atomic add, sub and
39     // CAS instructions being used, so this is recommended and enabled by default.
40     #ifndef LS_REF_ATOMIC
41     # define LS_REF_ATOMIC 1
42     #endif
43    
44 schoenebeck 3769 //NOTE: We are using our own atomic implementation for atomic increment and
45     // decrement instead of std::atomic of the C++ STL, because our atomic
46     // implementation is lock-free and wait-free. The C++ standard just recommends,
47     // but does not guarantee lock-free implementation (state of the C++20 standard
48     // as of 2020-04-27):
49 schoenebeck 3768 // https://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free
50 schoenebeck 3769 // For CAS though we simply use the STL version, since it is only used on
51     // resource deallocation, which is not real-time safe anyway.
52 schoenebeck 3768 #if LS_REF_ATOMIC
53     # include "atomic.h"
54 schoenebeck 3769 # include <atomic>
55 schoenebeck 3768 #else
56     # warning Ref<> class will not be thread safe (feature explicitly disabled)!
57     #endif
58    
59     #if LS_REF_ASSERT_MODE && LS_REF_ATOMIC
60     # include "Mutex.h"
61     #endif
62    
63 schoenebeck 2581 namespace LinuxSampler {
64    
65     template<typename T, typename T_BASE> class Ref;
66    
67 schoenebeck 3768 #if LS_REF_ASSERT_MODE
68 schoenebeck 2581 extern std::set<void*> _allRefPtrs;
69 schoenebeck 3768 #endif
70     #if LS_REF_ASSERT_MODE && LS_REF_ATOMIC
71     extern Mutex _allRefPtrsMutex;
72     #endif
73 schoenebeck 2581
74     /**
75     * Exists just for implementation detail purpose, you cannot use it
76     * directly. Use its derived template class Ref instead.
77     *
78     * @see Ref
79     */
80     template<typename T_BASE>
81     class RefBase {
82     public:
83     template<typename T_BASE1>
84     class _RefCounter {
85     public:
86 schoenebeck 3768 _RefCounter(T_BASE1* p, int refs, bool released) :
87     #if LS_REF_ATOMIC
88     references(ATOMIC_INIT(refs)),
89     #else
90     references(refs),
91     #endif
92     ptr(p)
93 schoenebeck 2581 {
94 schoenebeck 3768 #if LS_REF_ATOMIC
95 schoenebeck 3769 std::atomic_store(&zombi, released);
96 schoenebeck 3768 #endif
97 schoenebeck 2581 #if LS_REF_VERBOSE_DEBUG_MSG
98 schoenebeck 3768 printf("Ref %p: new counter (refs=%d)\n", ptr,
99     #if LS_REF_ATOMIC
100     atomic_read(&references)
101     #else
102     references
103     #endif
104     );
105 schoenebeck 2581 #endif
106     #if LS_REF_ASSERT_MODE
107     assert(p);
108     assert(refs > 0);
109 schoenebeck 3768 assert(!released);
110     {
111     #if LS_REF_ATOMIC
112     LockGuard lock(_allRefPtrsMutex);
113     #endif
114     assert(!_allRefPtrs.count(p));
115     _allRefPtrs.insert(p);
116     }
117 schoenebeck 2581 #endif
118     }
119    
120     virtual ~_RefCounter() {
121     #if LS_REF_VERBOSE_DEBUG_MSG
122 schoenebeck 3768 printf("Ref %p: counter destructor (refs=%d)\n", ptr,
123     #if LS_REF_ATOMIC
124     atomic_read(&references)
125     #else
126     references
127     #endif
128     );
129 schoenebeck 2581 #endif
130     fflush(stdout);
131     }
132    
133     void retain() {
134 schoenebeck 3768 #if LS_REF_ATOMIC
135     atomic_inc(&references);
136     #else
137 schoenebeck 2581 references++;
138 schoenebeck 3768 #endif
139 schoenebeck 2581 #if LS_REF_VERBOSE_DEBUG_MSG
140 schoenebeck 3768 printf("Ref %p: retain (refs=%d)\n", ptr,
141     #if LS_REF_ATOMIC
142     atomic_read(&references)
143     #else
144     references
145     #endif
146     );
147 schoenebeck 2581 #endif
148     }
149    
150     void release() {
151 schoenebeck 3768 #if LS_REF_ATOMIC
152     bool zero = atomic_dec_and_test(&references);
153     # if LS_REF_VERBOSE_DEBUG_MSG
154     printf("Ref %p: release (zero=%d)\n", ptr, zero);
155     # endif
156     if (!zero) return;
157     bool expect = false;
158 schoenebeck 3769 bool release = std::atomic_compare_exchange_strong(&zombi, &expect, true);
159 schoenebeck 3768 if (release) deletePtr();
160     #else
161 schoenebeck 2581 if (!references) return;
162     references--;
163 schoenebeck 3768 # if LS_REF_VERBOSE_DEBUG_MSG
164     printf("Ref %p: release (refs=%d)\n", ptr, references);
165     # endif
166 schoenebeck 2581 if (!references) deletePtr();
167 schoenebeck 3768 #endif // LS_REF_ATOMIC
168 schoenebeck 2581 }
169     //protected:
170     void deletePtr() {
171     #if LS_REF_VERBOSE_DEBUG_MSG
172 schoenebeck 3768 printf("RefCounter %p: deletePtr() (refs=%d)\n", ptr,
173     #if LS_REF_ATOMIC
174     atomic_read(&references)
175     #else
176     references
177     #endif
178     );
179 schoenebeck 2581 #endif
180     #if LS_REF_ASSERT_MODE
181 schoenebeck 3768 # if LS_REF_ATOMIC
182     assert(!atomic_read(&references));
183     # else
184 schoenebeck 2581 assert(!references);
185 schoenebeck 3768 # endif
186     {
187     #if LS_REF_ATOMIC
188     LockGuard lock(_allRefPtrsMutex);
189     #endif
190     _allRefPtrs.erase(ptr);
191     }
192 schoenebeck 2581 #endif
193     delete ptr;
194     delete this;
195     }
196    
197 schoenebeck 3768 #if LS_REF_ATOMIC
198     atomic_t references;
199 schoenebeck 3769 std::atomic<bool> zombi; ///< @c false if not released yet, @c true once @c ptr was released
200 schoenebeck 3768 #else
201 schoenebeck 2581 int references;
202 schoenebeck 3768 #endif
203 schoenebeck 2581 T_BASE1* ptr;
204     //friend class ... todo
205     };
206     typedef _RefCounter<T_BASE> RefCounter;
207    
208     virtual ~RefBase() {
209     if (refCounter) refCounter->release();
210     refCounter = NULL;
211     }
212    
213     //protected:
214     RefCounter* refCounter;
215     //friend class Ref<T_BASE, T_BASE>;
216    
217     protected:
218     RefBase() : refCounter(NULL) {
219     #if LS_REF_VERBOSE_DEBUG_MSG
220     printf("(RefBase empty ctor)\n");
221     #endif
222     }
223     /*
224     RefBase(RefCounter* rc) {
225     refCounter = rc;
226     }
227    
228     RefBase(const RefBase& r) {
229     refCounter = r.refCounter;
230     if (refCounter) refCounter->retain();
231     }
232     */
233     };
234    
235     /**
236 schoenebeck 3768 * Replicates a std::shared_ptr template class. Originally this class was
237     * introduced to avoid a build requirement of having a C++11 compliant
238     * compiler (std::shared_ptr was not part of the C++03 standard). This class
239     * had been preserved though due to some advantages over the STL
240     * implementation, most notably: unlike std::shared_ptr from the STL, this
241     * Ref<> class provides thread safety by using a (guaranteed) lock-free and
242     * wait-free implementation, which is relevant for real-time applications.
243 schoenebeck 2581 *
244     * In contrast to the STL implementation though this implementation here
245     * also supports copying references of derived, different types (in a type
246     * safe manner). You can achieve that by providing a second template
247     * argument (which is optional), for declaring a common subtype. For example
248     * the following code would not compile:
249     * @code
250     * void example(UILabel* pLabel) {
251     * Ref<UILabel> lbl = pLabel;
252     * Ref<UIWidget> w = lbl; // compile error, incompatible Ref types
253     * w->resize(16,300);
254     * }
255     * @endcode
256     * Whereas the following would work:
257     * @code
258     * void example(UILabel* pLabel) {
259     * Ref<UILabel,UIWidget> lbl = pLabel;
260     * Ref<UIWidget> w = lbl; // works (assuming that UILabel is a subclass of UIWidget)
261     * w->resize(16,300);
262     * }
263     * @endcode
264     * Like the STL's std::shared_ptr, this class also emulates raw pointer
265     * access and operators. With one addition: if used in the derived common
266     * subtype manner as shown above, access to the actual data and boolean
267     * operator will also check whether the underlying pointer (of the common
268     * subclass) can actually be casted safely to the objects main type (first
269     * template argument of this class). For example:
270     * @code
271     * void example(UILabel* pLabel) { // assuming pLabel is not NULL ...
272     * Ref<UILabel,UIWidget> lbl = pLabel;
273     * Ref<UIDialog,UIWidget> dlg = lbl;
274     * bool b1 = lbl; // will be true (assuming pLabel was not NULL)
275     * bool b2 = dlg; // will be false (assuming that UIDialog is not derived from UILabel)
276     * lbl->setText("foo"); // works
277     * dlg->showModal(); // would crash with -> operator providing a NULL pointer
278     * }
279     * @endcode
280     * Like with std::shared_ptr you must be @b very cautious that you
281     * initialize only one Ref class object directly with the same raw pointer.
282     * If you forget this fundamental rule somewhere, your code will crash!
283     * @code
284     * UIWidget* ptr = new UIWidget();
285     * Ref<UIWidget> w1 = ptr;
286     * Ref<UIWidget> w2 = w1; // this is OK, copy from a Ref object
287     * Ref<UIWidget> w3 = ptr; // illegal! 2nd direct init from same raw pointer. This will crash!
288     * @endcode
289     * It would be possible to write an implementation of the Ref class that
290     * could handle the case above as well without crashing, however it would be
291     * too slow for practice. Because it would require a global lookup table
292     * maintaining all memory pointers which are currently already guarded by
293     * this class. Plus it would need an expensive synchronization to prevent
294     * concurrent access on that global lookup table.
295     */
296     template<typename T, typename T_BASE = T>
297     class Ref : public RefBase<T_BASE> {
298     public:
299     typedef RefBase<T_BASE> RefBaseT;
300     typedef typename RefBase<T_BASE>::RefCounter RefCounter;
301 schoenebeck 3769
302 schoenebeck 2581 Ref() : RefBaseT() {
303     #if LS_REF_VERBOSE_DEBUG_MSG
304 schoenebeck 3768 printf("Ref empty ctor Ref:%p\n", this);
305 schoenebeck 2581 #endif
306     }
307    
308 schoenebeck 3767 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
309     template<std::enable_if<!std::is_same<T_BASE, T>::value>* = NULL> // prevent compiler error if T == T_Base (due to method signature duplicate)
310     #endif
311 schoenebeck 2581 Ref(const T_BASE* p) : RefBaseT() {
312     #if LS_REF_VERBOSE_DEBUG_MSG
313 schoenebeck 3768 printf("Ref base ptr ctor Ref:%p <- p:%p\n", this, p);
314 schoenebeck 2581 #endif
315 schoenebeck 3768 RefBaseT::refCounter = p ? new RefCounter((T_BASE*)p, 1, false) : NULL;
316 schoenebeck 2581 }
317    
318     Ref(const T* p) : RefBaseT() {
319     #if LS_REF_VERBOSE_DEBUG_MSG
320 schoenebeck 3768 printf("Ref main ptr ctor Ref:%p <- p:%p\n", this, p);
321 schoenebeck 2581 #endif
322 schoenebeck 3768 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1, false) : NULL;
323 schoenebeck 2581 }
324    
325     Ref(const RefBaseT& r) : RefBaseT() {
326     #if LS_REF_VERBOSE_DEBUG_MSG
327 schoenebeck 3768 printf("Ref base ref ctor Ref:%p <- Ref:%p\n", this, &r);
328 schoenebeck 2581 #endif
329     RefBaseT::refCounter = r.refCounter;
330     if (RefBaseT::refCounter)
331     RefBaseT::refCounter->retain();
332     }
333    
334     Ref(const Ref& r) : RefBaseT() {
335     #if LS_REF_VERBOSE_DEBUG_MSG
336 schoenebeck 3768 printf("Ref main ref ctor Ref:%p <- Ref:%p\n", this, &r);
337 schoenebeck 2581 #endif
338     RefBaseT::refCounter = r.refCounter;
339     if (RefBaseT::refCounter)
340     RefBaseT::refCounter->retain();
341     }
342    
343     inline T* operator->() {
344     return dynamic_cast<T*>( RefBaseT::refCounter->ptr );
345     }
346    
347     inline const T* operator->() const {
348     return dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
349     }
350    
351     inline T& operator*() {
352     return *dynamic_cast<T*>( RefBaseT::refCounter->ptr );
353     }
354    
355     inline const T& operator*() const {
356     return *dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
357     }
358    
359     inline bool operator==(const RefBaseT& other) const {
360     return RefBaseT::refCounter == other.refCounter;
361     }
362    
363     inline bool operator!=(const RefBaseT& other) const {
364     return RefBaseT::refCounter != other.refCounter;
365     }
366    
367     inline operator bool() const {
368     return RefBaseT::refCounter && RefBaseT::refCounter->ptr &&
369     dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
370     }
371    
372     inline bool operator!() const {
373     return !( RefBaseT::refCounter && RefBaseT::refCounter->ptr &&
374     dynamic_cast<const T*>( RefBaseT::refCounter->ptr ) );
375     }
376    
377     /*
378     inline operator RefBaseT&() {
379     return *this;
380     }
381 schoenebeck 3769
382 schoenebeck 2581 inline operator const RefBaseT&() const {
383     return *this;
384     }
385     */
386     inline bool isEquivalent(const RefBaseT& other) const {
387     if (static_cast<const RefBaseT*>(this) == &other)
388     return true;
389     return (RefBaseT::refCounter == other.refCounter);
390     }
391    
392     inline bool isEquivalent(const T_BASE* const other) const {
393     if (!other) return !RefBaseT::refCounter;
394     if (!RefBaseT::refCounter) return false;
395     return other == RefBaseT::refCounter->ptr;
396     }
397    
398     Ref<T,T_BASE>& operator=(const RefBaseT& other) {
399     #if LS_REF_VERBOSE_DEBUG_MSG
400 schoenebeck 3768 printf("Ref base ref assignment Ref:%p <- Ref:%p\n", this, &other);
401 schoenebeck 2581 #endif
402     if (isEquivalent(other)) {
403     #if LS_REF_VERBOSE_DEBUG_MSG
404 schoenebeck 3768 printf("Ref %p WRN: equivalent ref assignment ignored.\n", this);
405 schoenebeck 2581 #endif
406     return *this;
407     }
408     if (RefBaseT::refCounter) {
409     RefBaseT::refCounter->release();
410     RefBaseT::refCounter = NULL;
411     }
412     RefBaseT::refCounter = other.refCounter;
413     if (RefBaseT::refCounter)
414     RefBaseT::refCounter->retain();
415     return *this;
416     }
417    
418     Ref<T,T_BASE>& operator=(const Ref<T,T_BASE>& other) {
419     #if LS_REF_VERBOSE_DEBUG_MSG
420 schoenebeck 3768 printf("Ref main ref assignment Ref:%p <- Ref:%p\n", this, &other);
421 schoenebeck 2581 #endif
422     if (isEquivalent(other)) {
423     #if LS_REF_VERBOSE_DEBUG_MSG
424 schoenebeck 3768 printf("Ref %p WRN: equivalent ref assignment ignored.\n", this);
425 schoenebeck 2581 #endif
426     return *this;
427     }
428     if (RefBaseT::refCounter) {
429     RefBaseT::refCounter->release();
430     RefBaseT::refCounter = NULL;
431     }
432     RefBaseT::refCounter = other.refCounter;
433     if (RefBaseT::refCounter)
434     RefBaseT::refCounter->retain();
435     return *this;
436     }
437    
438     Ref<T,T_BASE>& operator=(const T* p) {
439     #if LS_REF_VERBOSE_DEBUG_MSG
440 schoenebeck 3768 printf("Ref main ptr assignment Ref:%p <- p:%p\n", this, p);
441 schoenebeck 2581 #endif
442     if (isEquivalent(p)) {
443     #if LS_REF_VERBOSE_DEBUG_MSG
444 schoenebeck 3768 printf("Ref %p WRN: equivalent ptr assignment ignored.\n", this);
445 schoenebeck 2581 #endif
446     return *this;
447     }
448     if (RefBaseT::refCounter) {
449     RefBaseT::refCounter->release();
450     RefBaseT::refCounter = NULL;
451     }
452 schoenebeck 3768 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1, false) : NULL;
453 schoenebeck 2581 #if LS_REF_VERBOSE_DEBUG_MSG
454     printf("Ref main ptr assignment done\n");
455     #endif
456     return *this;
457     }
458    
459 schoenebeck 3767 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
460     template<std::enable_if<!std::is_same<T_BASE, T>::value>* = NULL> // prevent compiler error if T == T_Base (due to method signature duplicate)
461     #endif
462 schoenebeck 2581 Ref<T,T_BASE>& operator=(const T_BASE* p) {
463     #if LS_REF_VERBOSE_DEBUG_MSG
464 schoenebeck 3768 printf("Ref base ptr assignment Ref:%p <- p:%p\n", this, p);
465 schoenebeck 2581 #endif
466     if (isEquivalent(p)) {
467     #if LS_REF_VERBOSE_DEBUG_MSG
468 schoenebeck 3768 printf("Ref %p WRN: equivalent ptr assignment ignored.\n", this);
469 schoenebeck 2581 #endif
470     return *this;
471     }
472     if (RefBaseT::refCounter) {
473     RefBaseT::refCounter->release();
474     RefBaseT::refCounter = NULL;
475     }
476 schoenebeck 3768 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1, false) : NULL;
477 schoenebeck 2581 return *this;
478     }
479     };
480    
481     } // namespace LinuxSampler
482    
483     #endif // LS_REF_H

  ViewVC Help
Powered by ViewVC