1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ 12 #define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ 13 14 #include <assert.h> 15 16 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" 17 #ifdef _WIN32 18 #include "webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h" 19 #endif 20 21 namespace webrtc { 22 23 enum CountOperation { 24 kRelease, 25 kAddRef, 26 kAddRefNoCreate 27 }; 28 enum CreateOperation { 29 kInstanceExists, 30 kCreate, 31 kDestroy 32 }; 33 34 template <class T> 35 // Construct On First Use idiom. Avoids 36 // "static initialization order fiasco". GetStaticInstance(CountOperation count_operation)37static T* GetStaticInstance(CountOperation count_operation) { 38 // TODO (hellner): use atomic wrapper instead. 39 static volatile long instance_count = 0; 40 static T* volatile instance = NULL; 41 CreateOperation state = kInstanceExists; 42 #ifndef _WIN32 43 // This memory is staticly allocated once. The application does not try to 44 // free this memory. This approach is taken to avoid issues with 45 // destruction order for statically allocated memory. The memory will be 46 // reclaimed by the OS and memory leak tools will not recognize memory 47 // reachable from statics leaked so no noise is added by doing this. 48 static CriticalSectionWrapper* crit_sect( 49 CriticalSectionWrapper::CreateCriticalSection()); 50 CriticalSectionScoped lock(crit_sect); 51 52 if (count_operation == 53 kAddRefNoCreate && instance_count == 0) { 54 return NULL; 55 } 56 if (count_operation == 57 kAddRef || 58 count_operation == kAddRefNoCreate) { 59 instance_count++; 60 if (instance_count == 1) { 61 state = kCreate; 62 } 63 } else { 64 instance_count--; 65 if (instance_count == 0) { 66 state = kDestroy; 67 } 68 } 69 if (state == kCreate) { 70 instance = T::CreateInstance(); 71 } else if (state == kDestroy) { 72 T* old_instance = instance; 73 instance = NULL; 74 // The state will not change past this point. Release the critical 75 // section while deleting the object in case it would be blocking on 76 // access back to this object. (This is the case for the tracing class 77 // since the thread owned by the tracing class also traces). 78 // TODO(hellner): this is a bit out of place but here goes, de-couple 79 // thread implementation with trace implementation. 80 crit_sect->Leave(); 81 if (old_instance) { 82 delete old_instance; 83 } 84 // Re-acquire the lock since the scoped critical section will release 85 // it. 86 crit_sect->Enter(); 87 return NULL; 88 } 89 #else // _WIN32 90 if (count_operation == 91 kAddRefNoCreate && instance_count == 0) { 92 return NULL; 93 } 94 if (count_operation == kAddRefNoCreate) { 95 if (1 == InterlockedIncrement(&instance_count)) { 96 // The instance has been destroyed by some other thread. Rollback. 97 InterlockedDecrement(&instance_count); 98 assert(false); 99 return NULL; 100 } 101 // Sanity to catch corrupt state. 102 if (instance == NULL) { 103 assert(false); 104 InterlockedDecrement(&instance_count); 105 return NULL; 106 } 107 } else if (count_operation == kAddRef) { 108 if (instance_count == 0) { 109 state = kCreate; 110 } else { 111 if (1 == InterlockedIncrement(&instance_count)) { 112 // InterlockedDecrement because reference count should not be 113 // updated just yet (that's done when the instance is created). 114 InterlockedDecrement(&instance_count); 115 state = kCreate; 116 } 117 } 118 } else { 119 int new_value = InterlockedDecrement(&instance_count); 120 if (new_value == 0) { 121 state = kDestroy; 122 } 123 } 124 125 if (state == kCreate) { 126 // Create instance and let whichever thread finishes first assign its 127 // local copy to the global instance. All other threads reclaim their 128 // local copy. 129 T* new_instance = T::CreateInstance(); 130 if (1 == InterlockedIncrement(&instance_count)) { 131 InterlockedExchangePointer(reinterpret_cast<void * volatile*>(&instance), 132 new_instance); 133 } else { 134 InterlockedDecrement(&instance_count); 135 if (new_instance) { 136 delete static_cast<T*>(new_instance); 137 } 138 } 139 } else if (state == kDestroy) { 140 T* old_value = static_cast<T*>(InterlockedExchangePointer( 141 reinterpret_cast<void * volatile*>(&instance), NULL)); 142 if (old_value) { 143 delete static_cast<T*>(old_value); 144 } 145 return NULL; 146 } 147 #endif // #ifndef _WIN32 148 return instance; 149 } 150 151 } // namspace webrtc 152 153 #endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ 154