154 lines
4.7 KiB
C
154 lines
4.7 KiB
C
|
/*
|
||
|
* 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_INCLUDE_STATIC_INSTANCE_H_
|
||
|
#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_
|
||
|
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
|
||
|
#ifdef _WIN32
|
||
|
#include "webrtc/system_wrappers/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 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<void * volatile*>(&instance),
|
||
|
new_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_INCLUDE_STATIC_INSTANCE_H_
|