diff options
Diffstat (limited to 'src/system_wrappers/interface/static_instance.h')
-rw-r--r-- | src/system_wrappers/interface/static_instance.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/system_wrappers/interface/static_instance.h b/src/system_wrappers/interface/static_instance.h new file mode 100644 index 0000000000..8fe91cc3e4 --- /dev/null +++ b/src/system_wrappers/interface/static_instance.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2011 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_STATICINSTANCETEMPLATE_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATICINSTANCETEMPLATE_H_ + +#include <assert.h> + +#include "critical_section_wrapper.h" +#ifdef _WIN32 +#include "fix_interlocked_exchange_pointer_win.h" +#endif + +namespace webrtc { + +enum CountOperation { + kRelease, + kAddRef, + kAddRefNoCreate +}; +enum CreateOperation { + kInstanceExists, + kCreate, + kDestroy +}; + +template <class T> +// 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 newValue = InterlockedDecrement(&instance_count); + if (newValue == 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)) { + T* old_value = static_cast<T*> (InterlockedExchangePointer( + reinterpret_cast<void* volatile*>(&instance), new_instance)); + assert(old_value == NULL); + assert(instance); + } else { + InterlockedDecrement(&instance_count); + if (new_instance) { + delete static_cast<T*>(new_instance); + } + } + } else if (state == kDestroy) { + T* old_value = static_cast<T*> (InterlockedExchangePointer( + reinterpret_cast<void* volatile*>(&instance), NULL)); + if (old_value) { + delete static_cast<T*>(old_value); + } + return NULL; + } +#endif // #ifndef _WIN32 + return instance; +} + +} // namspace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STATICINSTANCETEMPLATE_H_ |