/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATIC_INSTANCE_H_ #define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATIC_INSTANCE_H_ #include #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #ifdef _WIN32 #include "webrtc/system_wrappers/interface/fix_interlocked_exchange_pointer_win.h" #endif namespace webrtc { enum CountOperation { kRelease, kAddRef, kAddRefNoCreate }; enum CreateOperation { kInstanceExists, kCreate, kDestroy }; template // Construct On First Use idiom. Avoids // "static initialization order fiasco". static T* GetStaticInstance(CountOperation count_operation) { // TODO (hellner): use atomic wrapper instead. static volatile long instance_count = 0; static T* volatile instance = NULL; CreateOperation state = kInstanceExists; #ifndef _WIN32 // This memory is staticly allocated once. The application does not try to // free this memory. This approach is taken to avoid issues with // destruction order for statically allocated memory. The memory will be // reclaimed by the OS and memory leak tools will not recognize memory // reachable from statics leaked so no noise is added by doing this. static CriticalSectionWrapper* crit_sect( CriticalSectionWrapper::CreateCriticalSection()); CriticalSectionScoped lock(crit_sect); if (count_operation == kAddRefNoCreate && instance_count == 0) { return NULL; } if (count_operation == kAddRef || count_operation == kAddRefNoCreate) { instance_count++; if (instance_count == 1) { state = kCreate; } } else { instance_count--; if (instance_count == 0) { state = kDestroy; } } if (state == kCreate) { instance = T::CreateInstance(); } else if (state == kDestroy) { T* old_instance = instance; instance = NULL; // The state will not change past this point. Release the critical // section while deleting the object in case it would be blocking on // access back to this object. (This is the case for the tracing class // since the thread owned by the tracing class also traces). // TODO(hellner): this is a bit out of place but here goes, de-couple // thread implementation with trace implementation. crit_sect->Leave(); if (old_instance) { delete old_instance; } // Re-acquire the lock since the scoped critical section will release // it. crit_sect->Enter(); return NULL; } #else // _WIN32 if (count_operation == kAddRefNoCreate && instance_count == 0) { return NULL; } if (count_operation == kAddRefNoCreate) { if (1 == InterlockedIncrement(&instance_count)) { // The instance has been destroyed by some other thread. Rollback. InterlockedDecrement(&instance_count); assert(false); return NULL; } // Sanity to catch corrupt state. if (instance == NULL) { assert(false); InterlockedDecrement(&instance_count); return NULL; } } else if (count_operation == kAddRef) { if (instance_count == 0) { state = kCreate; } else { if (1 == InterlockedIncrement(&instance_count)) { // InterlockedDecrement because reference count should not be // updated just yet (that's done when the instance is created). InterlockedDecrement(&instance_count); state = kCreate; } } } else { int new_value = InterlockedDecrement(&instance_count); if (new_value == 0) { state = kDestroy; } } if (state == kCreate) { // Create instance and let whichever thread finishes first assign its // local copy to the global instance. All other threads reclaim their // local copy. T* new_instance = T::CreateInstance(); if (1 == InterlockedIncrement(&instance_count)) { InterlockedExchangePointer(reinterpret_cast(&instance), new_instance); } else { InterlockedDecrement(&instance_count); if (new_instance) { delete static_cast(new_instance); } } } else if (state == kDestroy) { T* old_value = static_cast(InterlockedExchangePointer( reinterpret_cast(&instance), NULL)); if (old_value) { delete static_cast(old_value); } return NULL; } #endif // #ifndef _WIN32 return instance; } } // namspace webrtc #endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATIC_INSTANCE_H_