/[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 3768 - (hide annotations) (download) (as text)
Fri May 15 19:52:31 2020 UTC (3 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 17399 byte(s)
Ref<> smart pointer class is now thread-safe.

* Require C11 compliant compiler.

* Ref<> class: Implemented lock-free & wait-free thread-safety.

* Ref<> class: Use portable format specifier %p for pointers.

* Bumped version (2.1.1.svn56).

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

  ViewVC Help
Powered by ViewVC